[Spring]그림으로 배우는 스프링 6 - 5장 스테레오 어노테이션

Gaeng·2024년 11월 14일

[Spring] 공부

목록 보기
4/21
post-thumbnail

그림으로 배우는 스프링 6을 기반으로 내용을 정리하며 추가한 내용입니다.

chapter 5. 스테레오 어노테이션

5.1 스테레오타입 어노테이션?

스테레오타입 어노테이션은 Bean으로 관리하고 싶은 구체적인 클래스에 붙이는 어노테이션으로, 스프링에서 사용하는 고유한 용어.

  • 스테레오타입 어노테이을 붙이면 스프링이 이를 감지해서 구상 클래스의 생성자를 호출하고 객체를 생성한다. 생성된 객체는 Bean으로 관리해준다.

5.2 스테레오타입 어노테이션의 종류

스테레오 어노테이션은 구상 클래스의 역할에 따라 구분해서 사용한다.

  • @Service와 @Component는 부가 기능이 없기 때문에, 코드의 가독성을 위해 Service 클래스에는 @Service를, Component 클래스에는 @Component를 사용하는 것이 권장된다. 이렇게 하면 소스 코드에서 클래스의 역할을 명확히 알 수 있어 유지보수가 용이하다.
  • 스테레오 어노테이션을 사용하게 되면 DI컨테이너를 생성하면 각 클래스의 생성자가 DI 컨테이너에 의해 호출되어 객체가 생성됨.
스테레오타입 애너테이션설명
@ServiceService 계층의 구성 클래스에 붙이며, 비즈니스 로직을 담는 역할을 한다. 부가 기능은 없다.
@RepositoryRepository 계층의 구성 클래스에 붙이며, 데이터베이스 접근을 담당한다. 부가 기능으로, 데이터베이스 접근 시 발생하는 예외를 스프링이 제공하는 예외로 자동 변환해주는 기능을 제공한다.
@ControllerController 계층의 구성 클래스에 붙이며, 웹 요청을 처리하는 역할을 한다. 부가 기능으로 스프링 MVC 기능을 사용할 수 있다.
@Component역할을 나타내지 않는 범용적인 스테레오타입 애너테이션으로, 특정 계층에 속하지 않는 일반적인 빈(Bean) 정의 시 사용한다. 부가 기능은 없으며, 구성 클래스의 역할이 위의 세 가지(@Service, @Repository, @Controller)에 해당하지 않을 때 주로 사용된다.

5.3 인젝션 지시 (@Autowired 혹은 생성자 주입(권장))

스테레오 어노테이션을 객체를 생성해도 의존 객체 인젝션이 이루어지지 않았기에, DI컨테이너 인젝션을 요청하려면 @Autowired 를 사용해야한다.
@Autowired는 인젝션을 지시하는 어노테이션이다.

구분생성자 주입@Autowired 필드 주입
장점- 불변성 보장 (final 필드 사용 가능)- 코드가 간결함
- 주입할 의존성을 명확히 보여주어 가독성 향상- 간단한 테스트에는 편리
- 테스트가 용이함 (Mock 주입 가능)- 수동으로 빈을 주입할 경우 용이
- 의존성 주입 누락 방지 가능 (컴파일 시점 확인 가능)
단점- 필드가 많은 경우 코드가 길어질 수 있음- 필드 주입은 불변성 보장이 어려움
- 의존성이 많을 경우 생성자 인자가 많아져 가독성 저하- 테스트하기 어려움 (Mock 객체 주입이 복잡함)
- DI 컨테이너가 없으면 객체 생성 어려움
권장 여부권장 (스프링 공식 권장 방식)권장하지 않음 (테스트와 유지보수의 어려움)

스테레오타입 애너테이션과 의존성 주입 사용 흐름

  • 스테레오타입 애너테이션을 클래스에 붙이기: @Service, @Repository 등으로 클래스의 역할을 지정합니다.
  • 주입할 대상 클래스 선언: 필요한 클래스를 @Autowired나 생성자 주입( priavet final 생성자명 )으로 주입합니다.
  • 스프링이 관리하는 빈으로 등록된 객체 사용: 스프링이 빈으로 관리하는 인스턴스를 자동으로 주입해 사용하게 됩니다.

예시코드

  @Service
  public class MyService {
      private final MyRepository myRepository;
      // 생성자 주입 방식 (권장)
      public MyService(MyRepository myRepository) {
          this.myRepository = myRepository;
      }
      public void performAction() {
          myRepository.saveData();
      }
    }
     @Repository
     public class MyRepository {
     	public void saveData() {
        // 데이터 저장 로직
    }
 }

5.4 컴포넌트 스캔

스프링에서 컴포넌트 스캔(Component Scan) 은 @Service, @Repository, @Controller, @Component와 같은 스테레오타입 애노테이션이 붙은 클래스들을 자동으로 탐색하고, 이를 빈(Bean) 으로 등록하여 관리하는 중요한 과정입니다.

  • 단순히 스테레오타입 애노테이션을 붙이는 것만으로는 해당 클래스가 스프링 빈으로 관리되지 않으며, 반드시 컴포넌트 스캔이 이루어져야 합니다.
    컴포넌트 스캔을 활성화

컴포넌트 스캔의 필요성

  • 자동화된 의존성 관리
    컴포넌트 스캔을 통해 스프링이 자동으로 필요한 빈을 관리하고, 빈들 간의 의존성을 주입합니다. 이로 인해 개발자는 수동으로 객체를 생성하고 주입하는 번거로움을 덜 수 있습니다.
  • 프로젝트 유지보수성 향상
    컴포넌트 스캔을 통해 각 클래스가 빈으로 관리되므로, 코드 가독성이 좋아지고, 클래스의 역할에 따른 명확한 구조가 유지됩니다. 이를 통해 팀 간 협업 시 클래스의 역할과 위치를 쉽게 파악할 수 있습니다.
컴포넌트 스캔을 지시하는 법 -> 패키지의 하위 패키지를 포함하여 DI 컨테이너가 스테레오 어노테이션이 붙어있는 구상 클래스를 찾아줌. 

베이스 패키지 지정을 하여 하는 법
#자동으로 지정
package com.example.shopping;

@Configuration
@ComponentScan
public class ShoppingApplication {
    public static void main(String[] args) {
#베이스 패키지 지정
@Configuration
@ComponentScan("com.example.shopping")
public class ShoppingApplication {
    public static void main(String[] args) {

5.5 DI 컨테이너를 생성하고 Bean으로 가져오기

스프링에서는 애플리케이션이 실행될 때, 의존성 주입(DI)을 관리하는 DI 컨테이너가 생성됩니다. 이 컨테이너는 빈(Bean) 을 관리하고, 애플리케이션 전반에서 필요한 객체의 생명 주기와 의존성을 제어합니다. DI 컨테이너를 생성하는 대표적인 방법 중 하나로 AnnotationConfigApplicationContext 클래스를 사용하며, 이는 Java Config 클래스를 통해 설정된 빈들을 컨테이너에 등록하여 관리합니다.

ApplicationContext context = new AnnotationConfigApplicationContext(ShoppingApplication.class);

AnnotationConfigApplicationContext 클래스
AnnotationConfigApplicationContext 클래스는 애플리케이션에 필요한 빈들을 생성하고 관리하는 DI 컨테이너의 일종입니다.

  • Java Config 클래스 로드
    생성자의 인수로 전달된 Java Config 클래스(ShoppingApplication.class)를 로드하고, 해당 설정을 기반으로 DI 컨테이너를 초기화합니다.

  • 컴포넌트 스캔 및 빈 등록
    Config 클래스에 지정된 컴포넌트 스캔 설정을 통해 스테레오타입 애노테이션(@Component, @Service, @Repository, @Controller)이 붙은 클래스를 스캔하고, 이를 빈으로 등록합니다.

  • 빈 관리
    DI 컨테이너는 의존성을 가진 객체들 간의 관계를 관리하고, 필요한 곳에 자동으로 주입하여 사용할 수 있도록 합니다. 또한, 빈의 생명 주기(lifecycle)를 관리하여 객체를 재사용 가능하게 합니다.

5.6 생성자 말고 인젝션 하는 방법

아래의 3가지 방법이 있어도, 권장은 생성자 인젝션 final 한정자를 붙일 수 있음. 위에서 말했듯이 final 필드 내용을 변경하지 못하게 하기 때문.

  • 생성자 인젝션 : 생성자 인젝션의 인수로 의존 객체 하는 방법.
@Service
public class TrainingServiceImpl implements TrainingService {

    private TrainingRepository trainingRepository;

    // 생성자 주입 방식
    @Autowired // 생략가능
    public TrainingServiceImpl(TrainingRepository trainingRepository) {
        this.trainingRepository = trainingRepository;
    }
  • Setter 인젝션 : Setter 인젝션은 Setter 메서드의 인수로 의존 객체를 인젝션하는 방법
@Service
public class TrainingServiceImpl implements TrainingService {

    private TrainingRepository trainingRepository;

    @Autowired
    public void setTrainingServiceImpl(TrainingRepository trainingRepository) {
        this.trainingRepository = trainingRepository;
    }
  • 필드 인젝션 : 필드 인젝션은 직접 인젝션하는 방법, 직접 @Autowired를 붙인다.
@Service
public class TrainingServiceImpl implements TrainingService {

	@Autowired
    private final TrainingRepository trainingRepository;

5.7 같은 타입의 Bean이 여러 개 존재할 때 인젝션 방법.

오류를 피하는 방법 : 프로파일을 사용하는 방법을 이용하면 됨

그림출처 : 그림으로보는 스프링6

공부해야할 것 정리 : Bean Lifecycle

profile
문제를 해결하면서 나온 문제를 기록하는 노트

0개의 댓글