본문 바로가기
에러 해결

HttpMediaTypeNotAcceptableException - No acceptable representation

by sangyunpark99 2024. 9. 28.
에러가 발생한 상황을 해결하기 위한
생각의 흐름을 정리한 글입니다.

 
에러가 발생한 상황은 다음과 같습니다.
UserController에서 회원가입 기능을 하고, 로컬 테스트를 하기 위해서 postman을 사용해서 테스트를 하고, 결과를 받은 response가 다음과 같다.

 
응답 결과로 406 Not Acceptable 오류가 발생했다.
시스템 로그를 확인해보자.
 
로그의 쿼리문을 먼저 확인해보겠습니다.
Postman 요청을 보낸 후, 다음과 같은 쿼리가 나간다.
현재 내가 짠 로직에 맞게 쿼리가 알맞게 나갔는지부터 확인해보자.

 
쿼리를 각각 분리해보면 select 쿼리와 insert 쿼리로 나뉘어져 있다.
쿼리를 날리는 로직은 대개 UserServcie 클래스에 작성을 해두었다.  쿼리가 사용되는 로직은 다음과 같다.
 
UserService - signup 메서드

@PostMapping("/signup")
    public ResponseEntity<UserSignupResponse> signup(@Valid  @RequestBody UserSignupRequest request) {

        userService.signup(request);

        return ResponseEntity.status(HttpStatus.CREATED).body(new UserSignupResponse());
    }

 
 
isDuplicated 메서드

@Transactional(readOnly = true)
public boolean isDuplicated(final String id) {
    return userRepository.countByUserId(id) == 1;
}

 

 

userRepository.countByUserId(id);

 
위 쿼리는 isDuplicated 메서드 내부에 존재하는 위 로직을 통해서 호출된다.
 
 
 

 

userRepository.save(user);

 
위의 쿼리는 위 로직을 통해서 호출한다.
 
쿼리문이 필요한 로직은 모두 정상 호출 되었다.
로직의 오류가 발생하지 않는 것은 아래 명시되어 있는 UserLoginRequest.class 또한 오류가 없음을 알 수 있다.
(쿼리가 날라가려면, 이전의 전반적인 모든 과정이 오류가 없어야하기 때문이다.)
 
Request - UserLoginRequest

package com.sangyunpark.smileboard.user.dto.request;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;

@Getter
public class UserLoginRequest {

    @NotBlank
    private String userId;

    @NotBlank
    private String password;
}

 
마지막으로 의심해볼 클래스는 UserSignupResponse.class이다.
이  클래스는 회원가입이라는 비즈니스 로직의 마지막 부분에 사용되는 응답 관련 클래스이다.

package com.sangyunpark.smileboard.user.dto.response;

public class UserSignupResponse {

    private String message;

    public UserSignupResponse() {
        this.message = "회원가입 성공";
    }
}

 
 
우선 UserSignupResponse 코드에 대한 찜찜함을 잠깐 미뤄두고, 에러의 코드를 먼저 봐보자. 에러 코드는 다음과 같다.

HttpMediaTypeNotAcceptableException - No acceptable representation

 
 
공식 문서에는 이 오류를 다음과 같이 정의한다.

request handler가 클라이언트에 의해 허용되는 response를 생성할 수 없을 때, 던져지는 예외

 
 
쉽게 말해, 서버에서 클라이언트 요청을 처리하는 과정에서, 요청에 대한 응답이 클라이언트가 원하는 형식이나 조건에 맞지 않아서 서버가 적절한 응답을 만들지 못할 때 발생하는 예외 상황을 의미한다.
 
서버가 적절한 응답을 만들지 못한다..?
Response라는 클래스도 잘 정의해주고 있고, 코드 단에서도 ResponseEntity라는 클래스를 사용하여, 적절한 로직으로 작성되어있다.
 
"왜 응답을 만들지 못할까?" 라는 질문을 던졌고, 이 질문을 하다보니, 다음과 같은 질문이 꼬리를 물었다.
"ResponseEntity는 어떠한 방식으로 응답코드를 주는 걸까?"
"ResponseEntity는 어떻게 제너레이터안에 넣어준 UserSignupResponse 클래스에 정의된 값을 가져올까?"
 
먼저 생각한 질문인 "ResponseEntity는 어떠한 방식으로 응답코드를 줄까?"라는 질문부터 해결해보자.

 
내가 사용하고 있는 ResponseEntity 의 메서드는 다음과 같다.
코드만 보면, 응답의 상태값과 바디값을 정의해주는 것을 볼 수 있다.
 

ResponseEntity
HttpEntity

 
status라는 필드값은 ResponseEntity에 정의되어 있고, body에 대한 부분은
부모인 HttpEntity에 정의되어 있다.
 

ResponseEntity.ok()

ok메서드는 위에처럼 빌더패턴을 이용해 사용되도록 구현되어 있었다.
단순히 status 필드의 값을 정의해주는 역할이다.
 
돌아가는 로직에대한 부분은 이해가 갔다.
 
내가 생각한 두번째 질문은 다음과 같다.
"ResponseEntity는 어떻게 제너레이터안에 넣어준 UserSignupResponse 클래스에 정의된 값을 가져올까?"
 
이 질문을 생각해보니, 이런 질문도 떠오른다. "나는 클래스에 있는 필드를 가져올 때 어떻게 가져오지?"
코드의 상황을 대입해 다시 생각해 질문을 보니, "클래스의 필드가 private인 경우는 어떻게 가져오지?"라는 질문이 떠올랐다.
 
아뿔싸!! 그렇다...
private은 클래스 내부에서만 접근이 가능하지, 외부에서 접근하기 위해선 접근에 필요한 메서드가 필요했다.
get메서드를 만들어주거나 Lombok의 @Getter 어노테이션을 사용해서 private한 필드의 값을 가져올 수 있다는 말이다.
 
UserSignupResponse.class 코드를 다시한번 봐보자.

package com.sangyunpark.smileboard.user.dto.response;

public class UserSignupResponse {

    private String message;

    public UserSignupResponse() {
        this.message = "회원가입 성공";
    }
}

 

아뿔싸.. private으로 필드를 선언해두고,
private한 필드의 값을 가져올 수 있는 getter 메소드도 만들어 두지 않은 채로
왜 제너레이터의 클래스를 넣어줌에도 불구하고,
클래스 내부 필드 변수 값에 맞는 응답 형태가 JSON으로 반환되지 않을까? 라고 생각했다.
private하기 때문에 getter메서드가 아니면, 가져올 수가 없는 구조였다.

 
이 논리를 기반으로, getter메서드를 선언해서 응답값이 잘 넘어오는지 확인해보자.
 
변경한 코드는 다음과 같다.
@Getter 어노테이션만 추가 해주었다.

package com.sangyunpark.smileboard.user.dto.response;

import lombok.Getter;

@Getter
public class UserSignupResponse {

    private String message;

    public UserSignupResponse() {
        this.message = "회원가입 성공";
    }
}

 
 
POSTMAN으로 로컬 환경으로 응답값을 확인해준 결과, 올바른 결과값이 나오게 되었다.

 

 

깨달은 점

컴퓨터는 내가 짠 코드에 대해서만 정직하게 실행해준다.
오류가 발생하는 건 100% 나의 잘못이다. 컴퓨터는 아무런 잘못이 없다.