SPRING/Spring MVC

[SpringMVC] HTTP 응답 - Controller 반환타입

IT록흐 2023. 8. 14. 17:10
반응형
 

[SpringMVC] 응답(Response)의 종류 ( Text, Html, Json )

[SpringMVC] 요청(Request)의 종류 ( GET, POST, JSON ) [SpringMVC] 웹서비스에서 Request(요청)가 처리되는 원리 ( + Servlet ) JAVA Runtime Enviroment(JRE)는 하나의 프로세스, 다수의 스레드 환경이다. JVM은 하나의 프로

lordofkangs.tistory.com

 

지난 포스팅에서 서버에서 클라이언트로 보내는 응답(Response)의 종류에 대해서 다루어 보았다.

 

응답의 종류는 크게 2가지로 나뉜다. 

 

 

 

 

 

웹브라우저(Client)는 HTML로 화면을 그린다. HTML을 어디서 동적으로 렌더링하느냐에 따라, 서버의 응답이 달라진다. 

 

1) 서버에서 동적으로 화면을 생성하는 경우 ( 뷰템플릿 방식 ) ( ex. JSP, Thymleaf )

2) 클라이언트에서 동적으로 화면을 생성하는 경우 ( HTTP 메시지 방식 ) ( ex. React, vue.js )

 

그러므로 개발자는 비즈니스 로직을 처리하는 Controller 코드를 작성할 시, 환경에 맞는 데이터를 반환해야 한다. Controller가 어떤 데이터를 반환하느냐에 따라 SpringMVC가 그에 맞게 응답데이터를 처리하기 때문이다. 

 

 

서버에서 동적으로 화면을 생성하는 경우 ( 뷰템플릿 방식 ) ( ex. JSP, Thymleaf )

 

뷰템플릿 동작방식에서 Controller는 두 가지 데이터를 전달한다. 

 

1) 화면에 그릴 데이터

2) 뷰의 상대경로 ( ex. jsp 파일 )

 

두 가지 데이터를 전달하는데는 3가지 방법이 있다. 

 

1) ModelAndView

    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1(){
        ModelAndView mav = new ModelAndView("response/hello")
                .addObject("data","hello");
        return mav;
    }

 

ModelAndView 객체에 화면에 그릴 데이터와 뷰의 상대경로를 넣어주고 반환한다. 뷰의 상대경로는 뷰리졸버를 거치며 절대경로로 바뀐다. 화면에 그릴 데이터는 서블릿에서 관리하는 Request 객체에 담겨 View 영역으로 넘어간다. 

 

2) String

    @RequestMapping("/response-view-v2")
    public String responseViewV2(Model model){
        model.addAttribute("data","hello!");
        return "response/hello";
    }

 

파라미터로 Model 객체를 받는다. Model 객체는 화면에 그릴 데이터를 담는 그릇이다. 화면에 그릴 데이터는 Model 객체에 넣어주고 뷰의 상대경로는 String으로 반환한다. 

 

3) void 

    @RequestMapping("/response/hello") 
    public void responseViewV3(Model model){
        model.addAttribute("data","hello!");
    }

 

화면에 그릴 데이터는 파라미터로 받은 Model 객체에 넣어준다. 그리고 뷰상대경로를 void로 반환한다. void를 반환하면 SpringMVC는 @RequestMapping에 설정된 경로를 뷰의 상대경로로 인식한다. 

 

그런데 이는 잘 사용되지 않는다. @RequestMapping에 설정된 경로는 요청경로이다. 요청경로를 응답경로와 동일하게 설정하는 것은 인지적 혼란을 일으킬 수 있다. 그저 SpringMVC가 관례적으로 허용하는 것이니 이런게 있다는 정도만 알면 된다. 

 

 

 

 

클라이언트에서 동적으로 화면을 생성하는 경우 ( HTTP 메시지 방식 ) ( ex. React, vue.js )

 

 

 

 

HTTP는 크게 Header 영역과 Body 영역이 있다. HTTP가 요청으로 들어오면 Header 영역과 Body 영역에 있는 데이터를 가져와 비즈니스 로직을 처리한다. 비즈니스 로직이 처리되면, Body 영역에는 데이터를, Header 영역에는 데이터의 메타데이터를 담아 Client에게 전달한다.  뷰템플릿 방식도 HTTP Body에 HTML을 담아 전달하는 방식이다. HTML 동적생성이 워낙 복잡하다보니 중간에 뷰템플릿 같은 미들웨어를 두어 처리하는 것뿐이다.  

 

JSON이나 단순텍스트는 HTML만큼 복잡하지 않아 SpringMVC가 Body영역에 데이터를 바로 넣어줄 수 있다. 

 

 

HttpServletResponse

    @GetMapping("/response-body-string-v1")
    public void responseBodyV1(HttpServletResponse response) throws IOException {
        response.getWriter().write("ok");
    }

 

가장 원시적인 형태이다. HTTP Body 영역에 접근 가능한 Response 객체에다가 전달하고 싶은 데이터를 입력하는 것이다. 이는 HTTP와 가장 밀접한 객체를 호출하여 처리하는 방법이다. Controller는 비즈니스 로직을 처리하는 클래스로 통신과 관련된 객체와 직접 의존하는 것은 좋지 못하다. 그러므로 통신은 Spring에게 맡기고 Controller는 전달할 데이터만 반환하면 된다.

 

HttpEntity

 

- 단순 텍스트

    @GetMapping("/response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }

 

HttpEntity는 자식클래스로 RequestEntity와 ResponseEntity를 갖는다. RequestEntity는 요청으로 들어온 HTTP Body 영역의 데이터를 제네릭으로 명시된 타입으로 변환하여 가져오는 클래스이다. ResponseEntity는 제네릭에 명시된 타입의 응답 데이터를 HttpBody 영역에 전달하는 클래스이다.

 

- JSON

    @GetMapping("/response-body-json-v1")
    public ResponseEntity<HelloData> responseBodyJsonV1(){
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);

        return new ResponseEntity<>(helloData,HttpStatus.OK);
    }

 

제네릭으로 명시된 데이터가 클래스 타입이라면  SpringMVC는 이를 JSON 데이터로 변환하여 HTTP Body 영역에 전달한다. 그러므로 단순 텍스트가 아닌 JSON 데이터를 클라이언트로 넘기고 싶다면 ResponseEntity 객체에  특정한 객체를 넘겨야 한다. JAVA객체를 JSON 데이터로 변환하는 과정은 HTTP 메시지 컨버터가 담당한다. 이는 Spring 영역에서 얼어나는 과정이니 Controller는 그저 객체만 반환하면 된다.  HTTP 메시지 컨버터에 대해서는 나중에 따로 다루어 보겠다. 

 

 

@ResponseBody

 

- 단순데이터

    @ResponseBody
    @GetMapping("/response-body-string-v3")
    public String responseBodyJsonV3(){
        return "ok";
    }

 

@ResponseBody는 반환타입이 String 같은 단순타입이 가능하여 효과적이다. SpringMVC는 @ResponseBody으로 선언된 메소드에서 반환된 데이터는 HttpBody 영역으로 전달한다. @ResponseBody가 없다면 SpringMVC는 반환된 데이터를 뷰리졸버로 보낼 상대경로로 인식하니, 반드시 @ResponseBody가 선언되어 있어야 한다. 

 

- JSON

    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @GetMapping("/response-body-json-v2")
    public HelloData responseBodyJsonV2(){
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);
        return helloData;
    }

 

@ResponseBody로 선언된 메소드에서 객체를 반환하면 SpringMVC는 메시지 컨버터를 동작시켜 객체를 변환시켜 HTTP Body 영역에 전달한다. JSON도 이런 과정으로 JAVA객체가 JSON 데이터로 변환되어 전달된다. 

 

 

@ResponseBody는 가장 단순하여 실무에서 자주 사용되는 방식이다. 

 

@Controller
public class ResponseBodyController {

    @ResponseBody
    @GetMapping("/response-body-string")
    public String responseBodyJsonV3(){
        return "ok";
    }

    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @GetMapping("/response-body-json") 
    public HelloData responseBodyJsonV2(){
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);
        return helloData;
    }

}

 

@ResponseBody를 모든 메소드에 선언하는 과정을 줄이는 방법이 있다.  @Controller가 아닌 @RestController를 선언하는 것이다. @RestController는 모든 메소드에 @ResponseBody 효과를 준다.

 

@RestController
public class ResponseBodyController {

    @GetMapping("/response-body-string")
    public String responseBodyJsonV3(){
        return "ok";
    }

    @ResponseStatus(HttpStatus.OK)
    @GetMapping("/response-body-json") 
    public HelloData responseBodyJsonV2(){
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);
        return helloData;
    }

}

 

@RestController를 선언하면 메소드마다 @ResponseBody를 선언할 필요가 사라진다. 반환된 데이터는 뷰리졸버의 상대경로가 아닌 HTTP Body 영역으로 전달되는 데이터로 인식된다. 

 

 

 


 

 

참고자료

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원

www.inflearn.com

 

반응형