내배캠/Spring

[내일배움캠프/백엔드] 기초 Spring 5주차 강의

jy3574 2024. 11. 11. 23:05

<목차>

1. HTTP 요청 데이터

-Client에서 Server로 Data를 전달하는 방법

 

2. Spring 요청 데이터

-@RequestParam

-@ModelAttribute

-HTTP Message Body(요청)

-TEXT

-HttpEntity

-@RequestBody, @ResponseBody

-JSON

-HTTPMessageConverter

 

3. Spring 응답 데이터

-Server에서 Client로 Data를 전달하는 방법

-정적 리소스

-View Template

-HTTP Message Body(응답)

 

4. CRUD 실습


Client에서 Server로 Data를 전달하는 방법

-크게 세가지 방법으로 나뉜다.

1. Query Parameter

2. HTTP Form Data

3. HTTP Request Body

 

1. GET + Query Parameter(=Query String)

-URL의 쿼리 파라미터를 사용하여 데이터를 전달하는 방법

http://localhost:8080/request-params?key1=value&key2=value2

 

-HttpServletRequest 사용

@Slf4j
@Controller
public class RequestParamController {

    @GetMapping("/request-params")
    public void params(
            HttpServletRequest request,
HttpServletResponse response
 ) throws IOException {
 
        String key1Value = request.getParameter("key1");
String key2Value = request.getParameter("key2");

log.info("key1Value={}, key2Value={}", key1Value, key2Value);
        response.getWriter().write("success");
    }
}

 

-response.getWriter().write()

  • HttpServletResponse를 사용해서 응답값을 직접 다룰 수 있다.
  • @Controller 지만 @ResponseBody를 함께 사용한 것과 같다.

-postman 요청

-Log 출력결과

 

2. POST + HTML Form(x-www-form-urlencoded)

-HTTP Request Body에 쿼리 파라미터 형태로 전달하는 방법

 

-HTTP Request

POST /form-data
content-type: application/x-www-form-urlencoded

key1=value1&key2=value2

 

-HttpServletRequest 사용

@Slf4j
@Controller
public class RequestBodyController {

    @PostMapping("/form-data")
    public void requestBody(
            HttpServletRequest request,
HttpServletResponse response
 ) throws IOException {
 
        String key1Value = request.getParameter("key1");
String key2Value = request.getParameter("key2");

log.info("key1Value={}, key2Value={}", key1Value, key2Value);
        response.getWriter().write("success");
    }
}

 

-postman

 

-Log 출력

 

+ HttpServletRequest.getParameter("key");를 사용하면

Query Parameter, HTML Form Data 두가지 경우 모두 데이터 형식(key = value)가 같기 때문에 해당 값에 접근할 수 있다.

 

3. HTTP Request Body

-데이터(JSON, TEXT, XML 등)를 직접 HTTP Message Body에 담아서 전달한다.

-주로 @RestController에서 사용하며, 대부분 JSON 형식으로 데이터를 전달한다.

  • POST, PUT, PATCH Method에서 사용한다.
  • GET, DELETE Method는 Body에 데이터를 담는 것을 권장하지 않는다.

-HttpServletRequest 사용

@Getter
@Setter
public class Board {

private String title;
private String content;

}
package com.example.springbasicannotation.controller;

import cohttp://m.example.springbasicannotation.entity.Board;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PostMapping;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Slf4j
@Controller
public class RequestBodyController {

    // JSON을 객체로 변환해주는 Jackson 라이브러리
    private ObjectMapper objectMapper = new ObjectMapper();

    @PostMapping("/request-body")
    public void requestBody(
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {

        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBody={}", messageBody);

        Board board = objectMapper.readValue(messageBody, Board.clss);

        log.info("board.getTitle()={}, board.getContent()={}", board.getTitle(), board.getContent());

        response.getWriter().write("success");

    }
    
}

 

-Postman

-출력결과

 

+JSON을 Java 객체로 변환하려면 Jackson과 같은 라이브러리를 사용해야 한다.

-Spring Boot는 기본적으로 Jackson 라이브러리의 ObjectMapper를 제공하며, starter-web에 포함되어 있다.

 

 

@RequestParam

-URL에서 파라미터 값과 이름을 함께 전달하는 방식으로 주로 HTTP 통신 Method 중 GET 방식의 통신을 할 때 많이 사용한다.

-@Requestparam을 사용하면 요청 파라미터 값에 아주 쉽고 간편하게 접근(Parameter Binding)할 수 있다.

 

-? 뒤에 오는 URL

  1. Query String
  2. Query Parameter
  3. Request Param

-예시코드

@Slf4j
@Controller
public class RequestParamControllerV2 {

@ResponseBody
@GetMapping("/v1/request-param")
public String requestParamV1 (
@RequestParam("name") String userName,
@RequestParam("age") int userAge
) {
// logic
log.info("name={}", userName);
    log.info("age={}", userAge);
return "success";
}

}

 

1. @Controller + @ResponseBody

-View를 찾는 것이 아니라 ResponseBody에 응답을 작성한다.(=@RestController)

 

2. @RequestParam

-파라미터 이름으로 바인딩한다.

 

3. @RequestParam("속성값")

-속성값이 파라미터 이름으로 매핑된다.

 

-Postman

-출력결과

 

-"속성값"과 변수명이 같으면 생략이 가능하다

ex. @RequestParam("name") String name

// GET http://localhost:8080/v2/request-param?name=sparta&age=100
@ResponseBody
@GetMapping("/v2/request-param")
public String requestParamV2 (
@RequestParam String name,
@RequestParam int age
) {
// logic
log.info("name={}", name);
  log.info("age={}", age);
return "success";
}

 

*@RequestParam 사용법

1. 어노테이션, 속성값 모두 생략

-@RequestParam은 생략이 가능하다.

// GET http://localhost:8080/v3/request-param?name=sparta&age=100
@ResponseBody
@GetMapping("/v3/request-param")
public String requestParamV3 (
String name,
int age
) {
// logic
log.info("name={}", name);
  log.info("age={}", age);
return "success";
}

 

-생략하면 @RequestParam(required=false) 필수 여부 속성이 default로 설정된다.

-단, 요청 파라미터와 이름이 완전히 같아야 한다.

-단순 타입(int, String, Integer 등)이어야 한다.

 

+위의 방식은 권장하지 않는다.

-명시적으로 표시되어 있지 않으면 팀의 협의가 있지 않는 경우 다른 개발자들에게 혼동을 주게 된다.

-최소 @RequestParam String name 속성값 생략 형태를 쓰면된다.

 

2. required 속성 설정

-파라미터의 필수 값을 설정한다.

-API 스펙을 규정할 때 사용한다.

@ResponseBody
@GetMapping("/v4/request-param")
public String requestParam (
@RequestParam(required = true) String name, // 필수
@RequestParam(required = false) int age // 필수가 아님
) {
// logic
log.info("name={}", name);
  log.info("age={}", age);
return "success";
}

 

-@RequestParam을 사용하면 기본 Default 값은 True 이다.

-True로 설정된 파라미터 값이 요청에 존재하지 않으면 400 BadRequest(클라이언트 측 에러)

 

-Exception이 발생하지 않는 경우

ex) http://localhost:8080/v4/request-param?name=sparta&age=100

 

-Exception이 발생하는 경우

ex) http://localhost:8080/v4/request-param?age=100

ex) http://localhost:8080/v4/request-param

 

-required = false 설정이 되어 있으면 해당 파라미터는 없어도 된다.

주의) http://localhost:8080/v4/request-param?name=sparta 요청한다면?

-500 Error가 발생한다.

-int Type에는 null을 넣을 수 없다. 0이라도 들어가야 한다.

--따라서 보통 null을 허용하는 Integer로 사용하거나 default 옵션을 사용한다.

@ResponseBody
@GetMapping("/v4/request-param")
public String requestParam (
@RequestParam(required = true) String name, // 필수
  @RequestParam(required = false) Integer age
) {
// logic
log.info("name={}", name);
  log.info("age={}", age);
return "success";
}

 

-파라미터 Key 값만 있고 Value가 없는 경우

http://localhost:8080/request-param?name=

-null과 빈 문자열 ""은 다르다.

--위 형태는 빈 문자열 ""로 인식한다, True지만 통과가 되어 버린다. 주의!

 

3. default 속성 적용

-파라미터의 기본 값을 설정한다.

@ResponseBody
@GetMapping("/v5/request-param")
public String requestParam (
@RequestParam(required = true, defaultValue = "sparta") String name,
    @RequestParam(required = false, defaultValue = "1") int age
) {
// logic
log.info("name={}", name);
  log.info("age={}", age);
return "success"
}

 

-name Parameter의 값이 없으면 기본적으로 "sparta"로 설정한다.

ex) http://localhost:8080/v5/request-param?age=100

 

-age Parameter의 값이 없으면 기본적으로 1으로 설정한다.

ex) http://localhost:8080/v5/request-param?name=jy

ex) http://localhost:8080/v5/request-param

-주의) defaultValue 속성을 설정하게 되면 "" 빈 문자열의 경우에도 기본 값이 설정된다.

 

ex)http://localhost:8080/v5/request-param?name&age

 

4. Map 사용

-Parameter를 Map 형태로 조회가 가능하다.

@ResponseBody
@GetMapping("/v6/request-param")
public String requestParamV6(
        @RequestParam Map<String, String> map
) {
    // logic
    log.info("name={}", map.get("name"));
    log.info("age={}", map.get("age"));

    return "success";
}

 

-Map 형태(key=value)로 조회가 가능하다.

ex)http://localhost:8080/v6/request-param?name=sparta&age=100

 

-MultiValueMap 형태(key=[value1, value2])로 조회가 가능하다.

@ResponseBody
@GetMapping("/v6/request-param")
public String requestParamV6(
        @RequestParam MultiValueMap<String, String> map
) {
    // logic
    log.info("name={}", map.get("name"));
    log.info("age={}", map.get("age"));

    return "success";
}

 

ex) http://localhost:8080/v6/request-param?name=sparta&name=jy&name=student&age=100

 

-파라미터 Map의 Value가 1개인 경우에는 Map / 여러개인 경우 MultiValueMap을 사용한다.

-대부분의 파라미터 값은 한개만 존재한다.

 

 

@ModelAttribute

-요청 파라미터를 받아 필요한 Object로 바인딩 해준다.

-주로 HTML 폼에서 전송된 데이터를 바인딩하고 HTTP Method POST인 경우 사용된다.

 

1. 기존코드

-@Data는 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructer를 자동으로 설정해주는 역할을 한다.

 

ex) http://localhost:8080/v1/tutor + x-www-form-urlencoded

@Data
public class Tutor {

private String name;
private int age;

}

@Controller
public class ModelAttributeController {

@ResponseBody
  @PostMapping("/v1/tutor")
  public String requestParamV1(
          @RequestParam String name,
          @RequestParam int age
  ) {
      Tutor tutor = new Tutor();
      tutor.setName(name);
      tutor.setAge(age);

      return "tutor name = " + name + " age = " + age;
  }

}

 

-@RequestParam의 Mapping을 사용하게 되면 위와 같은 객체를 생성하는 코드가 포함된다.

-@ModelAttribute는 해당 과정을 자동화한다.

 

-Postman

POST /v1/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100

 

2. @ModelAttribute 적용

ex) http://localhost:8080/v2/tutor + x-www-form-urlencoded

@ResponseBody
@PostMapping("/v2/tutor")
public String modelAttributeV2(
@ModelAttribute Tutor tutor
) {

String name = tutor.getName();
int age = tutor.getAge();

return "tutor name = " + name + " age = " + age;
}

 

-Postman

POST /v2/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100

 

*@ModelAttribute 동작 순서

  1. 파라미터에 @ModelAttribute가 있으면 파라미터인 Tutor 객체를 생성한다.
  2. 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩한다.
    1. 파라미터 이름이 name이면 setName(value); 메서드를 호출한다.
    2. 파라미터 이름과 필드 이름이 반드시 같아야 한다.

*@Data의 Setter가 없다면?

@Getter
public class Tutor {

private String name;
private int age;

}

-객체 필드에 값이 set 되지 않는다.

 

*파라미터의 타입이 다른 경우

-만약 요청 파라미터 age에 int가 아닌 String이 전달된다면?

ex)http://localhost:8080/v2/tutor + x-www-form-urlencoded

POST /v2/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=nbcamp

-BindException 발생

-이런 경우 때문에 Validation(검증)이 필요하다.

 

3. @ModelAttribute 생략

-@ModelAttribute와 @RequestParam은 모두 생략이 가능하다.

ex) http://localhost:8080/v3/tutor + x-www-form-urlencoded

POST /v3/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100
@ResponseBody
@PostMapping("/v3/tutor")
public String modelAttributeV3(Tutor tutor) {

String name = tutor.getName();
int age = tutor.getAge();

return "tutor name = " + name + " age = " + age;
}

 

-Spring에서 @RequestParam 이나 @ModelAttribute가 생략되면 String, int, Integer와 같은 기본 타입은 @RequestParam과 Mapping한다.

@ResponseBody
@PostMapping("/v4/tutor")
public String requestParamV2(
        String name,
        int age
) {
    return "tutor name = " + name + " age = " + age;
}

 

-나머지 경우들(객체)은 모두 @ModelAttribute와 Mapping 한다.

 

 

HTTP Message Body(요청)

-@RequestParam, @ModelAttribute는 Get + Query Parameter와 Post HTML Form Data를 바인딩 하는 방법이다.

-아래 방법들은 HTTP Message Body에 직접적으로 Data가 전달 되는 경우이다. = Requst Body의 Data를 바인딩 하는 방법.

 

-REST API에서 주로 사용하는 방식이다.

-HTTP Method POST, PUT, PATCH에서 주로 사용한다.

-GET은 Request Body가 존재할 수는 있지만 권장하지 않는다.

-JSON, XML, TEXT 등을 데이터 형식으로 사용한다.

 

*HTTP Message 구조

 

*HTTP Request, Response 예시

-Server에서 Request로 전달받은 Data를 처리하기 위해서 바인딩 해야한다.

ex) JSON -> Object

 

 

TEXT

-HTTP Request Body 에 Data가 전송되는 경우 HttpMessageConverter를 통해 바인딩 된다.

-요즘은 RestfulAPI를 주로 사용하고 있어서 대부분의 경우 JSON 형식으로 통신한다.

 

1. HttpServletRequest 예시

-request.getInputStream();

@Slf4j
@Controller
public class RequestBodyStringController {

@PostMapping("/v1/request-body-text")
  public void requestBodyTextV1(
          HttpServletRequest request,
          HttpServletResponse response
  ) throws IOException {

      ServletInputStream inputStream = request.getInputStream();
      String bodyText = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
      
      response.getWriter().write("response = " + bodyText);
  }
}

 

*Postman

-Request → Body → raw → Text

 

*Request Header Content-Type : text/plain

 

2. I/O 예시

-InputStream(읽기) 파라미터 지원

  • HTTP Request Body Data 직접 조회

-OutputStream(쓰기) 파라미터 지원

  • HTTP Response Body 직접 결과 출력
@PostMapping("/v2/request-body-text")
public void requestBodyTextV2(
InputStream inputStream,
Writer responseWriter
) throws IOException { 

String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

responseWriter.write("response = " + bodyText);
}

-Postman

 

3. HttpEntity 예시

-HttpMessageConverter 사용

-HttpEntity를 사용하면 HttpMessageConverter를 사용한다.

@PostMapping("/v3/request-body-text")
public HttpEntity<String> requestBodyTextV3(HttpEntity<String> httpEntity) { 

// HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
String body = httpEntity.getBody();

return new HttpEntity<>("response = " + body); // 매개변수 = Body Message

}

 

-Postman

 

-Spring의 HttpMessageConverter 덕분에 간편하게 Request Data에 접근할 수 있다.

  • HttpEntity를 사용하면 HttpMessageConverter가 동작하여 자동으로 매핑된다.
  • 요청 뿐만이 아닌 응답까지 HttpEntity 하나만으로 사용이 가능해진다.

-Converter 는 어떤 무언가를 다른 무언가로 바꿔주는(convert) 장치를 말한다.

 

 

HttpEntity

-HttpEntity : HTTP Header, Body 정보를 편리하게 조회할 수 있도록 만들어준다.

 

*HttpEntity 역할

  1. Http Request Body Message를 직접 조회한다.
  2. Request 뿐만 아니라 Response도 사용할 수 있도록 만들어준다.
  3. Response Header 또한 사용할 수 있다.
  4. Request Parameter를 조회하는 기능들과는 아무 관계가 없다.
  5. View를 반환하지 않는다.

*HttpEntity를 상속 받은 객체

-RequestEntity<>

  • HTTP Request Method, URL 정보가 추가 되어있다.

-ResponseEntity<>

  • HTTP Response 상태 코드 설정이 가능하다.

-코드예시

@Controller
public class RequestBodyStringController {

@PostMapping("/v4/request-body-text")
  public HttpEntity<String> requestBodyTextV4(RequestEntity<String> httpEntity) {

      // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
      String body = httpEntity.getBody();
      // url, method 사용 가능

      return new ResponseEntity<>("response = " + body, HttpStatus.CREATED); // Body Data, 상태코드
  }

}

 

-Postman

 

-위 방법을 적용해도 불편하다면 Data를 HttpEntity에서 꺼내서 사용해야한다.

-Spring은 Http RequestBody Message를 읽어서 String이나 Object로 자동으로 변환해준다. (-> HttpMessageConverter 사용)

 

 

@RequestBody, @ResponseBody

-Spring에서 @RequestBody, @ResponseBody 어노테이션을 사용하면 각각 Request, Response 객체의 Body에 편하게 접근하여 사용할 수 있다.

 

-코드 예시

@Controller // @RestController = @Controller + @ResponseBody
public class RequestBodyStringController {

  @ResponseBody
  @PostMapping("/v5/request-body-text")
  public String requestBodyTextV5(
          @RequestBody String body,
          @RequestHeader HttpHeaders headers
  ) {
      // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
      String bodyMessage = body;

      return "request header = " + headers + " response body = " + bodyMessage;
  }
}

 

-Postman

 

  • @RequestBody
    • 요청 메세지 Body Data를 쉽게 조회할 수 있다.
  • @RequestHeader
    • 요청 헤더 정보 조회
  • @ResponseBody
    • 응답 메세지 바디에 값을 쉽게 담아서 전달할 수 있도록 해준다.
    • View가 아닌 데이터를 반환한다.

*요약정리

  1. 요청 파라미터, HTML Form Data에 접근하는 경우
    • @RequestParam, @ModelAttribute를 사용한다.
  2. Http Message Body에 접근하는 경우
    • @RequestBody를 사용한다.(JSON, XML, TEXT)

 

JSON

-JSON은 @RestController에서 가장 많이 사용되는 데이터 형식이다.

-현재 대부분의 API는 Request, Response 모두 JSON 형태로 통신한다.

-JSON 형태로 Data를 전송할 때는 Request Header의 content-type이 꼭 application/json이어야 한다.

 

1. HttpServletRequest 사용

@Data
public class Tutor {
private String name;
private int age;
}

@RestController
public class JsonController {

private ObjectMapper objectMapper = new ObjectMapper();

@PostMapping("/v1/request-body-json")
public void requestBodyJsonV1(
HttpServletRequest request, 
HttpServletResponse response
) throws IOException {

// request body message를 Read
ServletInputStream inputStream = request.getInputStream();
// UTF-8 형식의 String으로 변환한다.
String requestBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

// String requestBody를 ObjectMapper를 사용하여 변환 "{\"name\":\"wonuk\", \"age\":10}"
Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);

// 응답
response.getWriter().write("tutor" + tutor);
}
}

*Postman

- Content-Type 헤더 확인 : application/json

-HttpServletRequest를 사용하여 HTTP Message Body 데이터를 Read 하여 문자로 변환한다.

-문자로 만들어진 JSON을 jackson 라이브러리의 objectMapper를 사용하여 Object로 변환

 

2. @RequestBody 사용

@RestController
public class JsonController {

private ObjectMapper objectMapper = new ObjectMapper();

@PostMapping("/v2/request-body-json")
  public String requesBodytJsonV2(@RequestBody String requestBody) throws IOException {

      Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);

      return "tutor.getName() = " + tutor.getName() + "tutor.getAge() = " + tutor.getAge();
  }
    
}

-Postman

-@RequestBody를 사용하여 HTTP Request Body의 Data에 접근한다.

 

3. ObjectMapper 제거

@RestController
public class JsonController {

@PostMapping("/v3/request-body-json")
public String requestBodyJsonV3(@RequestBody Tutor tutor) {

Tutor requestBodyTutor = tutor;

return "tutor = " + requestBodyTutor;
}
}

-Postman

-위 Controller가 동작하는 이유는?

 

*@RequestBody

-@RequestBody 어노테이션을 사용하면 Object를 Mapping 할 수 있다.

-HttpEntity<>, @RequstBody를 사용하면 HTTPMessageConverter가 Request Body의 Data를 개발자가 원하는 String이나 Object로 변환해준다.

-JSON to Object의 Mapping 또한 가능하다.

 

-MappingJackson2HttpMessageConverter의 역할

-쉽게 설명하면 HTTP Message Converter가 ObjectMapper를 대신 실행한다.

 

4. @RequestBody는 생략할 수 없다.

-@RequestParam, @ModelAttribute는 생략이 가능하다.

@Slf4j
@RestController
public class JsonController {

@PostMapping("/v4/request-body-json")
  public String requestBodyJsonV4(Tutor tutor) { // @RequestBody 생략시 @ModelAttribute가 된다.

      Tutor requestBodyTutor = tutor;

      return "tutor.getName() = " + requestBodyTutor.getName() + " tutor.getAge() = " + requestBodyTutor.getAge();
  }

}

 

-Postman

 

-생략하면 @ModelAttribute가 된다.

  • 요청 파라미터를 처리하도록 설정된다.

-Request Header의 contentType은 꼭 application/json이여야 한다.

  • 위 설정 정보를 기반으로 MessageConverter가 실행된다.

 

5. HttpEntity 사용

@RestController
public class JsonController {

@PostMapping("/v5/request-body-json")
  public String requestBodyJsonV5(
          HttpEntity<Tutor> httpEntity
  ) {
      // 값을 꺼내서 사용해야한다!
      Tutor tutor = httpEntity.getBody();

      return "tutor.getName() = " + tutor.getName() + " tutor.getAge() = " + tutor.getAge();
  }

}

-Postman

-HttpEntity<Tutor>

  • Generic Type으로 Tutor가 지정되어 있기 때문에 해당 Class로 반환된다.

 

6. @ResponseBody

@Controller
public class JsonController {

@ResponseBody // @RestController = @Controller + @ResponseBody
@PostMapping("/v6/request-body-json")
    public Tutor requestJson(@RequestBody Tutor tutor) {
        return tutor;
  }

}

 

-Postman

 

-View를 조회하지 않고 ResponseBody에 Data를 입력해서 직접 반환한다.

-요청 뿐만이 아니라 응답에도 HttpMessageConverter가 동작한다.

  • MappingJackson2HttpMessageConverter 적용
  • 응답 객체인 Tutor가 JSON으로 변환되어 반환된다.

-HttpEntity를 사용해도 된다.

 

*정리

  1. 요청 데이터는 @RequestBody를 사용해서 바인딩하면 된다.
  2. @RequestBody는 생략이 불가능하다.
    • @ModelAttribute가 적용되기 때문
  3. HttpMessageConverter가 요청 응답 데이터를 모두 변환할 수 있다.
    • JSON은 MappingJackson2HttpMessageConverter를 사용한다.
    • Request Header의 Content-Type은 application/json 이어야 한다.
      • Header로 어떤 Converter가 동작할지 판별한다.

 

HTTPMessageConverter

-Spring Framework에서 HTTP 요청과 응답을 변환하는 인터페이스이다.

-클라이언트와 서버 간에 데이터를 주고 받을 때, 요청 데이처를 자바 객체로 변환하거나 자바 객체를 응답 데이터로 변환하는 역할을 수행한다.

-MappingJackson2HttpMessageConverter 는 JSON을 처리하는 대표적인 HTTPMessageConverter의 구현체이다.

 

*HttpMessageConverter의 역할

-데이터를 Object로 변환한다.

-대표적으로 JSON을 변환한다.

-@RequestBody

  • 요청 데이터 + Request Header를 참고하여 Object로 변환한다.
  • HTTP Request Body(JSON Data) -> Converter(jackson) -> Object
  • Request Header -> Content-Type:application/json(전달할 데이터 형식)

-@ResponseBody

  • 응답 데이터 + Accept Header를 참고하여 원하는 데이터 형식으로 변환한다.
  • Object -> Converter(jackson) -> HTTP Response Body(JSON Data)
  • Request Header -> Accept: application/json(허용할 데이터 형식)

 

Server에서 Client로 Data를 전달하는 방법

1. 정적 리소스

2. View Template

3. HTTP Message Body

 

1. 정적 리소스

-정적인 HTML, CSS, JS, Image 등을 변경 없이 그대로 반환한다.

2. View Template

-SSR(Server Side Rendering)을 사용할 때 View가 반환된다.

3. HTTP Message Body

-응답 데이터를 직접 Message Body에 담아 반환한다.

 

 

정적 리소스

-웹 애플리케이션에서 변하지 않는 파일들을 의미

-HTML, CSS, JS, 이미지 파일들(JPG, PNG, GIF) 등이 정적 리소스에 해당한다.

 

*Spring Boot의 정적 리소스 경로

-이 경로들에 정적 리소스가 존재하면 서버에서 별도의 처리 없이 파일 그대로 반환된다.

  1. /static
  2. /public
  3. /META-INF/resources
  4. src/main/resources
    • /static

*Spring Boot Directory 구조

-src/main/resources/static/hello/world.html 디렉토리 구조라면 http://localhost:8080/hello/world.html URL로 리소스에 접근이 가능하다.

-/static 대신 /public 혹은 /META-INF/resources도 사용가능하다.

 

 

View Template

-Spring에서는 Thymeleaf, JSP와 같은 템플릿 엔진을 사용해 View Template을 작성할 수 있다.

-View Template은 서버에서 데이터를 받아 이를 HTML 구조에 맞게 삽입한 후 최종적으로 클라이언트에게 전송되는 HTML 문서로 변환하여 사용자에게 동적으로 생성된 웹 페이지를 제공한다.

 

*View Template

-View Template은 Model을 참고하여 HTML 등이 동적으로 만들어지고 Client에 응답된다.

-Spring Boot는 기본적으로 View Template 경로(src/main/resources/templates)를 설정한다.

-build.gradle에 Thymeleaf 의존성을 추가하면 ThymeleafViewResolver와 필요한 Spring Bean들이 자동으로 등록된다.

 

*기본 설정 예시

-아래 내용을 자동으로 Spring Boot가 등록해준다.

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

 

1. @Controller의 응답으로 String을 반환하는 경우

-@ResponseBody가 없으면 View Resolver가 실행되며 View를 찾고 Rendering 한다.

@Controller
public class ViewTemplateController {

@RequestMapping("/response-view")
  public String responseView(Model model) {
      // key, value
      model.addAttribute("data", "sparta");

      return "thymeleaf-view";
  }

}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1>Thymeleaf</h1>
    <h2 th:text="${data}"></h2>
</body>
</html>

ex) http://localhost:8080/response-view

-@ResponseBody가 있으면 HTTP Message Body에 return 문자열 값이 입력된다.

@Controller
public class ViewController {

@ResponseBody // @RestController = @Controller + @ResponseBody
@RequestMapping("/response-body")
public String responseBody() {
return "thymeleaf-view";
}

}

-postman

 

2. 반환 타입이 void인 경우

-잘 사용하지 않는다.

-@Controller + (@ResponseBody, HttpServletResponse, OutputStream)과 같은 HTTP Message Body를 처리하는 파라미터가 없으면 RequestMapping URL을 참고하여 View Name으로 사용한다.


@Controller
public class ViewTemplateController {

// thymeleaf-view.html 과 Mapping된다.
  @RequestMapping("/thymeleaf-view")
  public void responseViewV2(Model model) {
      model.addAttribute("data", "sparta");
  }

}

-예시와 같은 경우에는 viewTemplate(viewName)을 RequestMapping URL 주소로 찾는다.

 

 

HTTP Message Body(응답)

-REST API를 만드는 경우 Server에서 Client로 HTML을 전달하는 방식이 아닌 HTTP Message Body에 직접 Data를 JSON 형식으로 담아 전달한다.

-정적 HTML, View Template 또한 HTTP Message Body에 담겨서 전달된다.

-Response의 경우 정적 HTML, View Template을 거치지 않고 직접 HTTP Response Message를 만들어 전달하는 경우를 말한다.

 

*HTTP Message Body

1. HttpServletResponse 사용

@Controller
public class ResponseBodyController {

@GetMapping("/v1/response-body")
public void responseBodyV1(
HttpServletResponse response
) throws IOException {

response.getWriter().write("data");

}
}

 

-postman

-Response Body에 data라는 문자열이 입력되어 응답된다.

-기존 Servlet을 다룰 때 코드와 형태가 같다.

 

2. ResponseEntity<> 사용

@GetMapping("/v2/response-body")
public ResponseEntity<String> responseBodyV2() {

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

 

-postman

 

-Response Body에 data라는 문자열과 HttpStatus.OK에 해당하는 상태 코드를 반환한다.

-ResponseEntity는 HttpEntity를 상속받았다.

-HttpEntity는 HTTP Message의 Header, Body 모두 가지고 있다.

 

3. @ResponseBody(TEXT, JSON) 사용

@Data
@NoArgsConstructor // 기본 생성자
@AllArgsConstructor // 전체 필드를 인자로 가진 생성자
public class Tutor {

    private String name;
    private int age;

}
// TEXT 데이터 통신
@ResponseBody
@GetMapping("/v3/response-body-text")
public String responseBodyText() {

return "data"; // HTTP Message Body에 "data"
}

 

-postman

// JSON 데이터 통신
@ResponseBody
@GetMapping("/v3/response-body-json")
public Tutor responseBodyJson() {

Tutor tutor = new Tutor("wonuk", 100);

return tutor; // HTTP Message Body에 Tutor Object -> JSON
}

 

-postman

-View를 사용하는 것이 아닌 HTTP Message Converter를 통해 HTTP Message Body를 직접 입력할 수 있다.(=ResponseEntity)

 

-@ResponseStatus를 사용하여 상태 코드를 지정할 수 있다.

@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/v4/response-body")
public Tutor responseBodyV4() {

Tutor tutor = new Tutor("wonuk", 100);

return tutor;
}

 

-postman

-단, 응답코드를 조건에 따라서 동적으로 변경할 수는 없다.

ex) 1번의 경우 OK, 2번의 경우 Created

 

4. ResponseEntity<Object>(JSON)

@ResponseBody
@GetMapping("/v5/response-body")
public ResponseEntity<Tutor> responseBody() {

Tutor tutor = new Tutor("wonuk", 100);

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

 

-postman

 

-ResponseEntity<> 두번째 파라미터에 Enum을 사용하여 상태 코드를 바꿀 수 있다.

-HTTP Message Converter를 통하여 JSON 형태로 변환되어 반환된다.

-동적으로 응답 코드를 변경할 수 있다.

@ResponseBody
@GetMapping("/v5/response-body")
public ResponseEntity<Tutor> responseBody() {

Tutor tutor = new Tutor("wonuk", 100);

if (조건) {
return new ResponseEntity<>(tutor, HttpStatus.OK);
} else {
return new ResponseEntity<>(tutor, HttpStatus.BAD_REQUEST);
}

}

 

-HttpEntity를 상속 받았다.