7장 스프링 핵심 기술의 응용

Created
2022/09/03 11:19
Tags
스프링의 모든 기술은 결국 객체지향적인 언어의 장점을 적극적으로 활용해서 코드를 작성하도록 도와주는 것이다. 스프링은 이 세 가지 핵심 기술을 자바 엔터프라이즈 개발을 필요로 하는 다양한 분야에 적용해서 개발자가 손쉽게 이용할 수 있는 기능으로 만들어 제공해주기도 한다.

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에 넣는걸 허용함
다만 자기참조 빈을 만들어보는 것은, 책임과 관심사가 복잡하게 얽혀 있어서 확장이 힘들고 변경에 취약한 구조의 클래스를 유연한 구조로 만들려고 할 때 처음 시도해볼 수 있는 방법