내배캠 TIL 28일차

오병택·2025년 3월 27일

내배캠

목록 보기
50/73

학습 요약

Spring 숙련 강의

Spring 입문 5주차

숙련 강의 1주차를 듣고 있지만 밀린 TIL을 쓰도록 하겠습니다..

HTTP 요청 데이터

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

1. GET + Query Parameter(=Query String)

  • URL의 쿼리 파라미터를 사용하여 데이터 전달하는 방법
http://localhost:8080/request-params?key1=value1&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를 함께 사용한 것과 같음
  • log 출력결과
key1Value=value, key2Value=value2

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");
    }
}

HttpServletRequest.getParameter(”key”);를 사용하면 Query Parameter, HTML Form Data 두가지 경우 모두 데이터 형식(key=value)이 같기 때문에 해당값에 접근할 수 있음

  • Log 출력
key1=value1&key2=value2

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;

}
@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");

    }
}
  • HTTP Request
{
   "title": "value1",
   "content": "value2"
}
  • Log 출력
messageBody={
   "title": "value1",
   "content": "value2"
}
board.getTitle()=value1, board.getContent()=value2

JSON을 Java 객체로 변환하려면 Jackson과 같은 라이브러리를 사용해야 하고 Spring Boot에포함되어 있음


Spring 요청 데이터 1강

@RequestParam

  • URL에서 파라미터 값과 이름을 함께 전달하는 방식으로 주로 HTTP 통신 Method 중 GET 방식의 통신을 할 때 많이 사용
  • @Requestparam을 사용하면 요청 파라미터 값에 아주 쉽고 간편하게 접근(Parameter Binding)할 수 있음

startLine에서 ?뒤에 오는 URL을 Query String, Query Parameter, 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(”속성값”)
    • 속성값이 파라미터 이름으로 매핑
  • HTTP Request
localhost:8080/v2/request-param?name=sparta&age=100
  • Log 출력
name=sparta
age=100
  • 속성값과 변수명이 같으면 생략 가능

ex) @RequestParam("name") String name

@RequestParam 사용법

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

ex) String name

  • 생략하면 @RequestParam(required=false) 필수 여부 속성이 default로 설정
  • 단, 요청 파라미터와 이름이 완전히 같아야 함
  • 단순 타입(int, String, Integer 등)이어야 함

위의 방식은 권장x

  1. 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(클라이언트 측 에러)
  • /v4/request-param?name=sparta로 요청 값을 줘도 에러가 뜬다. 에러는 500번대(서버 측 에러). 왜냐하면 int에 널이 반환될 수 없어서 이다. 널을 반환시키려면 Integer 타입으로 변경해야 함.
  • /v4/request-param?name= 는 null이 아닌 빈 문자열 “” 로 인식해서 주의해야 함.
  1. 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”으로 설정하고 age Parameter의 값이 없으면 1로 설정
  • 빈 문자열로 받게 되어도 기본값으로 설정되므로 주의해야 함
  1. 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)로 조회가 가능
  • MultiValueMap 형태(key=[value1, value2])로도 조회가 가능

@ModelAttribute

  • 요청 파라미터를 받아 필요한 Object로 바인딩 해줌
  • 주로 HTML 폼에서 전송된 데이터를 바인딩하고 HTTP Method POST인 경우 사용

1. 기존 코드

@RequestParam 의 Mapping을 사용하게 되면 객체를 생성하는 코드가 포함된다.
ex)

@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;
  }

}

@ModelAttribute 는 해당 과정을 자동화

2. @ModelAttribute 적용

@ResponseBody
@PostMapping("/v2/tutor")
public String modelAttributeV2(
				@ModelAttribute Tutor tutor													
) {
		
	String name = tutor.getName();
	int age = tutor.getAge();

	return "tutor name = " + name + " age = " + age;
}
  • @ModelAttirubte 동작 순서
    1. 파라미터에 @ModelAttribute가 있으면 파라미터인 Tutor 객체를 생성
    2. 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩
      • 파라미터 이름이 name 이면 setName(value); 메서드를 호출
      • 파라미터 이름과 필드 이름이 반드시 같아야 함
      • @setter가 없다면 객체 필드에 set되지 않음
      • 파라미터의 다른 타입의 값이 들어오면 에러남

3. @ModelAttirubte 생략

생략 가능

  • Spring에서 @RequestParam이나 @ModelAttribute가 생략되면 기본 값은 @RequestParam으로 매핑하고, 아니면 @ModelAttribute로 매핑

Spring 요청 데이터 2강

HTTP Message Body(요청)

Request Body의 Data를 바인딩하는 방법

  • REST API에서 주로 사용하는 방식
  • HTTP Method POST, PUT, PATCH에서 주로 사용
    • GET은 Request Body가 존재할 수는 있지만 권장하지 않음
  • JSON, XML, TEXT 등을 데이터 형식으로 사용

TEXT

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

1. HttpServletRequest 예시

@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);

  }

}

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 bodytext = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
	
	responseWriter.write("response = " + bodyText);
}

3. HttpEntity 예시

  • 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
	
}
  • Spring의 HttpMessageConverter 덕분에 간편하게 Request Data에 접근할 수 있음
    1. HttpEntity를 사용하면 HttpMessageConverter가 동작하여 자동으로 매핑
    2. 요청 뿐만이 아닌 응답까지 HttpEntity 하나만으로 사용이 가능

Spring 요청 데이터 3강

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 상태 코드 설정이 가능

ex)

@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,상태코드
  }
}
  • Data를 httpEntity에서 꺼내어 사용해야 해서 불편함

@RequestBody, @ResponseBody

Spring에서 @RequestBody, @ResponseBody 어노테이션을 사용하면 각각 Request, Response 객체의 Body에 편하게 접근 가능
ex)

@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;
  }
}
  • @RequestBody
    • 요청 메세지 Body Data를 쉽게 조회
  • @RequestHeader
    • 요청 헤더 정보 조회

Spring 요청 데이터 4강

JSON

  • Json은 @RestController 에서 가장 많이 사용되는 데이터 형식
  • 현재 대부분의 API는 Request, Response 모두 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);
	}
}
  • 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();
  }
}
  • @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;
	}
}
  • @RequestBody 어노테이션을 사용하면 Object를 Mapping할 수 있음
  • HttpEntity<>, @RequestBody를 사용하면 HTTPMessageConverter가 Request Body의 Data를 개발자가 원하는 String이나 Object로 변환
  • JSON to Object의 Mapping 또한 가능

4. @RequestBody는 생략할 수 없음

  • 생략하면 @ModelAttribute가 됨

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();
  }
	
}
  • 제네릭 타입인 Tutor타입으로 반환

6. @ResponseBody

@Controller
public class JsonController {
	
	@ResponseBody
	@PostMapping("/v6/request-body-json")
    public Tutor requestJson(@RequestBody Tutor tutor) {
        return tutor;
  }
	
}
  • View를 조회하지 않고 Response Body에 Data를 입력해서 직접 반환
  • 요청 뿐만이 아니라 응답에도 HttpMessageConverter가 동작
    • MappingJackson2HttpMessageConverter 적용
    • 응답 객체인 Tutor가 JSON으로 변환되어 반환
  • HttpEntity를 사용해도 됨

HTTPMessageConverter

  • Spring Framework에서 HTTP 요청과 응답을 변환하는 인터페이스
  • 클라이언트와 서버 간에 데이터를 주고받을 때, 요청 데이터를 자바 객체로 변환하거나 자바 객체를 응답 본문으로 변환하는 역할을 수행

Spring 응답 데이터 1강

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

  1. 정적 리소스
    • 정적인 HTML, CSS, JS, Image 등을 변경 없이 그대로 반환
  2. View Template
    • SSR(Server Side Rendering)을 사용할 때 View가 반환
  3. HTTP Message Body
    • 응답 데이터를 직접 Message Body에 담아 반환

정적 리소스

  • 웹 애플리케이션에서 변하지 않는 파일들을 의미
  • HTML, CSS, JavaScript, 이미지 파일들(JPG, PNG, GIF) 등이 정적 리소스에 해당

Spring Boot의 정적 리소스 경로

  • 아래 경로들에 정적 리소스가 존재하면 서버에서 별도의 처리 없이 파일 그대로 반환
    1. /static
    2. /public
    3. /META-INF/resources
    4. src/main/resources
      • /static
  • src/main/resources/static/hello/world.html 디렉토리 구조라면
    • http://localhost:8080/hello/world.html URL로 리소스에 접근이 가능

View Template

  • Spring에서는 Thymeleaf, JSP와 같은 템플릿 엔진을 사용해 View Template을 작성할 수 있음
  • View Template은 서버에서 데이터를 받아 이를 HTML 구조에 맞게 삽입한 후, 최종적으로 클라이언트에게 전송되는 HTML 문서로 변환하여 사용자에게 동적으로 생성된 웹 페이지를 제공
  • View Template은 Model을 참고하여 HTML 등이 동적으로 만들어지고 Client에 응답
  • Spring Boot는 기본적으로 View Template 경로(src/main/resources/templates)를 설정
  • build.gradle에 Thymeleaf 의존성을 추가하면 ThymeleafViewResolver와 필요한 Spring Bean들이 자동으로 등록

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

  • @ResponseBody 가 없으면 View Resolver가 실행되며 View를 찾고 Rendering
java
@Controller
public class ViewTemplateController {
	
	@RequestMapping("/response-view")
  public String responseView(Model model) {
      // key, value
      model.addAttribute("data", "sparta");

      return "thymeleaf-view";
  }

}
html
<!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>
  • @ResponseBody 가 있으면 HTTP Message Body에 return 문자열 값이 입력

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");
  }
}

Spring 응답 데이터 2강

HTTP Message Body(응답)

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

1. HttpServletResponse 사용

@Controller
public class ResponseBodyController {
	
	@GetMapping("/v1/response-body")
	public void responseBodyV1( HttpServletResponse response) throws IOException {
		
		response.getWriter().write("data");
	}
 }
  • Response Body에 data 라는 문자열이 입력되어 응답

2. ResponseEntity<> 사용

@GetMapping("/v2/response-body")
public ResponseEntity<String> responseBodyV2() {
		
	return new ResponseEntity<>("data", HttpStatus.OK);
}
  • Response Body에 data라는 문자열과 HttpStatus.OK에 해당하는 상태 코드를 반환

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"
}
// JSON 데이터 통신
@ResponseBody
@GetMapping("/v3/response-body-json")
public Tutor responseBodyJson() {
		
	Tutor tutor = new Tutor("wonuk", 100);
		
	return tutor; // HTTP Message Body에 Tutor Object -> JSON
}
  • @ResponseStatus 를 사용하여 상태 코드를 지정할 수 있음
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/v4/response-body")
public Tutor responseBodyV4() {
		
	Tutor tutor = new Tutor("wonuk", 100);
		
	return tutor;
}
  • 응답 코드를 조건에 따라서 동적으로 변경할 수는 없음

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);
}
  • ResponseEntity<>두 번째 파라미터에 Enum을 사용하여 상태 코드를 바꿀 수 있음
  • HTTP Message Converter를 통하여 JSON 형태로 변환되어 반환
  • 동적으로 응답 코드를 변경할 수 있음

느낀 점

뭔가 너무 집중하고 생각해서 그런가 에너지 소모가 많이 되는 것 같다. 아무래도 처음 배우는 부분이라 더 놓치지 않고 하려다 보니 그런 것 같기도 하고 아니면 너무 많은 지식이 들어와서 그런 것 같기도 하고 잘 모르겠다. 주말에 복습을 제대로 한번 해야 할 것 같다. 🥕🥕

profile
걱정하지 말고 일단 해봐!

0개의 댓글