JAVA/JAVA | Spring 학습기록

[ERROR] 406 에러 _ Not Acceptable (스프링 직렬화/역직렬화)

kth990303 2022. 4. 23. 15:28
반응형

문제 상황

스프링 환경에서 특정 dto 를 반환해주는 api에서 406 에러가 발생했다.

@GetMapping("/status")
public ResponseEntity<ScoreDto> status() {
    return ResponseEntity.ok(chessService.getStatus());
}

undefined가 뜨면서 /status api가 아예 실행되지 않았다.

뿐만 아니라, 응답으로 MoveDto를 받아서 작동하는 move api도 아예 작동하지 않았다.

@PostMapping("/move")
public ResponseEntity<ChessMap> move(@RequestBody MoveDto moveDto) {
    System.out.println(moveDto);
    return ResponseEntity.ok(chessService.move(moveDto));
}

MoveDto를 요청으로 받아서 응답값을 던져줘야 하는데, 요청이 안되는지 응답이 안되는지 모르겠지만 실행히 되지 않았다.

 

스파크 환경에서는 잘 작동하던 것이, 스프링 환경에서 잘 작동하지 않아 꽤나 당황스러웠다.

db 연동도 문제 없고, 비즈니스 로직도 문제 없었어서 꽤나 당황스러웠던 현상이다.


문제 원인

1. /status api가 잘 진행되지 않았던 원인 _ getter 메서드의 부재

스프링은 별도로 지정해주지 않는 이상 직렬화/역직렬화 방법으로 Jackson 라이브러리를 이용한다.

Jackson은 Java Object를 Json 형태로 변환시켜주거나, 반대로 Json 객체를 Java Object 형태로 변환시켜주는 라이브러리이다.

db table에는 java object를 담을 수 없기 때문에 영속화를 위해선 json 형태로 변환해주는 라이브러리가 필요하다.

 

그런데 Jackson이 Java Object를 Json 형태로 변환시켜주기 위해선 각 필드의 getter 메서드와 기본생성자를 필요로 한다.

내 코드의 경우, 컨트롤러에서 반환하는 DTO에 getter 메서드가 없던 것이 화근이었다.

public class ScoreDto {

    private final String scoreStatus;

    public ScoreDto(String scoreStatus) {
        this.scoreStatus = scoreStatus;
    }

    public String getScoreStatus() {
        return scoreStatus;
    }
}

dto에 getScoreStatus() 메서드를 추가해주고 실행해보자.

현재 점수 보기 (/status api)기능이 잘 실행됨을 알 수 있다.

잘 실행된다!


2. /move api가 잘 진행되지 않았던 원인 _ 기본생성자의 부재

체스말을 움직일 때 에러가 발생하면서 움직이지 않는다.

@PostMapping("/move")
public ResponseEntity<ChessMap> move(@RequestBody MoveDto moveDto) {
    System.out.println(moveDto);
    return ResponseEntity.ok(chessService.move(moveDto));
}

/move api를 실행해보면 아래 에러가 발생하는 것을 볼 수 있다.

Type definition error: [simple type, class chess.dto.MoveDto]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `chess.dto.MoveDto` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]

 

이는 jackson이 데이터를 bind하는 과정에서 문제가 생겼다는 것인데,

기본 생성자가 존재하지 않아 deserialize가 되지 않았다는 것이다.

 

즉, Json 객체로 요청값은 받아왔는데, MoveDto에 기본생성자가 존재하지 않아 MoveDto로 역직렬화가 되지 않았다는 것이다.

이제 MoveDto에도 아래와 같이 기본생성자를 추가해보자.

private String currentPosition;
private String destinationPosition;

public MoveDto() {
}

public MoveDto(String currentPosition, String destinationPosition) {
    this.currentPosition = currentPosition;
    this.destinationPosition = destinationPosition;
}

원래 MoveDto는 파라미터를 받는 생성자만 존재했는데, Jackson 라이브러리 기능을 이용하기 위해 기본생성자를 추가해준 것이다.

MoveDto를 응답값으로 제대로 전달해주어

체스말이 잘 움직이는 것을 확인할 수 있다!

 

+) 406 에러 말고도, 400 에러, 500 에러가 뜨기도 하는 듯하다. 스프링 환경에서 직렬화/역직렬화 기능을 제대로 작동시키기 위해 dto (또는 responseentity에 담아주는 클래스)에 getter, 기본생성자를 만들어주도록 하자.


스프링 환경에서 Jackson 라이브러리를 사용할 때의 주의점을 이번 미션을 통해 깨닫게 됐다.

406 에러는 흔하게 볼 수 있는 에러가 아니었어서 이번 경험은 특히 더 귀중한 경험이 될 듯하다~

반응형