Web/Spring

[Inflearn] 스프링 MVC (6) HTTP 응답

UL :) 2022. 1. 31. 17:11

김영한 강사님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의 정리

2022.01.31~02.01 진행

강의를 듣고 개인적으로 정리한 글입니다. 코드와 그림 출처는 김영한 강사님께 있습니다. 문제 있을 시 알려주세요.

HTTP 응답

HTTP에서 응답 데이터를 만드는 방식은 크게 3가지 이다.

  1. 정적 리소스
  2. 뷰 템플릿 사용
  3. HTTP 메시지 사용

1. 정적 리소스 응답 - HTML

  • 정적 리소스 응답이란, 해당 파일을 변경 없이 그대로 서비스하는 것을 말한다.
    • 웹 브라우저에 정적인 HTML, CSS, JS를 제공할 때는 정적 리소스를 사용한다.
  • 스프링 부트에서 src/main/resources는 클래스 패스의 시작 경로로, 리소스를 보관하는 곳이다.
  • 스프링 부트는 클래스패스의 다음 디렉토리에 있는 정적 리소스를 제공한다.
    • 정적 리소스 경로: src/main/resources/static
  • /static, /public, /META-INF/resources

 

2. 동적 리소스 응답 - 뷰 템플릿

  • 웹 브라우저에 동적인 HTML을 제공할 때는 뷰 템플릿을 사용한다.
  • 뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다.
  • 스프링 부트가 제공하는 기본 뷰 템플릿 경로: src/main/resources/templates

Thymeleaf 를 사용해서 적용해보자

 

Thymeleaf 스프링 부트 설정

build.gradle에 org.springframework.boot:spring-boot-starter-thymeleaf 라이브러리가 추가되어 있으면

스프링 부트는 자동으로 ThymeleafViewResolver와 필요한 스프링 빈들을 등록한다.

또한 application.properties에는 다음 기본 값이 설정되어 있다. (필요할 때에만 변경)

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

 

반환타입에 따라 달라지는 응답- View or HTTP 메시지

  1. ModelAndView를 반환하는 경우
    • 해당 객체에 등록된 경로로 뷰 리졸버가 실행되어서 뷰를 찾고 랜더링 한다.
  2. String으로 반환하는 경우
    • @ResponseBody가 없는 경우 response/hello로 뷰 리졸버가 실행되어서 뷰를 찾고 랜더링 한다
    • @ResponseBody가 있는 경우 뷰 리졸버를 실행하지 않고, HTTP 메시지 바디에 직접 response/hello라는 문자가 입력된다.

void를 반환하는 경우는 권장되지 않으므로 생략하였다.

 

@Controller
public class ResponseViewController {

    /** ModelAndView 반환 */
    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1(){
        ModelAndView mav = new ModelAndView("response/hello")
                .addObject("data","hello!");

        return mav;
    }

   /** 권장, String 반환 */
    @RequestMapping("/response-view-v2")
    public String responseView2(Model model){
        model.addAttribute("date", "hello!");
        return "response/hello";
    }

    /** 권장X void 반환 */
    @RequestMapping("/response/hello")
    public void responseView3(Model model){
        model.addAttribute("date", "hello!");
    }
}

 

3. HTTP 메시지 바디 - 데이터 직접 전달하기

  • HTTP API를 제공하는 경우
    • 정적 리소스나 뷰 템플릿을 거치지 않고 직접 HTTP 응답 메시지로 데이터를 전달 해야한다.
    • HTTP 메시지 바디JSON 같은 형식으로 데이터를 실어보낸다.

 

3.1. 문자처리

HTTP 응답은 앞선 포스팅에서도 다루었으므로 예제 코드로만 정리하겠다.

@Slf4j
@Controller
public class ResponseBodyController {

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

    /** HttpEntity를 상속받은 ResponseEntity 사용
     *  HTTP 응답 코드 동적으로 설정 가능 */
    @GetMapping("/response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() throws IOException {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }

    /** @ResponseBody 사용*/
    @ResponseBody
    @GetMapping("/response-body-string-v3")
    public String responseBodyV3() {
        return "ok";
    }

 

3.2. JSON 처리

    /** HttpEntity를 상속받은 ResponseEntity 사용
    *  HTTP 응답 코드 동적으로 설정 가능 */
    @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);
    }

@ResponseStatus 애노테이션을 사용하면 다음과 같이 응답 코드를 설정할 수 있지만, 동적인 변경은 불가능하다.

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

프로그램 조건에 따라 동적으로 응답 코드를 변경하려면 ResponseEntity를 사용해야 한다.

 

3.3. @RestController

@ResponseBody를 클래스 단위에 두면 전체 메서드에 적용이 된다. 이 기능과 @Controller 적용을 합친 것이 @RestController이다.

@RestController를 적용한 전체 코드는 다음과 같다.

@Slf4j
//@Controller
@RestController /** @Controller + @ResponseBody 효과 */
public class ResponseBodyController {

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

    /** HttpEntity를 상속받은 ResponseEntity 사용
     *  HTTP 응답 코드 동적으로 설정 가능 */
    @GetMapping("/response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() throws IOException {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }

    /** @ResponseBody 사용*/
//    @ResponseBody
    @GetMapping("/response-body-string-v3")
    public String responseBodyV3() {
        return "ok";
    }

    /**
     * 2. 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);
    }


    /** 실무에서 이 방식 많이 사용 */
    @ResponseStatus(HttpStatus.OK)
//    @ResponseBody
    @GetMapping("/response-body-json-v2")
    public HelloData responseBodyJsonV2(){
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);
        return helloData;
    }

}