⚙️ 개발환경 : Java 11, JDK 11, SpringBoot 2.6.17, Mustache, IntelliJ, Postman
⚙️ 필요 배경 지식 : Dispather-servlet(디스패처 서블릿), Annotation, Component Scan, MVC패턴
⚙️ 배울 내용 키워드 : @Controller, @RequestMapping, @ResponseBody
⚙️ 개요 : Component Scan을 사용하여 Http메소드 실행하기.
Component Scan시, 스프링의 디스패처 서블릿은 @Component어노테이션이 붙은 클래스를 찾아 메모리에 올려준다. (@Component가 붙어있는 메타 어노테이션은 @Service, @Repository, @Controller 등이 있다.)
이를 확인해보자.
** 주의할점은 Component Scan은 사용자가 직접 만든 도메인 패키지 내부만 스캔하기 때문에 basic_controller 내부에 Controller패키지를 만들어야 한다.
@Controller //컴퍼넌트 스캔(디스패처 서블릿이 찾아준다.)
public class HttpMethodController {
public HttpMethodController() {
System.out.println("컴퍼넌트 스캔됨");
}
성공적으로 해당 클래스가 스프링에 의해 생성됨을 확인할 수 있다. 이제 @GetMapping으로 디스패처 서블릿에게 Method레벨의 어노테이션을 등록한다.
이때 @Controller가 붙은 클래스내부 메소드에서 return은 기본적으로 View를 찾는다. 그렇기 때문에 데이터값(메세지 등)을 return 하고 싶다면 @ResponseBody 어노테이션을 메소드 앞에 붙이면 된다.
Http 메소드를 통해 데이터를 읽고 써보자. 먼저 Get이다.
@Controller //컴퍼넌트 스캔(디스패처 서블릿이 찾아준다.)
public class HttpMethodController {
public HttpMethodController() {
System.out.println("컴퍼넌트 스캔됨");
}
@GetMapping("/req/get")
public void methodGet(){
System.out.println("methodGet() 호출됨");
}
}
현재 localhost:9000을 쓰고 있기 때문에 "localhost:9000/req/get"을 입력하면 로그에 "methodGet() 호출됨"이 남게 된다.
디스패처 서블릿은 클라이언트로부터 요구사항을 전달받아 저장하는 request객체와 응답을 보내기 위한 response객체를 가지고 있다.
여기에 직접 접근하여 해당 값을 사용할 수 있는데, 매개변수에서 각각을 입력함으로써 메서드 내에서 다룰 수 있다.
// 디스패처 서블릿이 들고 있는 request와 response를 전달받고 싶을 때
@GetMapping("/req/get/v2")
public void methodGetV2(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("methodGetV2() 호출됨");
PrintWriter pw = response.getWriter(); //BW버퍼에 직접 입력함.
pw.println("<h1>Get</h1>"); //Content-Type은 text/html, Status Code는 200(정상작동완료)
}
웹페이지->f12를 통해 확인할 수 있는 개발자 창에서 Request Method : GET과 Status Code : 200을 확인할 수 있다.
만약 메소드의 return값이 void가 아닌 String값이라면 어떨까?
-> 스프링은 기본적으로 RequestMapping 객체의 return을 Viewname으로 인식한다. 즉, templates폴더(default)에서 "hello.mustache"파일을 찾아서 클라이언트에 전달한다.
@Controller
public class HttpMethodController {
@GetMapping("/req/get/v3")
public String methodGetV3() {
return "hello"; //templates안에 hello.mustache를 찾으러 간다. // 그리고 ViewResolver가 발동됨.
}
}
웹페이지 정보를 확인해보면, v3이름의 요청의 상태코드는 200인 것을 확인할 수 있다. requestdispatcher를 사용하면 클라이언트(외부)의 한번의 요청으로 처리할 수 있다. 이를 다음 Redirection과 비교해보자.
public class HttpMethodController {
@ResponseBody //나는 return시 ViewResolver말고 문자 그대로 표시하고 싶다!
@GetMapping("/req/get/v4")
public String methodGetV4() {
return "<h1>Get</h1>"; //return시 MessageConverter가 발동함.
}
@GetMapping("/req/get/v5")
public void methodGetV5(HttpServletResponse response) throws IOException {
response.sendRedirect("/req/get/v4");
}
}
redirect로 전달하면 아래와 같이 302상태로 클라이언트에 보내고 다시 v4를 받아오는 것을 알 수 있다.
이유가 무엇일까??
stateless서버는 request를 저장하지 않기 때문에 httpHeader에 hello.mustache 정보를 전달한 후 hello.mustache를 view에 전달하는 과정을 거친다.
이 과정에서 총 2번의 외부 요청이 있게 되기 때문에 v5와 v4의 request가 표시된것이다.
반면, return string으로 view파일을 찾아서 화면에 표시하는 방식은 디스패처 서블릿이 requestdispatcher로 내부에서 바로 view로 전달했기 때문에 한번의 request기록이 남게 된다.
📌 sendRedirect vs requestDispatcher
: 리소스 관점에서 redirect는 request가 두번 만들어지기 때문에 requestdispatcher가 효율적이다.
: sendRedirect방식은 client가 바로 hello.mustache를 요청할 수 있기 때문에 외부에서 바로 접근할 수 있는 위험이 있다. (접근 통제가 안됨)
RestAPI 규칙을 만족하는 Restful API를 만들기 위해선 Http메서드로 자료를 CRUD(Create-Read-Update-Delete) 할수 있어야 한다.
웹서버에서는 Http메서드 중 Get만 확인할 수 있다.
그래서 Postman를 통해 http메서드 호출을 확인해 보았다.
public class HttpMethodController {
@GetMapping("/req/get")
public void methodGet(){
return "<h1>Get</h1>";
}
@ResponseBody
@PostMapping("/req/post")
public String methodPost(){
return "<h1>Post</h1>";
}
@ResponseBody
@PutMapping("/req/put")
public String methodPut(){
return "<h1>put</h1>";
}
@ResponseBody
@DeleteMapping("/req/delete")
public String methodDelete(){
return "<h1>delete</h1>";
}
}
그 중 Post를 예시로 보면 url을 통해 request를 보내면 "Post"를 확인할 수 있다.