스프링 컨테이너 사용
기존 소스 코드 정리
- 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));
}
서버 재실행 후 테스트
- 출처 : 인프런 토비의 스프링부트 - 이해와 원리 강의