에러 기록 : rest api 응답시 도메인 객체의 id 필드 생략되는 경우

김아무개·2023년 10월 25일
0

에러기록

목록 보기
14/17

api 응답을 EntityModel<User> 타입으로 해주고 있었더니
@Id 어노테이션이 붙은 id 필드가 응답 결과에 출력되지 않았다.

id 필드를 출력해주기 위해 다음 코드를 추가해 주고
MappingJacksonValue 타입으로 반환값을 변경해주니 원하는 형태로 응답 데이터가 출력되었다.

MappingJacksonValue mapping = new MappingJacksonValue(entityModel);
mapping.setFilters(null);

검색해도 원인을 찾지 못해서 chatGPT에 물어보니

Jackson은 객체를 직렬화할 때 @Id 어노테이션 또는 id 필드에 대해 특별한 처리를 하지 않는 경우, 기본적으로 id 필드를 출력하지 않도록 동작합니다.

라고 했다.


관련해서 설계되어있는 내용을 확인했다면 좋았을텐데
아직 알아내지 못했다.

구글링으로 id 필드에 @JsonIgnore(false) 를 추가하면 해결된다고 봤는데
나에겐 적용되지 않는 해결방안이었다.

그리고 jackson으로 직렬화 하는 과정에 대해 디버깅을 해보고자 노력을 기울여보았지만 실패하고 말았다;

application.yml에서 logging 레벨도 조절해봤는데 로그를 볼 수 없었다. 😠

디버깅 포인트도 찍어보았는데 알아내지 못했다.

포인트를 잘못 짚었던걸지도 🫤

관련해서 더 조사해보고자 한다.
알아내면 꼭 기록하러 다시 와야지 😡(내일)
gg😣


+ Spring Boot 버전 2.7에서 경험한 에러라 혹시 3점대로 버전을 올리면 사라질까? 싶어서 3.1.5로 올려봤는데 그대로였다


원치않았던 결과 : User 객체의 id 필드만 생략 된 코드

@Slf4j
@RequiredArgsConstructor
@RequestMapping("/jpa")
@RestController
public class UserJpaController {
    private final UserRepository userRepository;

    @GetMapping("/users")
    public ResponseEntity<CollectionModel<User>> retrieveAllUsers() {
        List<User> users = userRepository.findAll();

        UserJpaController methodOn = methodOn(this.getClass());
        CollectionModel<User> model = CollectionModel.of(users);
        model.add(linkTo(methodOn.retrieveAllUsers()).withSelfRel().withType("GET"));
        model.add(linkTo(methodOn).slash("_id").withRel("info-user").withType("GET"));

        return ResponseEntity.ok(model);
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<EntityModel<User>> retrieveUser(@PathVariable int id) {
        Optional<User> optionalUser = userRepository.findById(id);

        if (!optionalUser.isPresent())
            throw new UserNotFoundException(String.format("없는 사용자 입니다. [id : %d]", id));

        EntityModel<User> model = EntityModel.of(optionalUser.get());
        model.add(linkTo(methodOn(this.getClass()).retrieveUser(id)).withSelfRel().withType("GET"));
        model.add(linkTo(methodOn(this.getClass()).retrieveAllUsers()).withRel("all-users").withType("GET"));

        return ResponseEntity.ok(model);
    }
}

id 필드가 삭제된 응답 결과 - API GET "/users/{id}"

GET http://localhost:8088/jpa/users/1

HTTP/1.1 200 
Content-Type: application/hal+json
Transfer-Encoding: chunked
Date: Wed, 25 Oct 2023 09:54:27 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{
  "name": "zhyun1",
  "joinDate": "2023-10-25T18:54:22.185935",
  "password": "1111",
  "ssn": "701010-1111111",
  "_links": {
    "self": {
      "href": "http://localhost:8088/jpa/users/1",
      "type": "GET"
    },
    "all-users": {
      "href": "http://localhost:8088/jpa/users",
      "type": "GET"
    }
  }
}
응답 파일이 저장되었습니다.
> 2023-10-25T185427.200.json

Response code: 200; Time: 700ms (700 ms); Content length: 346 bytes (346 B)

 

원하던 결과 : User 객체의 id 필드가 출력되는 코드

@Slf4j
@RequiredArgsConstructor
@RequestMapping("/jpa")
@RestController
public class UserJpaController {
    private final UserRepository userRepository;

    @GetMapping("/users")
    public ResponseEntity<MappingJacksonValue> retrieveAllUsers() {
        List<User> users = userRepository.findAll();

        UserJpaController methodOn = methodOn(this.getClass());
        CollectionModel<User> model = CollectionModel.of(users);
        model.add(linkTo(methodOn.retrieveAllUsers()).withSelfRel().withType("GET"));
        model.add(linkTo(methodOn).slash("_id").withRel("info-user").withType("GET"));

        MappingJacksonValue mapping = new MappingJacksonValue(model);
        mapping.setFilters(null);

        return ResponseEntity.ok(mapping);
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<MappingJacksonValue> retrieveUser(@PathVariable int id) {
        Optional<User> optionalUser = userRepository.findById(id);

        if (!optionalUser.isPresent())
            throw new UserNotFoundException(String.format("없는 사용자 입니다. [id : %d]", id));

        EntityModel<User> model = EntityModel.of(optionalUser.get());
        model.add(linkTo(methodOn(this.getClass()).retrieveUser(id)).withSelfRel().withType("GET"));
        model.add(linkTo(methodOn(this.getClass()).retrieveAllUsers()).withRel("all-users").withType("GET"));

        MappingJacksonValue mapping = new MappingJacksonValue(model);
        mapping.setFilters(null);

        return ResponseEntity.ok(mapping);
    }
}

id 필드가 포함된 응답 결과 - API GET "/users/{id}"

GET http://localhost:8088/jpa/users/1

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 25 Oct 2023 10:00:53 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{
  "id": 1,
  "name": "zhyun1",
  "joinDate": "2023-10-25T19:00:48.985739",
  "password": "1111",
  "ssn": "701010-1111111",
  "_links": {
    "self": {
      "href": "http://localhost:8088/jpa/users/1",
      "type": "GET"
    },
    "all-users": {
      "href": "http://localhost:8088/jpa/users",
      "type": "GET"
    }
  }
}
응답 파일이 저장되었습니다.
> 2023-10-25T190053.200.json

Response code: 200; Time: 650ms (650 ms); Content length: 249 bytes (249 B)

HATEOAS 에 대해 배울 기회가 생겨서 공부하고는 있는데
controller가 너무 복잡해지는것 같다;

실무에서 잘 사용할까?

궁금한데 물어볼데가 읎그..,,,........,,.,,


 

로그를 찾아 헤매는 과정 기록

1. 분명 console 로그엔 id까지 잘 나타나는데


api 응답 결과에는 id만 쏙 빠진다

왤까 ☹️



2. 브레이크 포인트 찍어서 디버깅 해보다가 발견

여기까진 값이 잘 들어있는것을 확인


여기까진 값이 잘 들어있는것을 확인 2


여기까진 값이 잘 들어있는것을 확인 3


오 도저히 못찾겠다 🤮


profile
Hello velog! 

0개의 댓글