이번주 세션 주제 : IoC 컨테이너와 스프링부트 자동 설정 - 스프링부트의 자동 설정과 조건부 빈 처리에 대한 이해
<WIL>
1) 저번 주 복습
- POJO (Plain Old Java Object) : 자바로 생성하는 순수한 객체 - 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트
- DI : 의존성 주입 - 각 클래스 간의 의존 관계를, 빈 설정 정보를 바탕으로 컨테이너가 자동으로 연결해 주는 것 - 필요한 객체를 직접 생성하지 않고 외부로부터 받아서 사용
-> 빈? 컨테이너?
2) IoC 컨테이너 = 스프링 컨테이너 = 애플리케이션 컴포넌트의 중앙 저장소 = 스프링에서 객체 생성을 책임지고 의존성을 관리하는 컨테이너로, POJO(객체)의 생성/초기화/서비스/소멸에 대한 권한을 가진다 -> 객체 관리의 주체가 프레임워크가 되기에, 개발자는 로직에 집중 가능
=> 스프링 프로젝트를 생성하면 스프링 컨테이너가 자동으로 생성되고, 빈을 관리하는 역할도 자동으로 수행된다. 특히 스프링부트를 이용하면 이러한 설정 관련 부분이 더욱 자동으로 가능하다
* 스프링 프로젝트에서 컨테이너 동작 방식:
- 스프링 부트 초기화: 스프링 부트 프로젝트를 생성하고 main 메서드를 실행하면, 스프링은 내부적으로 SpringApplication.run() 메서드를 통해 애플리케이션을 시작 -> 이 과정에서 스프링 컨테이너인 ApplicationContext가 초기화됨
- 빈(Bean) 등록 및 관리: 스프링 컨테이너는 자동으로 프로젝트 내에 있는 빈을 스캔하고, 필요한 객체들을 생성해서 의존성을 관리함 -> 일반적으로 클래스에 붙은 @Component, @Service, @Repository, @Controller 같은 애너테이션이 붙은 클래스들이 스프링 컨테이너에 의해 자동으로 빈으로 등록됨
- 의존성 주입: 스프링 컨테이너는 빈 간의 의존성을 자동으로 주입함 -> 클래스에 @Autowired, @Inject, @Resource 등의 애너테이션을 사용하면 스프링이 해당 의존성을 자동으로 찾아서 주입
- 라이프사이클 관리: 스프링 컨테이너는 빈의 생성과 소멸 시점을 관리하고, 필요에 따라 @PostConstruct, @PreDestroy 같은 애너테이션을 통해 특정 시점에 메서드를 호출가능
3) 빈 (Bean) : 스프링 컨테이너가 관리하는, 즉 스프링이 직접 생성하고 의존관계를 부여하는 객체 - 스프링 핵심 개념인 IoC, DI를 통해 관리됨
4) IoC 컨테이너의 종류
(1) 빈팩토리 = 가장 기본적인 IoC 컨테이너이자 클래스
- 빈을 생성하고 관계를 설정하는 IoC 기본 기능에 초점
- Lazy Loading(지연 로딩) : 빈 팩토리는 요청이 들어올 때만 빈을 생성함. 즉, 애플리케이션이 시작될 때 빈을 생성하지 않고, 해당 빈이 실제로 필요할 때 생성하는 방식
- 경량 컨테이너, 기본 IoC기능만 제공(애플리케이션 전반에 걸친 관리기능은 상대적으로 부족)
(2) 애플리케이션컨텍스트 = 빈팩토리를 상속받아 확정한 컨테이너로, 빈팩토리의 모든 기능을 포함하면서 environment,messagesource등의 추가기능 제공 - 빈의 생성/관계 설정 등 제어 총괄에 초점
- Eager Loading(즉시 로딩) ; 애플리케이션 시작 시점에 모든 빈을 미리 생성해둠 -> 빈 팩토리보다 애플리케이션이 좀 더 빨리 준비됨
- 추가기능 : MessageSource : 국제화를 위한 메시지 관리 기능, Event Handling : 애플리케이션에서 발생하는 이벤트 처리기능, Environment : 환경설정 관리 기능 제공
=> 스프링부트 프로젝트에선 기본적으로 애플리케이션 컨텍스트를 사용한다고 한다
5) 클라이언트 요청에 따른 빈 처리 과정
- 애플리케이션 컨텍스트는 @Configuration이 붙은 클래스들을 설정 정보로 등록
- 해당 클래스 안에 존재하는 @Bean이 붙은 메소드의 이름으로 빈 목록 생성
- 클라이언트가 해당 빈을 요청
- 애플리케이션 컨텍스트는 자신의 빈 목록에서 클라이언트가 요청한 이름이 있는지 탐색
- 설정 클래스(Configuration class) 로 부터 빈 생성 요청하고 생성된 빈을 반환
=> 이처럼 빈 관리는 빈팩토리를 상속한 애플리케이션 컨텍스트가 관련 모든 작업을 총괄한다
6) 빈 등록 방법
- 수동 등록 : @Bean 어노테이션 - 메소드에 등록, 개발자가 직접 변경하기 어려운 대상에 주로 사용
- 자동 등록 : @Component 어노테이션 - 클래스에 등록, 개발자가 직접 변경 가능한 대상에 주로 사용
7) 빈 생성 방식 : 싱글톤 레지스트리
- 스프링에서 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능 제공
- 스프링이 애플리케이션 내에서 동일한 객체를 한 번만 생성하고, 이 객체를 여러 곳에서 공유하도록 해준다는 의미
- 전통적인 싱글톤 패턴의 단점 보완 : 싱글톤 패턴과 달리, 스프링이 지지하는 객체지향적 설계방식과 원칙, 디자인 패턴 적용에 제약 없음 (전통적 싱글톤 패턴은 한 개의 인스턴스만 생성하도록 제한하기 때문에, 테스트가 어렵고 / 확장성이 제약되고 / 강한 결합성으로 코드의 유연성이 떨어진다)
- 전통적 싱글톤 방식보다 유연함
- 클래스 자체에서 인스턴스를 생성하는 전통적 싱글톤과 달리, 스프링이 직접 싱글톤 객체 관리 -> 스프링 컨테이너가 알아서 객체 생성, 한 번 생성된 객체를 애플리케이션 전반에 걸쳐 공유
- 객체지향 설계 원칙 준수 : 전통적인 싱글톤 패턴은 클래스 설계에 제약이 많지만, 스프링에서는 **의존성 주입(Dependency Injection)**을 통해 객체 간의 관계를 느슨하게 유지 가능 -> 즉, 객체들이 서로 직접 의존하지 않고, 스프링이 객체들 간의 의존성을 주입해주기 때문에 설계가 훨씬 유연함
- 스프링에서는 기본적으로 싱글톤 스코프를 사용해서 동일한 클래스에 대해 하나의 인스턴스만 생성하지만,
테스트 환경에서는 실제 서비스 객체나 리소스를 사용하지 않고, 테스트를 위해 **특정 상황에 맞게 가짜 객체(목(Mock) 객체)**나 대체 객체를 사용할 수 있도록 해줌
8) 자동 설정 (auto configuration)
= 자주 사용하는 수많은 빈들을 자동으로 등록해주는 기능
- 반복적으로 빈 등록/설정하는 부분을 자동화함으로써 편리한 개발 가능 (스프링부트의 핵심 장점_
- @SpringBootApplication -> @SpringBootConfiguration / @ComponentScan / @EnableAutoConfiguraion
- @SpringBootConfiguration : 스프링부트의 설정을 나타내는 어노테이션, 스프링의 Configuration 어노테이션을 대체하는 스프링부트 전용 필수 어노테이션
- @ComponentScan : 자기 자신으로부터 시작해서 하위 패키지를 싹 훑어, @Component 어노테이션 붙은 클래스들 찾아서 빈으로 등록
- @EnableAutoConfiguraion : 자동 설정의 핵심 어노테이션 - auto configuration을 활성화하는 기능
- 자동설정 동작방식 : @SpringBootApplication -> @EnableAutoConfiguraion -> @Import(AutoConfigutationImportSelector.class) ->
@ resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일을 열어서 설정 정보를 가져와 유효하다면 스프링 컨테이너에 등록되어 사용
궁금한 사항
: 빈 등록 방법에서, 수동등록과 자동등록이 적합한 경우는 각각 언제?
-> 편리한 자동기능을 기본으로 사용하되, 직접 등록하는 기술지원객체 / 다형성을 적극 활용하는 비즈니스 로직 등은 수동등록을 고려
<과제> : 자동 설정 모듈을 만들고 이를 스프링 부트 애플리케이션에 통합
8)에서 배운 자동설정 모듈을 직접 만들어보자
Spring boot 자동 설정에서 가장 중요한 것은 AutoConfiguration 클래스
1. @Configuration 어노테이션으로 이 클래스가 스프링 설정 클래스임을 명시할 수 있는데요, 이때 특정 조건에서만 빈을 생성하도록 하기 위해 조건부 어노테이션을 사용합니다. (ex : @ConnditionalOnMissingBean, @ConditionalOnClass, @ConditionalOnProperty 등)
2. 스프링 부트는 자동 설정을 spring.factories 파일을 통해 인식합니다. 이 파일을 src/main/resources/META-INF 경로에 생성하고, 그 안에 자동 설정 클래스의 경로를 작성합니다.
3. 자동 설정 모듈은 별도의 라이브러리이므로, 이를 다른 스프링 부트 애플리케이션에서 사용하려면 의존성으로 추가해야 합니다. Maven 일 경우 pom.xml 에 추가해주세요.
1. build.gradle에 의존성 추가 -> Load gradle changes 버튼을 통해 변경사항 빌드
2. 자동설정 클래스 작성
위와 같이 자동 설정 클래스를 작성한다. @Configuration 어노테이션으로 이 클래스가 스프링 설정 클래스임을 명시하고, 특정 조건에서만 빈을 생성하도록 하기 위해 조건부 어노테이션인 @ConnditionalOnMissingBean을 사용했다
이렇게 하면 HelloSpringApplication이라는 빈이 없는 경우에만 자동으로 해당 빈을 생성한다
3. 자동 설정이 가능하게 하려면, resources/META-INF/spring.factories 파일을 생성하고, 자동 설정 클래스의 이름을 등록해야함 ( 스프링 부트는 자동 설정을 spring.factories 파일을 통해 인식) -> 이렇게 하면 Spring Boot가 애플리케이션을 실행할 때 spring.factories에 정의된 자동 설정 클래스를 찾아서 실행
4. 자동 설정 모듈 통합 : 완성된 자동 설정 모듈을 Jar 파일로 패키징한 후, 이를 Spring Boot 애플리케이션 프로젝트에 의존성으로 추가
이 부분에서 어려움을 겪었었다
우선 루트경로의 build.gradle에 auto-config-module을 의존성으로 추가했는데, 이를 제대로 읽지 못했다
알아보니 자동설정 클래스 밑에도 build.gradle이 있어야한다고 한다. ( 자동 설정 모듈의 경우, 해당 모듈이 Gradle 서브프로젝트로 제대로 인식되려면 build.gradle 파일이 필요)
Gradle은 각 서브프로젝트의 의존성 및 플러그인을 관리하기 위해 각 서브프로젝트에 대한 build.gradle 파일을 요구한다.
그래서 자동설정 클래스가 있는 경로에도 build.gradle을 추가해주었다
처음엔 플러그인과 의존성만 넣었는데, 에러가 나서 보니까 repositories도 필요하다는 메세지가 있어서 추가해주었다
그리고 초반엔 의존성 부분에 버전을 쓰지 않았는데, 이것도 에러가 났다 -> 루트의 build.gradle을 보고, 현재 사용중인 스프링부트 버전과 같은 버전을 명시해주면 성공적으로 반영된다
또한 루트의 build.gradle에서 자동설정 모듈을 사용하기 위해선, settings.gradle에서 설정을 해줘야한다
이 곳에서 자동설정 모듈을 프로젝트로 추가하고 파일경로를 지정할 수 있다
이때 경로는 루트 기준의 경로를 작성하면 된다 (자동설정 클래스가 들어있는 폴더명)
또한 build.gradle에는 패키지를 명시하면 안된다! (build.gradle 작성시에 package를 명시하라는 주의문구가 떠서 넣었었는데, settings.gradle에 위의 코드를 반영하는 과정에서 에러가 나서 지웠다)
6. Jar 파일 패키징: 자동 설정 모듈이 포함된 프로젝트를 빌드하여 Jar 파일로 패키징 ( ./gradlew build )
위의 과정을 거쳐 드디어 전체 빌드에 성공~
'dev > 백엔드' 카테고리의 다른 글
스프링 스터디 2주차 (0) | 2024.10.05 |
---|---|
스프링 스터디 1주차 (0) | 2024.10.04 |
gdg on campus ewha - Spring 1주차 WIL (1) | 2024.09.17 |
백엔드 입문하기 : 2장 스프링부트3 시작하기 (0) | 2024.08.18 |
백엔드 입문하기 : 1장 사전지식 (0) | 2024.08.17 |