반응형
7.1 SQL과 DAO의 분리
SQL을 dao에서 분리하는 작업이 남았다.
7.1.1 XML 설정을 이용한 분리
개별 SQL 프로퍼티 방식
- SQL은 문자열로 되어 있으니 설정파일에 프로퍼티 값으로 정의해서 DAO에 주입할 수 있다.
SQL 맵 프로퍼티 방식
- SQL을 하나의 컬렉션으로 담아주는 방법을 선택한다.
- 맵을 이용하면 키 값을 이용해 SQL문장을 가져올 수 있다.
- 일일이 프로퍼티로 등록하는 방법에 비해 작업량도 적고 코드도 간단하다.
- 대신 오타와 같은 실수가 있어도, 해당 메소드가 실행되기 전에는 오류를 확인하기 어렵다.
7.1.2 SQL 제공 서비스
- SQL과 DI 설정정보가 섞여 있으면 관리하기에도 좋지 않다.
- 스프링의 설정파일로부터 생성된 오브젝트와 정보는 애플리케이션을 다시 시작하기 전에는 변경이 매우 어렵다.
- 이런 문제점을 해결하고 요구사항을 축종하려면 독립적인 SQL 제공 서비스가 필요하다.
SQL 서비스 인터페이스
- SQL에 대한 키 값을 전달하면 그에 해당하는 SQL을 돌려준다.
7.2 인터페이스의 분리와 자기참조 빈
7.2.1 XML 세팅 방법
- 스프링의 XML 설정파일에서 <bean> 태그 안에 SQL 정보를 넣어놓고 활용하는 건 좋은 방법이 아니다.
- 그보다는 SQL을 저장해두는 전용 포맷을 가진 독립적인 파일을 이용하는 편이 바람직하다.
JAXB java architecture for XML binding
- JDK 6라면 java.xml.bind 패키지 안에 있다.
- DOM과 같은 전통적인 XML API와 비교했을 때 JAXB 장점은 XML 문서정보를 거의 동일한 구조의 오브젝트로 직접 매핑한다는 점이다.
- DOM은 XML 정보를 자바의 리플렉션 API 사용해서 오브젝트를 조작하는 것처럼 간접적으로 접근해야 한다.
- JABX는 XML 문서의 구조를 정의한 스키마를 이용해서 매핑할 오브젝트의 클래스까지 자동으로 만들어주는 컴파일러를 제공해준다.
- 스키마 컴파일러를 통해 자동생성된 오브젝트에는 매핑정보가 애노테이션으로 담겨 있다.
- JAXB API라는 애노테이션에 담긴 정보 이용해서 XML과 매핑된 오브젝트 트리 사이의 자동변환 작업을 수행해준다.
언마샬링
- XML 문서를 읽어서 자바의 오브젝트로 변환하는 것을 JAXB에서는 언마샬링 unmarshalling이라고 부른다.
- 반대로 바인딩 오브젝트를 XML 문서로 변환하는 것을 마샬링 marshalling이라고 한다.
7.2.2 XML 파일을 이용하는 SQL 서비스
XML SQL 서비스
- DAO가 SQL을 요청할 때마다 매번 xml파일을 다시 읽어서 SQL을 찾는 건 비효율적이다.
- 특별한 이유가 없는 한 XML 파일은 한 번만 읽도록 해야 한다.
- SQL 오브젝트를 리스트에 저장해뒀다가 검사하는 방법보다는 상대적으로 검색 속도가 빠르고 사용하기 간편한 Map타입 오브젝트에 저장해두는 게 더 나은 방법이다.
7.2.3 빈의 초기화 작업
- 생성자에서 예외가 발생할 수 있는 작업은 다루기 힘들고, 상속하기 불편하고, 보안에도 문제가 생길 수 있다.
- 일단 초기 상태 가진 오브젝트를 만들어놓고 별도의 초기화 메소드를 사용하는 방법이 바람직하다.
- 또 다른 문제점은 읽어들일 파일의 위치와 이름이 코드에 고정되어 있다는 점이다.
- 코드의 로직과 여타 이유로 바뀔 가능성이 있는 내용은 외부에서 DI로 설정할 수 있게 만들어야 한다.
- 먼저 파일 이름을 외부에서 지정할 수 있도록 프로퍼티를 추가한다.
- 생성자에서 진행하는 작업을 별도의 초기화 메소드로 만들어 옮긴다.
- 스프링 컨테이너인 애플리케이션 컨텍스트가 XML 설정파일을 읽고 진행하는 순서는 다음과 같다.
- @PostConstruct 애노케이션은 빈 오브젝트가 생성되고 의존 오브젝트와 설정 값을 넣어주는 DI 작업까지 마친 후에 호출된다.
- 따라서 @PostConstruct를 단 메소드의 코드는 모든 프로퍼티의 값이 준비됐다고 가정하고 작성하면 된다.
7.2.4 변화를 위한 준비 : 인터페이스 분리
- 현재까지 XmlSqlService는 특정 포맷의 XML에서 SQL 데이터를 가져오고, 이를 HashMap 타입의 맵 오브젝트에 저장해둔다. SQL을 가져오는 방법에 있어서 특정 기술에 고정되어 있다.
책임에 따른 인터페이스 정의
독립적으로 변경 가능한 책임을 뽑는다면 다음 두 가지이다.
- SQL 정보를 외부의 리소스로부터 읽어오는 것이다 ▶ 미리 정의된 XML, 액셀파일 혹은 애플리케이션에서 활용 가능하도록 메모리에 읽어들이는 것을 하나의 책임으로 생각할 수 있다.
- 읽어온 SQL을 보관해두고 있다가 필요할 때 제공해주는 것이다. ▶SQL 양에 따라 다양한 방식의 저장 방식을 생각할 수 있다. 키를 이용해 SQL을 검색하는 방법부터 애플리케이션 내의 저장소를 제공하는 것이 두 번째 책임이라고 생각할 수 있다.
- 부가적인 책임으로는 필요에 있게 수정할 수 있게 해야 한다.
- DAO 관점에서는 SqlService 인터페이스 구현한 오브젝트에만 의존하고 있으므로 달라지는 것은 없다.
- 대신 SqlService 구현 클래스가 변경 가능한 책임을 가진 SqlReader와 SqlRegistry 두 가지 타입의 오브젝트를 사용하도록 만든다.
- SqlRegistry의 일부 인터페이스는 SqlService가 아닌 다른 오브젝트가 사용할 수 있다.
- 대표적으로 SQL을 런타임 시에 변경하도록 요청하는 오브젝트가 필요에 따라 이를 호출해서 SQL을 갱신하도록 요청할 수 있다.
- SqlReader가 읽어오는 SQL 정보는 다시 SqlRegistry에 전달해서 등록되게 해야 한다.
- SqlReader가 제공하는 메소드의 리턴 타입은 특정 구현에 의존하도록 정의해서는 안된다.
- Map<String, String> sqls = sqlReader.readSql() ; 이런 식으로 정보를 전달하는 것보다 발상을 바꿔서 번거로움을 제거할 방법을 찾아야 한다.
- SqlService가 SqlReader에게 데이터를 달라고 요청하고, 다시 SqlRegistry에게 이 데이터를 사용하라고 하는 것보다 SqlReader에게 SqlRegistry 전략을 제공해주면서 이를 이용해 SQL 정보를 SqlRegistry에게 저장하도록 한다.
SqlRegistry 인터페이스
- SQL을 제공받아 등록했다가 키로 검색해서 돌려주는 기능을 담당한다.
SqlReader 인터페이스
- SqlReader는 SqlRegistry 오브젝트를 메소드 파라미터로 DI 받아서 읽어들인 SQL 등록하는 데 사용하도록 만들어야 한다.
7.2.5 자기참조 빈으로 시작하기
다중 인터페이스 구현과 간접 참조
- SqlService의 구현 클래스는 SqlReader와 SqlRegistry 두 개의 프로퍼티를 DI받을 수 있는 구조로 받아야 한다.
- 기존 XmlSqlService의 코드는 세분화해서 인터페이스를 정의하지 않았던 것이었기 때문에, 세 가지 인터페이스를 구현하도록 만들어야 한다.
자기참조 빈 설정
- 세 개의 빈이 등록된 것처럼 SqlService 빈이 SqlRegistry와 SqlReader를 주입받도록 만들어야 한다.
- 자기 자신을 참조하는 빈은 흔히 쓰이는 방법은 아니다.
- 책임이 다르다면 클래스를 구분하고 각기 다른 오브젝트로 만들어지는 것이 자연스럽다.
- 다만 자기참조 빈을 만들어보는 것은, 책임과 관심사가 복잡하게 얽혀 있어서 확장이 힘들고 변경에 취약한 클래스를 유연하게 만들려고 할 때 처음 시도해볼 수 있는 방법이다.
7.2.6 디폴트 의존관계
확장 가능한 기반 클래스
- BaseSqlService를 sqlService 빈으로 등록하고 SqlReader와 SqlRegistry를 구현한 클래스 역시 빈으로 등록해서 DI 해주면 된다.
- DI 적용했으니 언제든지 BaseSqlService의 코드에는 영향 주지 않은 채로 SqlReader와 SqlRegistry의 구현 클래스는 자유롭게 변경해서 기능을 확장할 수 있다.
디폴트 의존관계를 갖는 빈 만들기
- 디폴트 의존관계란 외부에서 DI 받지 않는 경우 기본적으로 자동 적용되는 의존관계를 말한다.
- 코드를 통해 의존관계의 오브젝트를 직접 주입해주면 특별히 DI가 필요한 상황이 아닌 대부분의 경우에는 편리하게 사용할 수 있다.
- 디폴트 의존 오브젝트를 사용하는 방법에는 한 가지 단점이 있다.
- 설정을 통해 다른 구현 오브젝트를 사용하게 해도 DefaultSqlService는 생성자에서 일단 디톨트 의존 오브젝트를 다 만들어버린다는 점이다.
- @PostConstruct 초기화 메소드 이용해 프로퍼티가 설정됐는지 확인하고 없는 경우에만 디폴트 오브젝트를 만드는 방법을 사용하면 된다.
토비 스프링 소스코드 ▶ https://github.com/AcornPublishing/toby-spring3-1/tree/main
토비 스프링 ▶ https://product.kyobobook.co.kr/detail/S000000935358
반응형
'스터디 > [토비의 스프링 3.1 Vol 1] (2025.03)' 카테고리의 다른 글
[토비의 스프링 3.1 Vol 1] 7장. 스프링 핵심 기술의 응용 (~7.7) (0) | 2025.05.31 |
---|---|
[토비의 스프링 3.1 Vol 1] 7장. 스프링 핵심 기술의 응용 (~7.5 DI를 이용해 다양한 구현 방법 적용하기) (0) | 2025.05.25 |
[토비의 스프링 3.1 Vol 1] 6장. AOP (6.7 ~ 6.8) (0) | 2025.05.11 |
[토비의 스프링 3.1 Vol 1] 6장. AOP (6.6 트랜잭션 속성) + spring boot @Transactional 옵션 (0) | 2025.04.28 |
[토비의 스프링 3.1 Vol 1] 6장. AOP (6.5 스프링 AOP) (0) | 2025.04.27 |