Today
-
Yesterday
-
Total
-
  • 에러 기록 : rest api 응답시 도메인 객체의 id 필드 생략되는 경우
    에러기록 2023. 12. 17. 00:01

    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


    오 도저히 못찾겠다 🤮


Designed by Tistory / Custom by 얼거스