💋 스프링 빈(Spring Bean)이란?
Spring IoC 컨테이너가 관리하는 자바 객체
✔ IoC(Inversion Of Control)이 뭘까?
제어의 역전이다.
우리가 사용하던 자바 프로그램에서는 프로그래머가 직접 객체를 생성하고, 원하는 클래스 내에서 다른 객체를 생성해 사용했다. 이 경우 프로그래머가 객체의 생명 주기를 관리하고 있다.
제어의 역전이 일어나면, 프로그래머가 아닌 다른 무언가가 관리를 위임하게 된다. 프로그래머의 제어 권한을 다른 주체에게 넘기는 것을 IoC(제어의 역전)라고 한다. Spring은 직접 자바 객체를 생성하고 관리하기 때문에 이 관리 위임 주체는 Spring이 된다.
Spring이 생성하고 관리하는 자바 객체를 Bean이라고 한다.
Spring Framework에서는 ApplicationContext.getBean() 등의 메서드로 직접 자바 객체를 가져올 수 있다.
@Test
void componentScan() {
ApplicationContext context = getApplicationContext();
LineDao dao = context.getBean("lineDao", LineDao.class);
assertThat(dao).isNotNull();
LineService service = context.getBean("lineService", LineService.class);
assertThat(service).isNotNull();
}
💋 스프링은 어떻게 자바 객체가 있다는 것을 알 수 있을까?
두 가지 방법이 있다.
- 스프링은 약속되어 있는 어노테이션이 붙어있는 클래스를 자동으로 인지한다. 아래에서 설명하는 어노테이션이 붙어있다면, 스프링이 자동으로 인지할 수 있게 된다.
- Bean Configuration File에 직접 Bean을 등록하면 스프링이 인지할 수 있게 된다.
💋 1. 어노테이션을 붙인 클래스
✔ @Component
스프링에서 관리하는 요소라는 의미를 주는 일반적인 어노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 어노테이션 집중!!!
public @interface Service {
// ...
}
하지만, 단순히 스프링에서 관리한다보다 이 클래스가 어떤 역할을 하는지에 대해 더 정확히 설명하는 것이 좋을 때가 많다. 이 아래에 나와있는 어노테이션들은 @Component의 의미보다 구체적으로, 각각이 특정한 용도를 설명한 어노테이션이다. 용도에 적합한 어노테이션을 활용하면 더 딱 맞게 활용할 수 있어서, 특정 기능에 대해서는 더 수월하게 사용할 수 있게 된다!
아래에 웹에 클라이언트의 요청이 들어왔을 때, 서버가 어떻게 동작하는 지에 대한 간단한 그림이 있다.
이중 Controller, Service, Repository가 백엔드 개발자가 관심을 가지는 영역이다.
각 계층에 해당하는 어노테이션과 계층이 하는 역할에 대해서 정리해 보았다.
✔ @Controller
Spring MVC의 컨트롤러 역할을 하는 클래스를 표시하는 어노테이션
Controller의 역할 중 하나로는, View에 표시할 템플릿을 찾기 위한 String같은 것들을 반환하는 것이 있다.
(컨트롤러의 역할은 이외에도 정말 많다! 이것은 전통적인 Spring MVC 컨트롤러가 하던 역할임!)
✔ @RestController
어노테이션을 조합해서 새로운 어노테이션의 기능을 만들 수도 있다.
@RestController는 @Controller와 @ResponseBody를 함께 사용한 조합이다.
따라서 @RestController 에서는 @ResponseBody를 사용하지 않아도 코드는 정상적으로 동작한다.
@RestController 는 @ResponseBody 를 가지고 있기 때문이다.
Return Value 중 @ResponseBody와 ResponseEntity의 차이점에 대해 공부해보면, ResponseEntity는 반환되는 내용 이외에도 모든 상태 코드에 대한 정적 팩토리 메서드를 제공하고, 응답 본문과 함께 HTTP 상태 코드, 응답 헤더를 포함하기 때문에 @RestController에서도 추가적인 정보를 나타내고자 한다면 ResponseEntity의 형태로 반환해야 한다.
무엇보다도 @RestController에서 @ResponseBody를 붙이는 것처럼 중복된 어노테이션을 붙이지 않고, 일관성을 가지고 코드를 작성하는 것이 중요하다.
✔ @Service
서비스 역할을 하는 클래스를 표시하는 어노테이션
Controller는 들어온 요청을 처리하기 위해 Service를 호출하고, Service는 컨트롤러로부터 받은 정보를 자바 로직을 처리하는 방식으로 가공해 Controller에 다시 넘겨주는 역할을 한다.
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
Controller에서 Repository로 곧바로 정보를 전달하면 안되나?
Controller는 클라이언트와 맡닿아 있다. 클라이언트가 작성한 러프한 내용을 그대로 담고 있기 때문에, 이 정보를 바탕으로 Controller가 직접 DB에 접근해서 정보를 꺼내고, 저장하는 것은 위험하다.
따라서 정보 변동의 가능성이 있는 로직들은 Service 계층에서 처리하도록 하고, 이마저도 DB에서 꺼내온 원본이 아닌, DTO를 통해 만들어진 복사본을 사용해 DB의 안정성을 지키려는 것이다.
✔ @Repository
DAO 혹은 레포지토리 역할을 하는 클래스를 표시하는 어노테이션
DB나 파일 같은 외부 I/O에 접근하는 메서드 들을 사용하기 위한 인터페이스임을 표시할 수 있고, 예외의 자동 변환 기능을 제공한다.
// TODO: Exception Translation 키워드를 공부해보자!
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
💋 2. Bean Configuration File에 직접 Bean 등록
@Configuration
public class HelloConfiguration {
@Bean
public HelloController sampleController() {
return new SampleController;
}
}
[Spring] Spring Core(2): 의존성 주입(DI), 개념, 방법, 장단점, 생성자 주입을 사용하자!
💋 참고자료