Spring Framework/토비의 스프링

Chapter 04. 독립 실행형 스프링 애플리케이션

계란💕 2023. 6. 24. 15:07


스프링 컨테이너 사용

  • 지난 시간까지는 독립 실행이 가능한 서블릿 애플리케이션을 만들었다. 
  • 그러면 독립 실행형 스프링 애플리케이션은 어떻게 만들 수 있을까?
  • 1) POJO(Plain Old Java Object): 비지니스 로직을 담은 Java 오브젝트 (상속 X, )
  • 2) 구성 정보를 담은 Configuration 메타 데이터
  • 에러가 나지 않는한 서블릿 컨테이너가 기본적으로 Http response 200을 세팅해서 넣어준다. (생략 가능)

 

 

 

 

  Ex) 

java
열기

 

  • GenericApplicationContext: 코드에 의해 손쉽게 만들 수 있는 애플리케이션 컨텍스트 
    • registerBean() : 빈 등록한다. 
    • refresh(): 컨테이너 초기화
  • 이로써 스프링 컨테이너를 만들었다.

 


의존 오브젝트 추가

  • 앞에서 코드를 이용해서 스프링 컨테이너를 생성했다. 이 방법은  프론트 컨트롤러가 new 키워드를 이용해서 직접 오브젝트를 만들어 쓰는 방법과 어떻게 다를까?
  •  스프링 컨테이너는 나중에도 적용할 수 있도록 기본 구조를 짜놨다는 것이 중요한다. 
  •  스프링 컨테이너는 어떤 오브젝트를 만들 때, 딱 하나만 만들어서 재사용한다. => "싱글톤 패턴"

 

 

 

  • 웹을 통해 들어온 유저의 요청 사항을 검증하고 비지니스 로직을 담당하는 다른 오브젝트에게 요청을 보내서 결과를 돌려 받고 클라이언트에 어떻게 돌려줄 것인가. 
  • SimpleHelloService 라는 빈을 만든다. => 컨트롤러는 책임이 줄어든다. 

 

  Ex) 

java
열기
  • name 이 넘어오지 않으면 예외처리를 한다. 
  • Objects.requireNonNull(): 파라미터로 들어온 데이터가 null인 경우 예외 처리하고 아닌 경우는 파라미터를 반환한다. 
  • 기존의 컨트롤러 클래스에서 애너테이션을 제거하고 서비스 로직을 서비스 클래스에 위임한다. 

 


Dependency Injection

컨트롤러가 서비스에 의존한다. 의존관계를 나타낸다. 출처 - 토비의 스프링

  • SimpleHelloService 의 로직이 바뀌면 컨트롤러는 영향을 받는다. 
  • 변경이 일어날 때마다 의존 관계에 있킄 클래스들 또한 수정이 필요하다. 이런 문제를 해결하기 위한 소프트웨어 원칙이 의존 관계 주입이다. 
  • Assembler(어셈블러): 디펜던시 인젝션을 위한 제3 의 존재

 

  • 어셈블러가  바로 스프링 컨테이너이다. 

 

 

  • 우리가 메타 정보를 주면 이를 가지고 싱글톤 오브젝트를 만드는데 이 오브젝트를 주입하는 작업을 어셈블러가 담당한다. 
  • 생성자 주입: Controller 클래스를 만들 때 생성자 파라미터로 Service를 넣어준다. 대표적이고 쉬운 방법
  • 팩토리 메서드로 빈을 만들어서 property를 만들어서 setter를 만든다?

 


의존 오브젝트 DI 적용

 

  • 위와 같이 인터페이스를 구현하는 방식으로 변경이 되고 override 애너테이션이 붙는다. 새로운 인터페이스  HelloService가 생성된다. 
    • Service라는 인스턴스를 만들 필요 없이 어셈블러인 스프링 컨테이너가 컨트롤러의 클래스의 오브젝트를 만들 때, 생성자 파라미터로 주입할 수 있도록 변경한다. 

 

  Note) 인터페이스 추출 결과

 

  • HelloService
java
닫기
public interface HelloService {
    String sayHello(String name);
}

 

  • SimpleHelloService: HelloService를 구현하는 방식으로 변경된다. 
java
닫기
public class SimpleHelloService implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello! " + name;
    }
}

 

  • HelloController
    • 기존에는 컨트롤러가 Service 인스턴스를 직접 만드는 방식이었지만
    • 어셈블러인 스프링 컨테이너가 HelloController 오브젝트를 만들 때, 생성자 파라미터로 주입할 수 있도록 바꿀 예정이다. 
java
닫기
public class HelloController {
    public String hello(String name) {
        SimpleHelloService service = new SimpleHelloService();
        return service.sayHello(Objects.requireNonNull(name));
    }
}

 

  • 생성자 주입
java
열기

 

  • Application 코드를 다음과 같이 바꾼다. 
    • 스프링 구성 정보를 만들 때는 반드시 클래스가 필요하며 인터페이스로는 만들 수 없다. 
    •  registerBean()을 통해 빈을 생성하고 나면  getBean()을 이용하면 빈 정보를 가져올 수 있다. 
java
열기

  


DispatcherServlet으로 전환

  • DispatcherServlet: 스프링이 처음 나올 때부터 많은 기능을 수행하는 Servlet 클래스가 들어있다. 프론트 컨트롤러의 많은 기능을 수행한다. 
  • 서블릿 컨테이너를 관리하지 않고 싶다. 자동으로 관리 되게끔 하려면?
  • DispatcherServlet:을 이용하면 기존의 service() 를 오버라이드할 필요 없다. 
    • 다른 Servlet을 집어 넣는다. 
  • DispatcherServlet은 웹 환경에서 쓰도록 만들어진 GenericWebApplicationContext가 필요하다. 
    • GenericApplicationContext => GenericWebApplicationContext로 바꾼다. 

GenericApplicationContext  오류

 

  • 이제  dispatcherServlet에게 applicationContext를 넘긴다.
  • dispatcherServlet이 매핑하다가 작업을 위임할 오브젝트를 찾아야하는데 그 때 사용할 서블릿이 applicationContext가 된다. 
java
열기

 

  Note) 실행 결과 - 에러 정상

  • dispatcherServlet에게 어떤 오브젝트가 웹 요청을 가지고 왔는지 요청 정보를 넘겨줘야하는데 이와 관련한 정보를 주지 않았기 때문에 당연히 오류가 발생한다. 
  • 과거에는 매핑 정보(url, bean)를 모두 xml 파일에 넣어줘야했다. 
  • 현재는 요청을 처리할 컨트롤러에 직접 매핑 정보를 넣는 방법을 사용한다. 

 


애너테이션 매핑 정보 사용

  • @GetMapping 는 @RequestMapping(value = "", method = "RequestMethod.GET")와 같다. 
  • 클래스 위에 @RequestMapping()을 붙이면  안에 있는 path에 이어서 method에 붙은 path를 이어붙인다. 
  • 컨트롤러 메서드의 반환형이 String일 경우, 컨트롤러가 처리하는 방식이 있다. 반환값과 이름이 같은 View 파일을 찾는다. 
    • 그런데 지금, "Hello momo!"라는 View 가 존재하는 게 아니라 응답 값으로 보여주고 싶은 상황이다.  
    • 이럴 때에는 컨트롤러의 메서드 앞에 @ResponseBody를 붙이면 해결된다.  
    • @RestController: 애너테이션 앞에  "Rest" 를 붙어 있으면 각 메서드에 @ResponseBody를 적용해준다.

  Ex) 

java
열기

 

 


스프링 컨테이너로 통합

  • application.refresh(): 스프링 컨테이너의 초기화 작업이 이뤄진다. 
  • 템플릿 메서드 패턴을 이용하면 상속을 통한 기능 확장이 가능하다. 
    • onRefresh() 를 오버라이드한다. 
    • 컨테이너를 초기화하는 코드를 onRefresh() 안에 넣어준다. 
    • super 클래스를 호출하는 코드 super.onRefresh는 생략하면 안 된다. 
java
열기

 


자바코드 구성 정보 구성

  • 클래스 앞에 @Configuration를 붙여서 안에 빈 팩토리 메서드가 있다는 것을 스프링 컨테이너에게 알려준다.
    •  구성 정보도 함께 넣어준다. 
  • 기존에 쓰던 GenericWebApplicationContext =>  AnnotationConfigWebApplicationContext
java
열기

 


@Component 스캔

  • 클래스 위에 @Component 를 붙이면 컴포넌트 스캐너가 해당 클래스를 빈으로 등록한다. 
  • @ComponentScan: ApplicationContext 클래스 위에 애너테이션을 붙이면 해당 클래스의 패키지부터 시작해서 하위패키지를 하나씩 확인하면서 컴포넌트를 등록한다. 
    • 새로운 빈을 추가하는 경우, 구성 정보를 매번 등록할 필요 없이 @Component 만 붙이면 된다. 
  • 메타 애너테이션:  애너테이션 위에 붙은 애너테이션을 말한다. 
    • ex)
    • @Controller: @Component가 @Controller의 메타 애너테이션이다. 
    • @RestController: @Controller가  @RestController의 메타 애너테이션이다. 

 

  • 애너테이션 생성
    • 애플리케이션이 실행되는동안 유지된다.
    • 어느 계층에서 어느 역할을 하는 애너테이션인지 표현할 수 있다. 
java
열기

 

  • 그리고 컨트롤러 위에 다음과 같이 붙여준다. 
    • 그러면 컨트롤러 위에 컴포넌트를 붙인 것처럼 똑같이 컴포넌트로 등록해준다. 
java
열기

 

@RestController의 메타 애너테이션은 @Controller이다.

 


Bean의 생명 주기 메서드

 

  • 독립 실행형 애플리케이션 Object 생성 방법 - 애
  • 1) TomcatServletWebServerFactory
  • 2) DispatchServlet

 

 

  • 다음과 같이 @Bean을 통해  두 개의 빈을 등록한다. 

 

  • this.getBean(ServletWebServerFactory.class)
    • ServletWebServerFactory 라는 빈은 없지만 ServletWebServerFactory 타입의 빈은 딱 하나뿐이므로 그걸 가져온다. 
  • 애플리케이션 컨텍스트를 주입하지 않았다. 
    • 스프링 컨테이너가 DispatcherServlet은 애플리케이션 컨텍스트가 필요하겠다고 판단하여 알아서 주입해준 것이다. 
java
열기

 

  • 컨트롤러
    • 스프링 컨테이너가 초기화되는 시점에 오버라이드한 setApplicationContext()가 실행된다. 
    • 서버를 띄우기만 해도 메서드가 실행된다. 
    • DispatcherServlet을  팩토리메서드에서 생성자 없이 인스턴스를 생성해서 반환하더라도 문제없이 동작한다. 
java
열기

 

  Note) 실행 결과

 


SpringBoot Application

  • 기존에 HelloBootAppluication 클래스의   applicationContext.register(HellobootApplication.class) 를 바꾸려고 한다.
  • 원래 짰던 코드를 다음과 같이 바꿔주면 
  • main 메서드가 있는 클래스가 달라지더라도 해당 클래스만 파라미터로 넘겨주면 되기 때문에 다른 메인이 되는 클래스에서도 main()을 재사용할 수 있다. 
    • 새로운 클래스 MySpringApplication을 만들고 run() 메서드를 이동한다. 

 

  • HellobootApplication
    • 그러면 상단에 있는 빈 메서드를 생략해도 될까?
    • servletWebServerFactory() 메서드는 서블릿 컨테이너를 생성하는 빈이므로 반드시 필요하다. 
java
열기

 

  • MySpringApplication
java
열기

 


title

  • content
  • content

title

  • content
  • content

출처 - 인프런 토비의 스프링

https://www.inflearn.com/course/%ED%86%A0%EB%B9%84-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%9D%B4%ED%95%B4%EC%99%80%EC%9B%90%EB%A6%AC/dashboard