@RequestBody 직렬화와 역직렬화 눈으로 확인하기

2024. 5. 30. 22:16·개발/Spring MVC
반응형

자바 스프링에서 @RequestBody 직렬화, 역직렬화에 대해 알아보겠다.

 

Java에서 @RequestBody

필자는 요청 바디에 json 객체를 담아 요청했다.

따라서 헤더의 content-type은 application/json이다.

 

@RequestBody를 사용하면 RequestResponseBodyMethodProcessor가 HTTP 요청을 우리가 정의한 자바 객체로 변환해준다.

 

그럼 어떻게 HTTP 요청을 자바 객체로 변환해주는지 살펴보자.

 

실습 코드는 아래와 같다.

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExampleController {

    @PostMapping("/json")
    public void json(@RequestBody Request request) {
        System.out.println("request = " + request);
    }

    static class Request {
        private String message;
        private int age;

        public Request(String message, int age) {
            this.message = message;
            this.age = age;
        }

        public String getMessage() {
            return message;
        }

        public int getAge() {
            return age;
        }

        @Override
        public String toString() {
            return "Request{" +
                    "message='" + message + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

 

스프링은 별도로 설정을 하지 않는한 요청 바디에서 읽거나 쓰는 경우  MappingJackson2HttpMessageConverter를 사용한다.

해당 코드는 내부적으로 ObjectMapper를 활용한다.

 

객체를 생성하는 순서는 크게 다음과 같다.

 

1. 먼저 @RequestBody 애노테이션이 붙은 클래스 타입을 파악한다.

RequestResponseBodyMethodProcessor.resolveArgument가 파라미터로 받는 parameter에서 클래스를 파악할 수 있다.

이후 요청에서 객체를 만들 때 여기서 얻은 클래스 타입을 계속 활용한다. 

RequestResponseBodyMethodProcessor 클래스 내부 코드

2. 생성자가 존재하는 경우 BeanDeserializer 클래스에서 요청 바디에서 각 필드의 값을 역직렬화해 PropertyValueBuffer 객체에 저장한다.

BeanDeserializer 클래스 코드

 

이후 리플렉션 기술인 java.lang.reflect.Constructor을 사용해 요청 객체를 생성하고 이를 리턴한다.

 

StdValueInstantiator 내부 코드

3. Getter만 있는 경우에도 동일하게 BeanDeserializer 클래스를 사용하지만 deserializeFromObject라는 메서드를 사용한다.

빨간 상자를 친 부분에서 기본 생성자를 통해 요청 객체를 생성한다.

 

빨간 상자를 친 부분에서 기본 생성자를 통해 요청 객체를 생성한다.

 

 

이후에 필드들을 추출해 java.lang.reflect.Field 클래스의 set 메서드를 사용해 필드 값을 주입한다.

아래 빨간 상자를 친 메서드 내부에서 로직이 동작한다.

 

이렇게 직접 input을 읽어서 객체로 매핑하는 과정을 살펴보았다. 

 

여기까지는 요청 클래스에 관한 얘기이다.

응답 클래스의 경우는 생성자는 사용하지않는다. 응답 값을 직렬화할 때는 게터만 필요하다.

 

추가적으로 public 인스턴스 변수를 사용하면 게터를 사용하지 않아도 된다! 리플렉션이 문제 없이 적용되기 때문이다.

사실 인스턴스 변수를 public으로 작성할 일은 아예 없다고 본다.

따라서 결론은 Getter를 꼭 작성해야 한다는 것이다.

 

그럼 이번에는 코틀린의 경우를 살펴보겠다.

 

Kotlin에서 @RequestBody

예시 코드는 다음과 같다.

 

data class와 class 모두 동일하게 생각하면 된다.

코틀린은 변수를 작성하면 Getter, Setter가 컴파일러에 의해 작성된다.

import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class LabController {

    @PostMapping("/json")
    fun json(@RequestBody request: Request): Request {
        println("request = ${request}")
        return request
    }
}

//data class가 아닌 class도 동일
data class Request(
    val message: String,
    val age: Int
)

 

따라서 요청 역직렬화, 응답 직렬화에 문제가 없다.

코틀린 바이트 코드를 다시 자바로 디컴파일 하면 해당 코드를 찾을 수 있다.

 

 

참고로 아래와 같이 코드를 작성하면 getter이 자동으로 생성되지 않아서 요청의 역직렬화는 가능하지만 응답의 직렬화는 실패한다.

//단순 class도 동일
data class Request(
    private val message: String,
    private val age: Int
)

 

참고로 아래와 같이 직접 게터를 작성하면 응답의 직렬화도 통과한다.

 

data class Request(
    private val message: String,
    private val age: Int
) {
    fun getMessage() = this.message
    fun getAge() = this.age
}

 

 

이상으로 포스팅을 마칩니다. 감사합니다.

반응형
'개발/Spring MVC' 카테고리의 다른 글
  • Spring Interceptor 경로에서 Swagger 경로 제거 시 발생 오류
  • Spring MVC 2편 - API 예외 처리 (@ControllerAdvice, ExceptionHandler)
  • Spring MVC 2편 - 예외 처리와 오류 페이지
  • Spring MVC 2편 - 로그인 필터와 인터셉터
Debin
Debin
공부 기록을 남기며 게시글 리팩토링을 진행하는 블로그입니다.
  • Debin
    리팩토링하는 블로그
    Debin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • DB
        • DB 기초
        • MySQL
        • SQL 튜닝
      • OS
      • Network
      • Git
      • 디지털콘텐츠기획
      • 소프트웨어공학
      • 코딩테스트
        • 프로그래머스
        • 백준
        • 인프런
      • 공부 일지
      • 독서
        • 클린코드
        • 일상 속 사물이 알려주는 웹 API 디자인
        • 토비의 스프링
        • 객체지향의 사실과 오해
        • 자바 잘 읽는 법
      • 기록 및 회고
      • Cloud
        • AWS
      • 개발
        • Java
        • Spring Core
        • Spring MVC
        • Spring DB
        • Spring Boot
        • Spring Security
        • Spring Batch
        • JPA
        • Test
        • Android
      • 대외활동
        • UMC SERVER
        • 카엔프 SW 아카데미
      • 프로젝트
      • Docker
      • Gradle
      • ELK
      • 실무 이야기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

    • 본인 깃허브입니다!
  • 인기 글

  • 태그

    container
    AWS
    도커
    운영체제
    프록시
    spring mvc
    코딩 #개발자 #노마드북클럽 #노개북
    mysql
    SQL
    컨테이너
    JPA
    자바
    리눅스
    인덱스
    Java
    스프링
    객체
    ORM
    spring
    AOP
    데이터베이스
    spring boot
    객체지향
    test
    토비의 스프링
    innodb
    트랜잭션
    스프링 부트
    docker
    redis
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Debin
@RequestBody 직렬화와 역직렬화 눈으로 확인하기
상단으로

티스토리툴바