공부/Spring

토비의 스프링 부트 - 독립 실행형 스프링 애플리케이션-1

데부한 2023. 4. 17. 01:01
반응형

출처 : 인프런

스프링 컨테이너 사용

기존 소스 코드 정리

  • FrontController의 로직 중 'else if~' 구문 삭제
  • HTTP Status Code 세팅 코드 삭제 (에러가 나지 않는 한 알아서 200 OK 코드가 붙어 나간다.) 즉, 생략가능
  • HTTP Header에 세팅한 Content-Type 부분 setContentType() 메서드로 변경. 이러면 header ID를 생략하고 값만 넣으면 됨
public class HellobootApplication {
   public static void main(String[] args) {
      TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
      // 다양한 서버 프로그램을 위한 추상화를 위해 WebServer
      WebServer webServer = serverFactory.getWebServer(servletContext -> {
         HelloController helloController = new HelloController();
         servletContext.addServlet("frontController", new HttpServlet() {
            @Override
            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
               if(req.getRequestURI().equals("/hello") && req.getMethod().equals(HttpMethod.GET.name())) {
                  String name = req.getParameter("name");

                  String ret = helloController.hello(name);

                  resp.setContentType(MediaType.TEXT_PLAIN_VALUE);
                  resp.getWriter().println(ret);
               }
               else {
                  resp.setStatus(HttpStatus.NOT_FOUND.value());
               }
            }
         }).addMapping("/*");
      });
      webServer.start();
   }
}

 

스프링 컨테이너를 만들기 위해 helloController를 불러왔던 코드를 삭제하고 스프링 컨테이너를 사용한다.

public class HellobootApplication {
   public static void main(String[] args) {

      GenericApplicationContext applicationContext = new GenericApplicationContext();
      applicationContext.registerBean(HelloController.class); // bean 등록 끝
      applicationContext.refresh();

      ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
      // 다양한 서버 프로그램을 위한 추상화를 위해 WebServer
      WebServer webServer = serverFactory.getWebServer(servletContext -> {
         servletContext.addServlet("frontController", new HttpServlet() {
            @Override
            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
               if(req.getRequestURI().equals("/hello") && req.getMethod().equals(HttpMethod.GET.name())) {
                  String name = req.getParameter("name");

                  // 등록되어있는 bean을 가져와서 사용할 수 있음
                  HelloController helloController = applicationContext.getBean(HelloController.class);
                  String ret = helloController.hello(name);

                  resp.setContentType(MediaType.TEXT_PLAIN_VALUE);
                  resp.getWriter().println(ret);
               }
               else {
                  resp.setStatus(HttpStatus.NOT_FOUND.value());
               }
            }
         }).addMapping("/*");
      });
      webServer.start();
   }
}

 

서버 실행 후 테스트

 

 

 

의존 오브젝트 추가

스프링 컨테이너는 싱글톤 패턴과 유사하게 어떤 타입의 오브젝트를 한 번만 만들고 이를 계속 재사용하는 방식으로 동작한다. 그래서 스프링 컨테이너를 `싱글톤 레지스트리`라고도 한다.

SimpleHelloService.java 생성

public class SimpleHelloService {
    String sayHello(String name) {
        return "Hello " + name;
    }
}

 

HelloController.java 수정

public String hello(String name) {
    SimpleHelloService helloService = new SimpleHelloService();
    return helloService.sayHello(Objects.requireNonNull(name));
}

Objects.requireNonNull() 메서드의 경우 매개변수가 null인지 아닌지 판별해주며 Null일때는 NullPointException을 터트려주며, 아니면 예외 발생 없이 사용할 수 있는 메서드이다.

 

서버 재실행 후 테스트

 

 

 

의존 오브젝트 DI 적용

Refactor를 이용해 interface 생성

 

Interface name 변경 및 interface Member sayHello 선택 후 Refactor 버튼을 클릭하면 interface도 만들어지고 SimpleHelloService도 그에 맞게 소스 코드가 수정된다.

 

 

HelloService.interface 생성

public interface HelloService {
    String sayHello(String name);
}

 

 

SimpleHelloService.java 수정

public class SimpleHelloService implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

 

 

HelloController.java 수정

public class HelloController {
    private final HelloService helloService;

    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    public String hello(String name) {
        return helloService.sayHello(Objects.requireNonNull(name));
    }
}

 

HelloService도 스프링 컨테이너에 Bean 등록을 해야한다.

HellobootApplication.java 수정

public static void main(String[] args) {

   GenericApplicationContext applicationContext = new GenericApplicationContext();
   applicationContext.registerBean(HelloController.class); // bean 등록 끝
   applicationContext.registerBean(SimpleHelloService.class);
   applicationContext.refresh();
   
   // ... 생략

 

서버 재실행 후 테스트

 

 

 

DispatcherServlet으로 전환

기존 서블릿 등록했던 코드 모두 삭제 후 DispatcherSerlvet 추가

public class HellobootApplication {
   public static void main(String[] args) {

      GenericWebApplicationContext applicationContext = new GenericWebApplicationContext();
      applicationContext.registerBean(HelloController.class); // bean 등록 끝
      applicationContext.registerBean(SimpleHelloService.class);
      applicationContext.refresh();

      ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
      // 다양한 서버 프로그램을 위한 추상화를 위해 WebServer
      WebServer webServer = serverFactory.getWebServer(servletContext -> {
         servletContext.addServlet("dispatcherServlet",
               new DispatcherServlet(applicationContext)
               ).addMapping("/*");
      });
      webServer.start();
   }
}

 

서버 재실행 후 테스트하면 404 에러가 발생한다. DispatcherServlet 까지는 요청이 잘 들어오는데 그 이후에 알맞은 컨트롤러로 보내기엔 정보가 부족하기 때문이다. 

 

 

 

애노테이션 매핑 정보 사용

HelloController.java 수정

@RequestMapping("/hello")
public class HelloController {
    private final HelloService helloService;

    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping
    public String hello(String name) {
        return helloService.sayHello(Objects.requireNonNull(name));
    }
}

 

서버 재실행 후 테스트하면 404 에러가 여전히 발생한다. hello() 메서드도 잘 호출했고 helloService의 sayHello() 메서드도 잘 호출했을 것이다. 문제는 `return 타입`이다. DispatcherServlet은 return 타입이 String이면 `view`라고 불리는 HTML 템플릿을 찾아 view를 return해주라라는 기본 동작 방식 때문이다. 그렇기 때문에 String으로 리턴하려면 view가 아닌 text라는 걸 알려주기 위해 `@ResponseBody`라는 애노테이션을 추가로 붙여주면 된다.

@GetMapping
@ResponseBody
public String hello(String name) {
    return helloService.sayHello(Objects.requireNonNull(name));
}

 

서버 재실행 후 테스트

 

 

 


- 출처 : 인프런 토비의 스프링부트 - 이해와 원리 강의

반응형