7장 스프링 핵심 기술의 응용
- Created
- 2022년 9월 3일 PM 8:19
스프링의 모든 기술은 결국 객체지향적인 언어의 장점을 적극적으로 활용해서 코드를 작성하도록 도와주는 것이다. 스프링은 이 세 가지 핵심 기술을 자바 엔터프라이즈 개발을 필요로 하는 다양한 분야에 적용해서 개발자가 손쉽게 이용할 수 있는 기능으로 만들어 제공해주기도 한다.
SQL과 DAO의 분리#
- 현실적으로 SQL은 자주 변경된다.
- DAO코드까지 변하는 상황을 막고자 한다.
분리 방법#
- DI변수를 이용하는 방법
- 가장 단순하게 생각할 수 있는 방법
- 각 쿼리마다
setter를 생성해서property를 통해서 주입받는 방법 - 작동은 문제가 없겠지만 번거롭고 변수를 추가해줘야만 한다.
- Map을 이용하는 방법
- SQL 프로퍼티를 맵으로 구성해서 주입받는 방법
- 하지만 SQL과 DI를 위한 정보가 뒤섞여 있으면 관리가 쉽지않다.
SQL 제공 서비스#
- 앞서 말했던것처럼 SQL과 DI 정보가 섞여있으면 관리가 쉽지않음.
- SQL을 따로 분리해둬야 독집적으로 SQL문의 리뷰나 튜닝 작업 수행이 편리함
- 반드시 XML에 담길 필요없이 다른 파일에서도 불러올 수 있도록 해야함
- 스프링의 설정파일로부터 생성된 오브젝트오 정보는 어플리케이션을 다시 시작하기 전에는 변경이 매우 어렵다는 점도 문제
- SQL 서비스 인터페이스
- 인터페이스를 통해서 키를 주면 SQL을 전달해주는 기능을 분리함java
public interface SqlService { String getSql(String key) throws SqlRetrievalFailureException; }- Key로 SQL을 가져오는게 실패하는 경우는 복구 불가능하니 런타임 예외로 설정한다.
인터페이스의 분리와 자기참조 빈#
- 인터페이스로 대표되는 기능을 구현하는 방법과 확장 가능성에 따라 유연한 방법으로 재구성할 수 있도록 설계할 필요도 있음
XML파일 매핑#
- 스프링의 XML 설정 내에서 SQL을 넣는건 좋은 방법이 아님
- 전용 포맷을 가진 독립적인 파일로 운영하는게 바람직함 대표적인 포멧은 XML
- JAXB (Java Architecture for XML Binding)
- JDK6 부터 지원하는 패키지
- XML 정보를 오브젝트 트리 구조로 만들어 주기 때문에 객체처럼 다룰 수 있음
- XML 문서의 구조를 정의한 스키마로 객체를 만들어주는 스키마 컴파일러를 제공한다. 매핑정보가 애너테이션으로 표시되어 있음
- 애너테이션의 정보를 이용하여 XML 원본을 오브젝트 트리로 만들어줌 (Unmarchalling)
그 반대도 가능 (Marchalling)
XML 파일을 이용하는 SQL 서비스#
java
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;
}
}- DAO가 SQL을 요청할때마다 XML을 읽는건 효율이 안좋음
- 특별할 때가 아니라면 XML은 한번만 읽어야함
java
public class XmlSqlService implements SqlService {
...
private String sqlmapFile;
public XmlSqlService(String sqlmapFile) {
this.sqlmapFile = sqlmapFile;
}
@PostConstruct // 빈 객체를 생성하고 DI작업을 마친 뒤에 @PostConstruct 메서드를 실행함
public void loadSql() {
...
}
}- 생성자에 예외처리블럭 안좋음
- 예외를 다루기 어려움
- 상속받기 불편함
- 보안상 문제가 발생할 수 있음
- 읽어들일 파일의 위치와 이름이 하드코딩되어있음. 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에 넣는걸 허용함
- 다만 자기참조 빈을 만들어보는 것은, 책임과 관심사가 복잡하게 얽혀 있어서 확장이 힘들고 변경에 취약한 구조의 클래스를 유연한 구조로 만들려고 할 때 처음 시도해볼 수 있는 방법