Java - Spring &&n SpringBoot

Spring - 컴포넌트 스캔과 자동 의존 관계 설정, 자바 코드로 스프링 빈 등록하기(feat. Annotation)

TerianP 2021. 12. 14.
728x90

해당 글에서는 IoC 제어의 역전과 DI 의존성 주입 개념이 포함되서 설명됩니다. 때문에 이를 모르시는 분들은 아래 글을 참고해주시기 바랍니다.

2021.12.13 - [Java - Spring] - 자바 Spring 개념 잡기 : IoC 제어의 역전, 의존성 주입(Dependency Injection) 와 컨테이너, 스프링 빈

 

자바 Spring 개념 잡기 : IoC 제어의 역전, 의존성 주입(Dependency Injection) 와 컨테이너, 스프링 빈

공부하면서 정리한 내용입니다. 틀린 부분이 있을 수 있으니 언제든지 지적 환영합니다. 1. DI(Dependency Injection) : 의존성 주입 의존성 주입에 대해서 설명하기 이전에 '의존성, 의존관계' 이 무엇

terianp.tistory.com

 

1. 회원가입 서비스와 의존관계

회원가입 조회 등의 서비스가 실행되기 위해서는 이전에 글에서 잠깐 보았던 스프링 Controller 와 이전 글에서 만들었던 memberService 를 서로 연결하여 의존관계를 설정해주어야 한다. 서로 의존관계가 설정된 후에야 원활하게 기능이 동작하기 때문이다.

그렇다면 의존관계 설정을 어떻게 하는가? 기본적으로 스프링에서 객체 생명주기 관리는 스프링 컨테이너가 담당한다. 다만 무조건 스프링에서 만들어진 객체라고 스프링에서 관리하는 것이 아니기 때문에 우리가 해야하는 일은 우리가 만들어 두었던 객체를 앞으로 스프링 컨테이너에서 관리하도록 도장(어노테이션 annotation)을 찍어서 스프링이 확인 할 수 있도록 해주어야 한다.

 

즉 우리가 해야하는 것은 회원 컨트롤러 memberController 가 회원 서비스 memberService 와 회원 리포지토리 memberRepository 를 사용할 수 있도록 의존 관계를 설정하는 것이다.

 

2. 자동 의존 관계 설정 방법

의존 관계를 스프링에서 담당하도록 설정하는 방법은 크게 2가지가 있다. 첫 번째로는 스프링 어노테이션을 사용한 컴포넌트 설정을 통해 의존 관계를 설정하도록 하는 방법과 두 번째로는 스프링 빈 Configuration을 사용하는 방법이다.

 

1) 컴포넌트 스캔 : 어노테이션 Annotation 을 사용한 스프링 빈 등록하기

- What is Annotation?

컴포넌트 설정을 통한 의존 관계 설정을 설명하기 위해서는 먼저 어노테이션에 대해서 살짝 알아야한다.

어노테이션 Annotation 은 사실 어려운 개념이 아니다. 어노테이션은 사전적 의미로 주석이라는 뜻이며, 자바에서 사용 될 때 어노테이션은 코드 사이에서 주석처럼 쓰여서 특별한 의미, 기능을 수행하도록 하는 기술이다. 즉, 프로그램에게 추가적인 정보를 제공해주는 메타 데이터(Meta data : 데이터를 위한 데이터)라고 생각하면 된다.

어노테이션의 주로 다음 3가지 역할을 한다.

  • 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공한다.
  • 소프트웨어 개발툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
  • 실행 시 특정 기능을 실행하도록 정보를 제공한다.

마지막으로 사용법은 정말 간단한데! 코드에 [ @어노테이션 명 ]을 사용하여 어노테이션을 배치 & 사용한다.

 

대표적으로 스프링을 처음 구동할때 Controller 를 생성할 때 보았던 @Controller 나 스프링 애플리케이션 클래스에서 찾아 볼 수 있는 @SpringBootApplication 등이 있다.

 

@Controller && @GetMapping

// My_Controller 클래스
=

@Controller
public class My_Controller {

    @GetMapping("hello")
    public String hello(Model model) { // MVC : model view controller 중 model
        model.addAttribute("data","hello!!"); // data 가 넘어오면 hello!! 출력
        return "hello";
        // viewResolver 가가화면을 찾아서 처리함
        // resources : 'templates/' + (ViewName)+'.html'
    }

@SpringBootApplication

@SpringBootApplication
public class HelloSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSpringApplication.class, args);
	}

}

 

-  어노테이션 Annotation 으로 스프링 빈 등록하기

 

그렇다면 스프링에서 어노테이션이 붙으면 어떻게 동작할까? 어노테이션이 붙은 코드는 스프링이 실행되는 순간 해당 어노테이션을 스프링이 인식하고, 해당 어노테이션이 붙은 클래스 객체를 생성하여 스프링 컨테이너에 넣어둔다.

 

이때 스프링에서는 빈을 생성할 때 각 클래스당 유일한1개의 객체만 생성해서 갖고 사용하게 된다. 이를 싱글톤(유일하게 하나만 등록해서 공유한다) 라고 한다. 따라서 같은 스프링 빈(스프링 객체)면 모두 같은 인스턴스.

 

여기서 IoC의 개념이 살짝 등장하는데 스프링이 실행되는 순간 어노테이션이 붙은 클래스의 객체를 생성해서 스프링 컨테이너에 넣어두고 스프링 컨테이너가 관리하게 된다. 이때부터는 개발자가 객체의 생명 주기를 관리하는것이 아니라 스프링에서 생명 주기를 담당하게 된다. 즉 객체 제어권이 역전 IoC된 것이다.

또한 스프링 컨테이너에서 객체를 관리하기 때문에 의존성 주입 DI 역시 스프링 컨테이너에서 해주게 되고, 이를 위해서 우리가 해주어야하는 것은 역시나 어노테이션을 붙이는 것이다.

 

하나 더 사족을 붙이자면 이 방식을 컴포넌트 스캔 방식이라고 하는 이유는 @Service @Controller 등 어노테이션을 붙여서 사용할때 이 어노테이션들들의 기초/기본? 이 되는 어노테이션이 @Component 혹은 @ComponentScan 이기 때문이라고 생각하면 된다.

 

이제 코드로 설명하겠다. 앞에서 우리가 만들었던 코드에 어노테이션을 붙여서 의존 관계를 스프링에서 담당하도록 한다.

  • MemberService : 서비스를 담당하는 자바 클래스에는 @Service 어노테이션
@Service
public class memberService {
-------
}

 

  • MemoryMemberRepository : 저장소 역할을 하는 자바 클래스에는 @Repository 어노테이션
@Repository
public class MemoryMemberRepository implements MemberRepository{
--------
}

 

  • MemberController : 컨트롤러 역할을 하는 자바 클래스에는 @Controller 어노테이션
  • 여기서 @Autowired 어노테이션도 보이는데 이것은 스프링에서 DI 를 해주게 하기위한 코드이다. 이렇게 해두면 MemberController 객체가 생성될때 생성자에서 필요한 memberService 는 스프링 컨테이너에 저장되어있던 객체가 들어오게 된다. 왜냐하면 우리가 memberService에 @Service 어노테이션 을 붙여두었으니까.
@Controller
public class MemberController {

    private final memberService memberService;

    @Autowired // DI 를 스프링 컨테이너에서 담당하게 하기 위한 어노테이션
    public MemberController(memberService memberService) {
        this.memberService = memberService;
    }
}

 

대표적으로 사용되는 스프링 어노테이션의 종류와 용도는 아래 표를 참고!

(여기서 Bean 이란 스프링 컨테이너가 관리하는 객체)

어노테이션 Annotation 종류 용도
@Component 개발자가 작성한 class를 Bean으로 등록하기 위한 가장 기본적인 어노테이션. 참고로 @Service, @Controller 대신 이것만써도 무방하다. 다만 이렇게 쓰면 실제로 해당 클래스가 어떤 역할을 하는지 알기 힘들고, 그외 여러 문제가 있을 수 있다고 한다.
@ComponentScan @Component와 @Service, @Repository, @Controller, @Configuration이 붙은 클래스 Bean들을 찾아서 Context에 bean등록을 해주는 Annotation
@Bean 개발자가 직접 제어가 불가능한  외부 라이브러리 등을 Bean 으로 만들려 할 때 사용되는 Annotation
@Autowired DI 의존성 주입을 위한 어노테이션. DI 시 field, setter , 생성자에서 사용하며 Type 에 따라 알아서 Bean을 주입한다.
참고로 생성자를 통한 DI 를 권장한다.
@Controller Controller 를 의미하는 어노테이션. spring MVC에서 Controller 클래스에서 사용됨
@RestController 스프링에서 Controller 중 view 로 응답하지 않는 controller 를 의미한다. 메서드 의 반환 결과를 JSON 형태로 반환한다.
이 어노테이션이 적혀있는 controller 의 메서드는 HttpResponse 로 바로 응답이 가능하다.
@Service Service class 에서 쓰인다. 비즈니스 로직을 수행하는 class 를 나타내는 용도
@Repository DAO class 에서 사용된다. 주로 DB 에 접근하는 메서드를 갖는 class에서 자주 쓰인다.
@Configuration @Configuration 을 클래스에 적용하고 @Bean을 해당 클래스의 메서드에 적용하면 @Autowired 로 B
@EnalbeAutoConfiguration Spring Application Context 를 만들 때 자동으로 설정하는 기능을 켠다. classpath 의 내용에 기반해서 자동으로 생성해준다. 만약 tomcat-embed-core.jar가 존재하면 톰캣 서버가 setting 된다(여기는 잘 모르겠어요ㅠㅠ)
@Required setter 메서드에 적용하면 Bean 생성 시 필수 프로퍼티 임을 알린다. 이를 통해  optional 하지 않은 꼭 필요한 속성들을 정의한다. 영향을 받는 bean property를 구성할 시에는 XML 설정 파일에 반드시 property를 채워야 한다.
@Lazy 지연 로딩을 지원한다. @Component나 @Bean Annotation과 같이 쓰는데 Class가 로드될 때 스프링에서 바로 bean등록을 마치는 것이 아니라 실제로 사용될 때 로딩이 이뤄지게 하는 방법이다.
@SpringBootApplication @Configuration, @EnableAutoConfiguration, @ComponentScan 3가지를 하나의 애노테이션으로 합친 것이다.
@RequestMapping - 요청 URL을 어떤 메서드가 처리할지 mapping 해주는 Annotation 이다. Controller 나 Controller 의 메서드에 적용한다. 요청을 받는 형식인 GET, POST, PATCH, PUT, DELETE 를 정의하기도 한다.
- 만약 요청 받는 형식을 정의하지 않는다면, 자동적으로 GET으로 설정

 

2) 자바 코드로 스프링 빈 등록하기

1) 에서는 어노테이션을 사용하여 자동으로 스프링 빈에 등록하는 방법이었다면 이번에는 내가 직접 수동으로 스프링 빈에 등록하는 방법이다. 아래 코드는 1) 에서 했던 어노테이션이 없다는 가정하에 진행한다.

  • SpringConfig 클래스 생성 : memberService 와 같은 패키지 안에 넣어둠
@Configuration // 스프링 빈에 등록하기 위한 설정파일이라는 Annotation
public class SpringConfig {

    @Bean // Bean 에 등록되어야하는 객체라는 의미의 Annotation
    public memberService memberService(){
        return new memberService(memberRepository());
        // 아래에서 생성된 memberRepository 객체를 넣어줌. 의존성 주입 DI
    }

    @Bean
    // 여기서 MemberRepository 는 인터페이스, MemoryMemberRepository 가 구현체
    // 따라서 구현체를 객체로 가져와야함
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

 

  • MemberController : 컨트롤러에서는 그대로 설정해둠
@Controller // 컨트롤러는 그냥 그대로
public class MemberController {

    private final memberService memberService;

    @Autowired
    // Bean 으로 설정되었던 memberService 를 넣어줌. 의존성 주입
    public MemberController(memberService memberService) {
        this.memberService = memberService;
    }
}
  • 참고로 이전에는 Configration 파일로 설정하는게 아니라 XML 로 설정했다고 한다. 요즘이 훨씬 좋다ㅋㅋ
  • 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 다만 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록해주는게 좋다.
    • 예를 들어 고정되서 변경할 필요가 없다면 그냥 컴포넌트 어노테이션으로 설정해서 편하게 하고, 만약 추후에라도 클래스를 변경해야하면 스프링 빈 config 로 설정하는게 좋다고한다. 이렇게 해두면 추후 바꿀때는 config 파일만 수정하면 되기 때문이란다.
    • 나는 추후 Memory 저장방식에서 DB 저장방식으로 바꿀 예정임으로 스프링 빈 방식으로 설정해놓도록 하겠다.

 


참고

 

https://honeyinfo7.tistory.com/56

 

Java에서 어노테이션(Annotation) 이란 무엇인가에 대해 알아보자.

 자바에서 어노테이션이라는 기술이 스프링 프레임워크상에서 자주 쓰이지만 이 기술에 대해 자세히 알아보지도 않고 IT에 몸담고 있는 5년간 대충 아는정도에 밖에 그치지 못하고 있다. 이러

honeyinfo7.tistory.com

https://velog.io/@gillog/Spring-Annotation-%EC%A0%95%EB%A6%AC

 

[Spring] Annotation 정리

Annotation(@)은 사전적 의미로는 주석이라는 뜻이다. 자바에서 사용될 때의 Annotation은 코드 사이에 주석처럼 쓰여서 특별한 의미, 기능을 수행하도록 하는 기술이다.

velog.io

 

댓글