🎯 0. 역직렬화 & 직렬화란?
이론적인 직렬화, 역직렬화는 다음과 같다.
* 직렬화 : 메모리를 디스크에 저장하거나, 네트워크 통신에 사용하기 위한 형식으로 변환하는 것
* 역직렬화 : 디스크에 저장한 데이터를 읽거나, 네트워크 통신으로 받은 데이터를 메모리에 쓸 수 있도록 변환하는 것
일반적으로 스프링 HTTP 요청 / 응답 과정에서의 직렬화, 역직렬화를 생각해보면 다음과 같다.
* 직렬화 : 자바 객체를 Byte, CSV, JSON, XML 등 네트워크 통신을 위한 다양한 포맷으로 변환하는 것!
주로 자바 객체 -> JSON을 사용한다.
* 역직렬화 : 네트워크 통신으로 받은 Byte, CSV, JSON, XML 등 다양한 포맷을 자바 객체로 변환하는 것!
주로 JSON -> 자바 객체를 사용한다.
요약하면, 자바 객체 ↔ JSON 간에 변환을 하는 것이 직렬화 & 역직렬화이다.
HTTP 응답은 서버 → 클라이언트 흐름이기 때문에 자바 객체 → JSON 변환인 직렬화 과정이고,
HTTP 요청은 클라이언트 → 서버 흐름이기 때문에 JSON 요청 → 자바 객체 변환인 역직렬화 과정이다.
쉽게 설명하면,
ResponseDto를 HTTP 응답에 담을 때 ResponseDto를 JSON으로 변환하는 과정이 직렬화 과정이고
JSON HTTP 요청 시 JSON이 RequestDto로 변환되는 과정이 역직렬화 과정이다.
역직렬화 & 직렬화의 개념도 알아봤으니
이제, 스프링이 HTTP 요청 & 응답 시 어떻게 역직렬화 & 직렬화를 하는지 살펴보자.
🎯 1. 역직렬화 & 직렬화 전체 흐름
✅ 1-1. HTTP 요청 역직렬화 흐름
1. HTTP API 요청
2. 요청에 맞는 `HttpMessageConverter` 사용
3. `HttpMessageConverter`에서 `ObjectMapper`를 사용해서
데이터(XML, JSON 등) -> 자바 객체로 역직렬화한 결과 반환
✅ 1-2. HTTP 응답 직렬화 흐름
1. 보낼 응답에 맞는 `HttpMessageConverter` 사용
2. `HttpMessageConverter`에서 `ObjectMapper`를 사용해서
자바 객체 -> 데이터(XML, JSON 등)로 직렬화한 결과를 반환
💡 HTTP 요청, 응답 흐름 요약
HTTP 요청과 응답 흐름을 공통적으로 요약해보면 다음과 같다.
1. 적절한 `HttpMessageConverter` 탐색
2. 찾은 `HttpMessageConverter`에서 역직렬화 & 직렬화한 결과 반환
공통적으로 등장하는 `HttpMessageConverter` 녀석이 역직렬화 & 직렬화 책임을 수행하는 객체이다!
요약한 HTTP 요청, 응답 흐름대로 `HttpMessageConverter`를 살펴보자!!
🎯 2. 적절한 `HttpMessageConverter 탐색`
* HTTP 요청 : @RequestBody, RequestEntity 객체
* HTTP 응답 : @ResponseBody, ResponseEntity 객체
위와 같은 어노테이션, 객체가 있으면 HttpMessageConverter를 호출해서 변환을 거친다.
※ HTTP 요청 & 응답 시 HttpMessageConverter 리스트
해당 리스트에 있는 Converter 중에서 적절한 Converter를 가져와서 사용한다.
그렇다면, 적절한 Converter를 어떻게 판단할까?
✅ HttpMessageConverter 인터페이스
package org.springframework.http.converter;
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage
outputMessage)
throws IOException, HttpMessageNotWritableException;
}
💡 canRead() & canWrite()
👉 HttpMessageConverter가 해당 클래스, 미디어 타입을 지원하는지 체크
HttpMessageConverter의 해당 메소드를 사용하여, 적절한 Converter를 찾아서 사용하게 된다.
적절한 Converter를 찾는 과정을 살펴보자.
✅ 2-1. HTTP 요청 시 적절한 HttpMessageConverter 탐색 과정
1. HTTP 요청 들어옴
2. Controller에서 @RequestBody, RequestEntity를 파라미터에서 사용하면
HttpMessageConverter 리스트 탐색
3. 해당 데이터를 읽을 수 있는지 판단하기 위해 HttpMessageConverter 리스트를 돌면서
해당 Converter의 `canRead()` 메소드 호출
3-1. HTTP 요청으로 온 해당 클래스 타입을 지원하는가?
(ex : String[], 객체, ...)
3-2. HTTP 요청으로 온 미디어 타입(Content-Type)을 지원하는가?
(ex : `text/plain`, `application/json`)
4. 3-1, 3-2의 조건을 만족하면 `canRead()`가 true를 반환
✅ 2-2. HTTP 응답 시 적절한 HttpMessageConverter 탐색 과정
1. Controller에서 @ResponseBody, ResponseEntity를 파라미터에서 사용하면
HttpMessageConverter 리스트 탐색
2. 해당 데이터를 읽을 수 있는지 판단하기 위해 HttpMessageConverter 리스트를 돌면서
해당 Converter의 `canWrite()` 메소드 호출
2-1. HTTP 응답으로 보낼 해당 클래스 타입을 지원하는가?
(ex : String[], 객체, ...)
2-2. HTTP 응답으로 보낼 Accept 미디어 타입(Content-Type)을 지원하는가?
(ex : `text/plain`, `application/json`)
3. 2-1, 2-2의 조건을 만족하면 `canRead()`가 true를 반환
🎯 3. 찾은 HttpMessageConverter에서 역직렬화/직렬화한 결과 반환
2번 과정을 거쳐서, 적절한 HttpMessageConverter를 찾았다.
그렇다면, 찾은 HttpMessageConverter에서 역직렬화 & 직렬화의 결과를 어떻게 생성할까?
다시 HttpMessageConverter 인터페이스를 살펴보자.
✅ HttpMessageConverter 인터페이스
package org.springframework.http.converter;
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage
outputMessage)
throws IOException, HttpMessageNotWritableException;
}
💡 read() & write()
👉 HttpMessageConverter를 통해 요청 & 응답 메시지를 읽고 쓰는 기능 수행
HttpMessageConverter의 해당 메소드를 사용하여, 역직렬화 & 직렬화 결과를 생성한다.
이때, ObjectMapper 클래스를 사용하여 역직렬화 & 직렬화 결과를 생성한다.
ObjectMapper는 ObjectReader, ObjectWriter를 생성하는 팩토리 역할을 수행한다.
(그 이상의 자세한 원리는 너무 깊은 것 같아서 다루지 않았다.)
✅ 3-1. HTTP 요청 : 역직렬화 -> ObjectReader.readValue()로 자바 객체 생성
...
objectReader.readValue(inputStream);
※ HttpMessageConverter의 read() 메소드
`objectReader.readValue()`를 통해 요청으로 들어온 데이터가 자바 객체로 역직렬화되어 반환된다.
✅ 3-2. HTTP 응답 : 직렬화 -> ObjectWriter.writeValue()로 데이터 생성
...
objectWriter.writeValue(generator, value);
`objectWriter.writeValue()`를 통해 응답으로 나갈 데이터가 XML, JSON 등으로 직렬화되어 반환된다.
🎯 4. 스프링 HTTP API 요청 & 응답 시 역직렬화 직렬화 원리 요약
* @RequestBody, @ResponseBody, RequestEntity, ResponseEntity가 존재할 때
1. 메시지 컨버터 리스트를 돌면서 적절한 `HttpMessageConverter` 탐색
2. 찾은 `HttpMessageConverter`에서 ObjectMapper로 역직렬화 & 직렬화한 결과 반환
'Spring' 카테고리의 다른 글
[Spring] 스프링 동시성 처리 방법(feat. 비관적 락, 낙관적 락, 네임드 락) (4) | 2023.11.21 |
---|---|
[Spring] SpringBoot의 Tomcat 설정 알아보기 (feat. Thread, Thread Pool) (4) | 2023.09.14 |
[Spring] 스프링 이벤트를 사용하여 도메인 의존성 분리하기 (2) | 2023.07.30 |
[Spring] 테스트 시 DB 데이터 초기화 Trouble Shooting (0) | 2023.05.28 |
[Spring] Spring Boot application.yml 환경별 프로필 설정 방법 (SpringBoot 2.4 기준 비교) (2) | 2022.08.28 |