SpringBoot는 어떻게 Json 데이터를 받아올 수 있을까?

주노·2023년 4월 23일
5

Spring이랑 친해지기

목록 보기
2/11
post-thumbnail

서론

SpringBoot로 학습을 시작하면 생각보다 "당연하게 해주는건가?" 싶은 내용이 많은 것 같다.

SpringBoot를 사용하면서 Controller 단에서 어떻게 json 형태로 들어오는 값을 객체로 매핑할까? 라는 의문이 들었고 이에 대해 알아보도록 하자.

ObjectMapper

우선 우리는 ObjectMapper라는 친구를 알아봐야한다.
ObjectMapper는 객체를 json형태로 직렬화(Serialize) 혹은 json을 객체로 역직렬화(DeSerialize) 해주는 라이브러리다.

의존성으로spring-boot-starter-web을 추가해주면 받아오는 라이브러리 중 jackson이라는 라이브러리가 존재한다.

이곳에 ObjectMapper가 존재한다.

ObjectMapper 동작

@PostMapping("/plays")
public ResponseEntity<GameResponse> plays(@RequestBody CarGameRequest request) {
    GameResponse result = racingGameService.play(request);
    return ResponseEntity.ok(result);
}

우리가 @RequestBody라는 어노테이션을 붙여줬을 때 SpringBoot는 ObjectMapper라는 친구를 이용해 데이터를 직렬화/역직렬화 시킨다.

ObjectMapper가 어떻게 동작하길래 json, 객체 간 변환이 가능할까?
어떤 점을 유의해야할지도 알아보자

어노테이션만 붙였는데 어떻게 감지해서 동작할까?
에 대한 부분은 차후 ArgumentResolver에 대해 다루면서 알게 될 예정이다.

여담으로 @RequestBody에는 ArgumentResolver가 동작하지 않는다...!!! 참고자료
너무 깊어지니까 다른 글에서 알아보자

json -> Object

프론트로부터 json 형태로 받아온 데이터를 객체에 매핑시키는 과정을 생각해보자

위 코드에서 인자로 들어오는 CarGameRequest라는 객체가 있다.

해당 객체는 다음과 같이 생겼다.

public class CarGameRequest {

    private String names;

	private Integer count;

    public CarGameRequest() {
    }

    public CarGameRequest(String names, Integer count) {
        this.names = names;
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public String getNames() {
        return names;
    }
}

@RequestBody 어노테이션을 붙여주면 ObjectMapper가 파싱을 수행한다.

ObjectMapper의 readValue() 메소드를 이용해서 Json 데이터가 객체로 파싱된다.

이 떄 ObjectMapper의 동작 과정에서 리플렉션을 이용해 내부 필드 값을 정해준다.

Reflection?

리플렉션을 사용하면 접근 제어자와 무관하게 클래스의 필드나 메소드를 가져와서 호출할 수 있다.

하지만 리플렉션을 사용했을 때 어떤 인자를 가진 생성자가 존재하는지는 읽을 수가 없다.

class Person() {
	Person(Integer age, String name) {
    	...
    }
}

예를들면 위와같이 기본생성자가 없는 경우 리플렉션으로 위 객체에 접근할 수가 없게 된다.

따라서 요약하자면 ObjectMapper의 readValue() 메소드는 자바의 리플렉션을 이용하기 때문에 기본 생성자를 반드시 선언해줘야한다.
라고 정리할 수 있겠다.

Object -> json

@PostMapping("/plays")
public ResponseEntity<GameResponse> plays(@RequestBody CarGameRequest request) {
    GameResponse result = racingGameService.play(request);
    return ResponseEntity.ok(result);
}

반대로 객체를 json형태로 변형할 때는 ObjectMapper의 writeValue() 메소드를 이용해서 직렬화를 수행한다.

이때 객체 내부에 선언되어있는 getXXX() 혹은 setXXX() 형태의 메소드를 통해 직렬화를 수행한다.

결론

이번 시간에는 jackson 라이브러리에 존재하는 ObjectMapper라는 친구가 어떠한 방식으로 동작하는지 가볍게 알아봤다.

다음은 1depth 더 들어가서 ArgumentResolver에 대해 알아보도록 하자.

➡️ 1depth 더 들어가서 ArgumentResolver에 대해 알아보기!

Reference

@RequestBody에 왜 기본 생성자는 필요하고, Setter는 필요 없을까? #1

Intro to the Jackson ObjectMapper

[우아한테크코스] 기본 생성자가 필요한 이유 (Why the default constructor is needed) (feat. Jackson ObjectMapper + Reflection)

자바 리플렉션 (Reflection) 기초

Java Reflection API에 대하여 (JPA에서 기본 생성자가 반드시 필요한 이유)

Guide to Java Reflection

profile
안녕하세요 😆

2개의 댓글

comment-user-thumbnail
2024년 1월 3일

많은 도움이 되었습니다 ~!

1개의 답글