스터디/[토비의 스프링 3.1 Vol 1] (2025.03)

[토비의 스프링 3.1 Vol 1] 7장. 스프링 핵심 기술의 응용 (~7.7)

ttoance 2025. 5. 31. 08:30
반응형

7.6 스프링 3.1의 DI

 

 

자바 언어의 변화와 스프링 (1) 애노테이션의 메타정보 활용 

  • 자바는 소스코드가 컴파일된 후 클래스 파일에 저장됐다가, JVM에 의해 메모리로 로딩되어 실행된다. 
  • 그런데 자바 코드 일부를 리플렉션 API 이용해 어떻게 만들었는지 살펴보고 동작하는 기능이 많아진다.
  • 특히 자바5에서 등장한 애노테이션 이후로 급격히 확산되기 시작했다. 
    • 자바 클래스나 인터페이스, 필드, 메소드 등은 그 자체로 실행 가능하고 상속하거나 참조하거나 호출하는 방식 등으로 직접 이용할 수 있다. 
    • 반면 애노테이션은 기존 자바 프로그래밍 방식으로 활용할 수 없고 옵션에 따라 컴파일된 클래스에 존재하거나 애플리케이션이 동작할 때 메모리에 로딩되기도 하지만 자바 코드가 실행되는 데 직접 참여하지 못한다. 복잡한 리플렉션 API를 이용해 애노테이션의 메타정보 조회하고, 애노테이션 내에 설정된 값을 가져와 참고하는 방법이 전부다. 
  • 애노테이션의 활용이 늘어난 이유는 무엇일까? 프레임워크가 참조하는 메타정보로 사용되기에 유리한 점이 많기 때문. 
    • (장점) 런타임 의존관계 정보를 명시적으로 표현해준다. 
    • (단점) 자바 코드에 존재하기 때문에 변경할 때마다 매번 클래스를 새로 컴파일 해줘야 한다. 
  • 자바 코드 형태로 시작됐던 DI 패턴 (팩토리 패턴)이 프레임워크 발전에 따라 자바 코드와 프레임워크, XML 메타 정보 형태로 진행되다가 스프링 3.1에 이르러서는 핵심 로직을 담은 자바 코드와 DI 프레임워크, 그리고 DI를 위한 메타데이터로서의 자바 코드로 재구성된다. 


자바 언어의 변화와 스프링 (2) 정책과 관례를 이용한 프로그래밍

  • 애노테이션 활용하는 프로그래밍 방식은 동작 내용 기술하는 대신 코드 없이도 미리 약속한 규칙 따라서 프로그램 동작하도록 만들도록 하는 스타일을 포용하게 했다.
  • DaoFactory 같은 자바 코드를 대체한 스프링의 XML도 미리 정의한 정책을 이용해서 특정 기능이 동작하게 만든다고 할 수 있다. 
    • <bean>태그 작성해두면 그에 따라 new 키워드 이용해 인스턴스 생성 코드가 동작한다. 
  • (장점) 장점은 자바 코드로 모든 작업을 표현했을 때보다 작성해야 할 내용이 줄어든다.
  • (단점) 프로그래밍 언어나 API 사용법 외에 정의된 규칙을 기억해야 하고, 메타정보를 보고 어떻게 동작할지 이해해야한다. 
    • 학습 비용이 들고, 자칫 잘못 이해하고 있을 경우 차기 힘든 버그를 만들어내기도 한다. 
  • 스프링은 점차 애노티이션으로 메타정보 작성하고, 미리 정해진 정책과 관례를 활용해서 코드에 많은 내용 담을 수 있는 방식을 적극 도입하고 있다. 

 

7.6.1 자바 코드를 이용한 빈 설정 

첫 번째 작업은 XML 코드를 없애는 것이다. 

 

테스트 컨텍스트의 변경 

  • @ContextConfiguration은 스프링 테스트가 테스트용 DI 정보를 어디서 가져와야 하는지 지정할 때 사용하는 애노테이션이다. 
  • locations 엘리먼트는 DI 설정정보를 담은 XML 파일위 위치를 가리킨다.

  • 그다음 DI 정보로 사용될 자바 클래스 만들고 자바 클래스에 @Configuration 애노테이션을 담아준다.

  • @ContextConfiguration의 locations 앨리먼트 제거하고 대신 classes를 넣는다.

 

 

<context:annotation-config/> 제거

  • 스프링 컨테이너가 참고하는 DI 정보 위치가 XML에서 TestApplicationContext 클래스로 바뀌면서 <context:annotaion-config/> 은 더 이상 필요하지 않아 제거해도 된다. 

 

<bean>의 전환

  • <bean>으로 정의된 DI 정보는 자바 코드, 특별히 @Bean이 붙은 메소드와 1:1로 매칭된다.
    • <bean>은 @Bean이 붙은 public 메소드로 만들어주면 된다. 
    • 메소드 이름은 <bean>의 id 값으로 한다. 

 

  • XML과 자바 클래스를 동시에 DI 정보로 사용하는 경우 자바 코드로 정의한 datasource 빈은 XML에서 <property>를 이용해 참조할 수 있다. 
    • 자바 코드에서는 @Autowired가 붙은 필드 선언해서 XML에 정의된 빈을 컨테이너가 주입해주게 한다. 
  • @Resource는 @Autowired와 유사하게 필드에 빈을 주입받을때 사용한다. 
  • 차이점은 @Autowired는 필드의 타입을 기준으로 빈을 찾고 @Resource는 필드 이름을 기준으로 한다는 점이다.
  • 필드 이름과 일치하는 빈 아이디를 가진 빈을 주입받을 때는 @Resource를 이용한다. 

 

전용 태그 전환

  • SQL 서비스에서 사용하는 내장형 DB를 생성하는 <jdbc:embedded-database> 전용 태그는 type에 저장형 내장형 DB를 생성한다.
    • EmbeddedDatabaseBuilder 이용해 내장형 DB 종류와 초기화 스크립트 등을 지정하고 build() 메소드 실행하면 내장형 DB생성하고 초기화한 뒤에 DB 커넥션 오브젝트를 돌려준다. 
  • <jdbc:script>로 지정한 스크립트로 초기화한 뒤에 DataSource 타입 DB의 커넥션 오브젝트를 빈으로 등록해준다. 빈의 타입은 DataSource이다. 
  • <tx:annotaion-driven />은 @EnableTransactioManagement 를 활용해서 변환할 수 있다. 

 

7.6.2 빈 스캐닝과 자동와이어링

@Autowired를 이용한 자동와이어링
  • @Autowired는 자동와이어링 기법 이용해서 조건에 맞는 빈을 찾아 자동으로 수정자 메소드나 필드에 넣어준다. 
  • 자동와이어링을 이용하면 컨테이너가 이름이나 타입을 기준으로 주입될 빈을 찾아주기 때문에 빈의 프로퍼티 설정을 직접해주는 자바 코드나 XML의 양을 줄일 수 있다. 
  • (장점) @Autowired와 같은 자동와이어링은 적절히 사용하면 DI 관련 코드를 대폭 줄일 수 있어서 편리하다
  • (단점) 빈 설정정보를 보고 다른 빈과 의존관계가 어떻게 맺어져 있는지 한눈에 파악하기 힘들다. 

 

@Component를 이용한 자동 빈 등록

  • @Component는 클래스에 부여된다.
  • @Component가 붙은 클래스는 빈 스캐너 통해 자동으로 빈에 등록된다. 
  • @Component 애노테이션이 달린 클래스를 자동으로 찾아서 빈으로 등록해주게 하려면 @ComponetScan으로 특정 패키지 아래에서만 찾도록 기준이 되는 패키지를 지정해준다. 

 

 

7.6.3 컨텍스트 분리와 @Import

테스트용 컨텍스트 분리

  • 두 개의 빈 설정은 테스트용 DI 정보로 구분되고, 테스트에서만 사용될 것이고, 테스트를 작성하거나 수정할 때 영향을 받고 변경되는 것이니 다른 애플리케이션 빈의 DI 정보에서 분리하는 것이 좋다. 
  • DI 설정 클래스를 추가하고 관련된 빈 설정 애노테이션, 필드, 메소드를 옮기면 분리할 수 있다. 

 

 

@Import

  • AppContext가 메인 설정정보가 되고, SqlServiceCOntext는 AppContext에 포함되는 보조 설정정보로 사용한다. 
  • 자바 클래스로 된 설정정보 가져올때는 @ImportResoure eotls @Import를 추가한다. 

 

7.6.4 프로파일

  • 테스트환경과 운영환경에서 각기 다른 빈 경우가 필요한 경우가 있다. 
  • 이 문제를 해결하려면 운영환경에서는 반드시 필요하지만 테스트 실행 중에는 배제돼야 하는 빈 설정을 별도의 설정 클래스를 만들어 따로 관리해야한다. 

 

@Profile과 @ActiveProfiles
  • 스프링은 환경에 따라서 빈 설정정보가 달라져야 하는 경우 파일 여러 개로 쪼개고 조합하는 대신 간단히 설정정보 구성할 수 있는 방법을 제공한다.
  • 실행환경에 따라 빈 구성이 달라지는 내용을 프로파일로 정의해서 만들어주고, 실행 시점에 어떤 프로파일의 빈 설정을 사용할지 지정하는 것이다.  

 

 

7.6.5 프로퍼티 소스 

@PropertySource

  • 스프링은 빈 설정 작업에 필요한 프로퍼티 정보를 컨테이너가 관리하고 제공해준다.
  • 스프링 컨테이너가 지정된 정보 소스로부터 프로퍼티 값을 수집하고, 이를 빈 설정 작업 중에 사용할 수 있게 한다. 
  • 컨테이너가 프로퍼티 값을 가져오는 대상을 프로파티 소스라고 한다. 
  • 환경 변수나 시스템 프로퍼티처럼 디폴트로 프로퍼티 정보 끌어오는 프로퍼티 소스도 있고, 프로퍼티 파일이나 리소스 위치 지정해서 사용되는 프로퍼티 소스도 있다. 
  • DB 연결정보는 database.properties라는 특정 파일에서 프로퍼티 값을 가져와야 하므로 프로파티 소스를 등록해줘야 한다. 
    • 프로파티 소스 등록에는 @PropertySource 애노테이션을 이용하면 된다. 

 

  • (장점) @Value 이용하면 driverClass처럼 문자열 그대로 사용하지 않고 타입 변환이 필요한 프로퍼티를 스프링이 알아서 처리해준다.
  • (단점) dataSource 빈에서만 사용되는 프로퍼티인데 값을 주입받도록 클래스에 필드 선언하는 것이 부담스러울 수 있다. 

 

7.6.6 빈 설정의 재사용과 @Enable

  • 설정정보 담은 코드도 리펙터링하면 반복적으로 사용되는 부분은 수정 없이 재사용될 수 있고, 적용환경에 따라 바뀌는 부분은 인터페이스로 분리하고 DI통해 외부에서 주입되게 만든다.

 

7.7 정리

  • SQL처럼 변경될 수 있는 텍스트로 된 정보는 외부 리소스에 담아두고 가져오게 만들면 편리하다.
  • 성격이 다른 콛가 한데 섞여 있는 클래스라면 먼저 인터페이스를 정의해서 코드를 각 인터페이스 별로 분리하는 게 좋다. 다른 인터페이스에 속한 기능은 인터페이스를 통해 접근하게 만들고, 간단히 자기참조 빈으로 의존관계를 만들어 검증한다. 검증을 마쳤으면 아예 클래스를 분리해도 좋다. 
  • 자주 사용되는 의존 오브젝트는 디폴트로 미리 정의해두면 편리하다.
  • XML과 오브젝트 매핑은 스프링의 OXM 추상화 기능을 활용한다.
  • 특정 의존 오브젝트를 고정시켜 기능을 특화하려면 멤버 클래스로 만드는 것이 편리하다. 기존에 만들어진 기능과 중복되는 부분은 위임을 통해 중복을 제거하는 게 좋다.
  • 외부의 파일이나 리소스를 사용하는 코드에서는 스프링의 리소스 추상화와 리스스 로더를 사용한다. 
  • DI를 의식하면서 코드를 작성하면 객체지향 설계에 도움이 된다.
  • DI에는 인터페이스를 사용한다. 인터페이스를 사용하면 인터페이스를 분리 원칙을 잘 지키는데도 도움이 된다. 
  • 클라이언트에 따라서 인터페이스를 분리할 때, 새로운 인터페이스를 만드는 방법과 인터페이스를 상속하는 방법 두 가지를 사용할 수 있다. 
  • 애플리케이션에 내장하는 DB를 사용할 때는 스프링의 내장형 DB 추상화 기능과 전용 태그를 사용하면 편리하다

 

 

반응형