스프링의 모든 기술은 결국 객체지향적인 언어의 장점을 적극적으로 활용해서 코드를 작성하도록 도와주는 것이다. 스프링은 이 세 가지 핵심 기술을 자바 엔터프라이즈 개발을 필요로 하는 다양한 분야에 적용해서 개발자가 손쉽게 이용할 수 있는 기능으로 만들어 제공해주기도 한다.
SQL과 DAO의 분리
•
현실적으로 SQL은 자주 변경된다.
•
DAO코드까지 변하는 상황을 막고자 한다.
분리 방법
•
DI변수를 이용하는 방법
◦
가장 단순하게 생각할 수 있는 방법
◦
각 쿼리마다 setter를 생성해서 property를 통해서 주입받는 방법
◦
작동은 문제가 없겠지만 번거롭고 변수를 추가해줘야만 한다.
•
Map을 이용하는 방법
◦
SQL 프로퍼티를 맵으로 구성해서 주입받는 방법
◦
하지만 SQL과 DI를 위한 정보가 뒤섞여 있으면 관리가 쉽지않다.
SQL 제공 서비스
•
앞서 말했던것처럼 SQL과 DI 정보가 섞여있으면 관리가 쉽지않음.
•
SQL을 따로 분리해둬야 독집적으로 SQL문의 리뷰나 튜닝 작업 수행이 편리함
•
반드시 XML에 담길 필요없이 다른 파일에서도 불러올 수 있도록 해야함
•
스프링의 설정파일로부터 생성된 오브젝트오 정보는 어플리케이션을 다시 시작하기 전에는 변경이 매우 어렵다는 점도 문제
•
SQL 서비스 인터페이스
◦
인터페이스를 통해서 키를 주면 SQL을 전달해주는 기능을 분리함
public interface SqlService {
String getSql(String key) throws SqlRetrievalFailureException;
}
Java
복사
▪
Key로 SQL을 가져오는게 실패하는 경우는 복구 불가능하니 런타임 예외로 설정한다.
인터페이스의 분리와 자기참조 빈
•
인터페이스로 대표되는 기능을 구현하는 방법과 확장 가능성에 따라 유연한 방법으로 재구성할 수 있도록 설계할 필요도 있음
XML파일 매핑
•
스프링의 XML 설정 내에서 SQL을 넣는건 좋은 방법이 아님
•
전용 포맷을 가진 독립적인 파일로 운영하는게 바람직함 대표적인 포멧은 XML
•
JAXB (Java Architecture for XML Binding)
◦
JDK6 부터 지원하는 패키지
◦
XML 정보를 오브젝트 트리 구조로 만들어 주기 때문에 객체처럼 다룰 수 있음
◦
XML 문서의 구조를 정의한 스키마로 객체를 만들어주는 스키마 컴파일러를 제공한다. 매핑정보가 애너테이션으로 표시되어 있음
◦
애너테이션의 정보를 이용하여 XML 원본을 오브젝트 트리로 만들어줌 (Unmarchalling)
그 반대도 가능 (Marchalling)
XML 파일을 이용하는 SQL 서비스
public class XmlSqlService implements SqlService {
private Map<String, String> sqlMap = new HashMap();
public XmlSqlService() {
String contextPath = Sqlmap.class.getPackage().getName();
try {
JAXBContext context = JAXBContext.newInstance(contextPath);
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream resourceAsStream = getClass().getResourceAsStream("/sql/sql-map.xml");
Sqlmap unmarshal = (Sqlmap) unmarshaller.unmarshal(resourceAsStream);
unmarshal.getSql().forEach(map -> this.sqlMap.put(map.getKey(), map.getValue()));
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
@Override
public String getSql(String key) throws SqlRetrievalFailureException {
String sql = sqlMap.get(key);
if (sql == null) {
throw new SqlRetrievalFailureException(key + "에 대한 SQL을 찾을 수 없습니다.");
}
return sql;
}
}
Java
복사
생성자에서 XML을 불러오는 코드
•
DAO가 SQL을 요청할때마다 XML을 읽는건 효율이 안좋음
•
특별할 때가 아니라면 XML은 한번만 읽어야함
public class XmlSqlService implements SqlService {
...
private String sqlmapFile;
public XmlSqlService(String sqlmapFile) {
this.sqlmapFile = sqlmapFile;
}
@PostConstruct // 빈 객체를 생성하고 DI작업을 마친 뒤에 @PostConstruct 메서드를 실행함
public void loadSql() {
...
}
}
Java
복사
•
생성자에 예외처리블럭 안좋음
◦
예외를 다루기 어려움
◦
상속받기 불편함
◦
보안상 문제가 발생할 수 있음
•
읽어들일 파일의 위치와 이름이 하드코딩되어있음. DI로 설정할 수 있도록 만들어주어야 함
인터페이스 분리
•
현재 XmlSqlService는 JAXB에 의존적임
•
만약 다른 기술으로 변경하고자 한다거나, HashMap이 아닌 다른 컬렉션에 두고자 한다면 모두 XmlSqlService 코드를 직접 수정해야하만 함
⇒ 결과적으로 단일 책임의 원칙을 위반하고 있음
•
책임에 따른 인터페이스 분리
◦
정보를 외부의 리소스로부터 읽어오는 것
◦
SQL을 보관하고 있다가 필요할때 제공해주는 것
•
SqlReader에서 읽어온 값을 SqlRegistry로 전달하기
◦
JAXB가 오브젝트 트리를 받아서 Map으로 다시 만들어 주어야 하는건 번거로운 일이 될 수가 있음
▪
SqlReader는 SqlRegistry를 필요에 따라 등록을 요청할 때만 활용하도록 함
▪
SqlReader에게 제공할 SqlRegistry는 SqlService가 제공
▪
파일을 읽어서 매 SQL 엔트리마다 SqlRegistry에 등록 요청
자기참조 빈으로 시작하기
분리 전
•
책임을 분리하면서 총 3개의 인터페이스로 분리되고 구현체 또한 3개로 분리됨
•
XmlSqlService 클래스 하나가 3개의 인터페이스를 구현해도 상관없다.
•
하나의 클래스로 합쳐져서 SqlRegistry의 sqlMap은 클래스 내부 변수로 존재하지만 독립적인 오브젝트를 접근하듯이 이용하여야 한다
◦
기존 SqlRegistry을 통해서 이용하던 방식과 달라지면 안된다는 뜻
•
스프링은 프로퍼티에 자시자신을 ref에 넣는걸 허용함
•
다만 자기참조 빈을 만들어보는 것은, 책임과 관심사가 복잡하게 얽혀 있어서 확장이 힘들고 변경에 취약한 구조의 클래스를 유연한 구조로 만들려고 할 때 처음 시도해볼 수 있는 방법