[토비의 스프링] Week11(7.2~7.4)

6 분 소요

토비의 스프링 3.1 Chapter 7.2 ~ 7.4

7.2 인터페이스의 분리와 자기참조 빈

JAXB: Java Architecture for XML Binding

: XML에 담긴 정보를 파일에서 읽어 오는 방법

  • XML 문서 정보를 거의 동일한 구조의 오브젝트로 직접 매핑한다.

  • 어노테이션에 담긴 정보를 이용해 XML과 매핑된 오브젝트 트리 사이의 자동변환 작업을 수행해준다.

마샬링(marshalling) : 바인딩 오브젝트를 XML 문서로 변환하는 것
언마샬링(unmarshalling) : XML 문서를 읽어서 자바의 오브젝트로 변환하는 것

사용 방법

JAXContext context = JAXBContext.newInstance(컨텍스트패스);
Unmarshaller unmarchaller = context.createUnmarshaller(); // 언마샬러 생성

Sqlmap sqlmap = (Sqlmap) unmarshaller.unmarshal(getClass.().getResourceAsStream("sqlmap.xml")); // xml 파일에 있는 정보를 Sqlmap에 매핑시킨다.

List<SqlType> sqlList = sqlmap.getSql();
  • 특별한 이유가 없는 한 XML 파일은 한 번만 읽도록 하자.

외부에서 XML 파일을 지정하는 방법

생성자에서 XML 파일 이름을 지정하던 것을 별도의 초기화 메서드를 만들어 옮겨 놓는다.
application.xml(또는 빈 설정 property 태그)를 이용해 값을 지정해주면 된다.

@PostConstruct

빈 오브젝트의 초기화 메서드를 지정하는 데 사용한다.

-> 스프링은 빈 오브젝트를 생성하고 DI 작업을 마친 뒤에 @PostConstruct가 붙은 메서드를 자동으로 실행해준다.
-> 일반 생성자와는 달리 프로퍼티까지 모두 준비된 후에 실행된다.

스프링 컨테이너의 초기 작업 순서

  1. applicationContext.xml에서 빈 설정을 읽는다.
  2. 빈의 객체를 생성한다.
  3. 프로퍼티에 의존 객체 또는 값을 주입한다.
  4. 빈이나 태그로 등록된 후처리기를 동작시킨다.
  5. 코드에 달린 어노테이션에 대한 부가작업을 진행한다. (@PostConstruct가 여기서 실행된다.)

인터페이스 분리

책임에 따라 XmlSqlService 인터페이스를 분리 한다.

책임 1: SQL 정보를 외부의 리소스로부터 읽어 온다. -> SqlReader
책임 2: 읽어온 SQL을 보관해두고 있다가 필요할 때 제공해준다. -> SqlRegistry
책임 3: XML에서 읽어온 SQL 정보를 관리 -> SqlService

SqlService가 SqlReader에게 Sql을 가져오라고 요청하고, SqlReader는 SqlRegistry에 sql을 등록하며, SqlService는 SqlRegistry에서 Sql을 검색할 수 있게 된다.

자기참조 빈

책임과 관심사가 복잡하게 얽혀 잇어서 확장이 힘들고 변경에 취약한 구조의 클래스를 유연한 구조롤 만들려고 할 때 사용할 수 있는 방법.

-> 일단 인터페이스를 통해 책임과 역할을 구분해두고 자기 자신을 참조하거나 DI한다.

디폴트 의존관계

외부에서 DI 받지 않는 경우 기본적으로 자동 적용되는 의존 관계.
생성자에서 디폴트 의존 객체를 직접 만들어소 스스로 주입해준다.
다른 명시적인 설정이 있다면, 그 설정을 따른다.

public class JaxbXmlSqlReader implements SqlReader {
	private static final String DEFAULT_SQLMAP_FILE = "sqlmap.xml";

	private String sqlmapFile = DEFAULT_SQLMAP_FILE;

	// 다른 설정이 없으면 여기서 지정한 파일이 사용된다.
	public void setSqlmapFile(String sqlmapFile) { this.sqlmapFile = sqlmapFile; }
}

7.3 서비스 추상화 적용

OXM 서비스 추상화

지금까지 JAXB로 XML 마샬링/언마샬링을 수행했지만, 상황에 따라 다른 서비스를 사용할 수 있도록 해야 한다.

자주 사용되는 OXM 서비스

  • Caster XML: 설정파일이 필요 없는 모드를 지원하기도 하는 가벼운 바인딩 프레임워크
  • JiBX: 퍼포먼스가 뛰어남.
  • XmlBeans: 아파치 XML 프로젝트 중 하나이다.
  • Xstream: 설정이 없는 바인딩 지원.

OXM(Object-XML Mapping) 프레임워크

  • 기능 면에서 호환성이 있음. 사용 목적이 모두 동일하기 때문.
  • 서비스 추상화를 통해 low-level의 구체적인 기술과 api에 종속되지 않는다.

리소스 추상화

프로퍼디로 xml 파일 이름을 외부에서 지정할 순 있지만, 클래스패스에 있는 파일로 제한된다.
-> Java에는 다양한 위치에 존재하는 리소스에 대해 접근하게 해주는 인터페이스가 존대하지 않는다.
-> InputStreamSource를 구현하는 리소스 접근 API를 추상화하면 각기 다른 리소스 타입에 맞추어 리소스를 가져올 수 있게 한다.

리소스 로더

  • 리소스 로더(Resource Loader): 문자열로 된 location에 담긴 정보를 바탕으로 적절한 Resource 객체로 변환시킨다.

스프링 컨테이너도 ResourceLoader 인터페이스를 상속하는, 리소스 로더이다.

7.4 인터페이스 상속을 통한 안전한 기능확장

Goal: 애플리케이션 재시작 없이 특정 SQL의 내용만을 변경하고 싶을 때 어떻게 해야 할까?

DI와 기능의 확장

DI를 의식하면서 설계하는 방식은 유연한 확장과 재사용이 가능한 설계를 만드는 데 많은 도움이 된다! 당장 기능이 동작하는 데 아무런 문제가 없으면 된다고 생각하면 오늘을 위한 설계밖에 나오지 않는다.

인터페이스를 사용해야 하는 이유

1. 다형성을 얻기 위해

하나의 인터페이스를 통해 여러 개의 구현을 바꿔가면서 사용할 수 있다.

2. 인터페이스 분리 원칙을 통해 클라이언트와 의존 오브젝트 사이의 관계를 명확하게 해줄 수 있기 때문

인터페이스는 하나의 오브젝트가 여러 개를 구현할 수 있기 때문에 관심사가 어디로 연결되어 있는지 직관적으로 알 수 있다. 결국 이를 통해 오브젝트의 응집도를 높여 작은 단위로 설계되게끔 해 준다.

인터페이스 분리 원칙(Interface Segregation Principle)은 오브젝트가 그 자체로 충분히 응집도가 높은 작은 단위로 설계됐더라도, 목적과 관심사가 다른 여러 개의 클라이언트가 있다면 인터페이스로 분리시켜야 한다는 원칙이다.

이 원칙이 주는 장점은 클라이언트가 자신의 관심에 따른 접근 방식을 불필요한 간섭 없이 유지할 수 있다는 점이다.

3. 클라이언트에 특화된 의존관계를 만들 수 있음

DI는 특별한 이유가 없는 한 항상 인터페이스를 사용해야 한다.

인터페이스 상속

인터페이스를 적절히 분리하고 확장하는 방법을 통해 오브젝트 사이의 의존관계를 명확하게 해 줄 수 있다.

새롭게 추가할 기능을 사용하는 클라이언트가 있다면, 새로운 인터페이스를 정의하거나 기존 인터페이스를 확장해야 한다.

댓글남기기