코디잉

[양방향 매핑 순환참조 발생] java.lang.IllegalStateException: Cannot call sendError() after the response has been committed 본문

JPA

[양방향 매핑 순환참조 발생] java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

yong_ღ'ᴗ'ღ 2023. 8. 22. 16:59

DB에 있는 모든 user를 불러오기 위해 코드를 작성하고, 

@RestController
@RequestMapping("/api")
public class UserApiController {

    @Autowired
    private UserRepository userRepository;
    
    @GetMapping("/users")
    List<User> all() {
        return userRepository.findAll();
    }
}

 

Postman으로 테스트를 진행하는데 에러가 발생했다.

 

 

그래서 웹브라우저에 해당 url을 입력해봤더니, 현재 DB에 User 테이블에는 한 명만 있는데,

그 한 명을 계속해서 조회하다가 결국 StackOverflowError가 발생했다.

찾아보니, jackson으로 직렬화를 할 때 순환참조가 발생해서 무한루프 도는 거라고 한다.

 

현재 User DB 상태 - gildong이 한 명만 들어있음
ㅎㅎ gildong이 무한루프 중

 


양방향 관계(순환 참조)가 있는 Entity를 JSON 데이터 구조로 생성하려고 할 때 접할 수도 있는 에러이다.

@JsonIgnore, @JsonManagedReference, @JsonBackReference, @JsonIdentityInfo를 사용해서 해결할 수 있는데, 

여기서는 간단하게 @JsonIgnore를 사용해서 해결해보려고 했지만....

@JsonIgnore 어노테이션을 붙여도 작동하지 않고, 계속 순환참조가 발생해서 @JsonIdentityInfo를 사용했다.

 

일단 어노테이션을 적용하기 전 User와 Board Entity이다.

@Entity
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;
    private Boolean enabled;

    @ManyToMany
    @JoinTable(
            name = "user_role",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private List<Role> roles = new ArrayList<>();

    @OneToMany(mappedBy = "user")
    private List<Board> boards = new ArrayList<>();

}
@Entity
@Data
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min=2, max=30, message = "제목은 2자 이상 30자 이하여야 합니다.")
    private String title;

    private String content;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

✔  @JsonIgnore

- Json 직렬화를 진행할 때, @JsonIgnore가 적용되어 있으면, 직렬화가 대상에서 제외된다.

  그래서 더 이상 순환참조가 발생하지 않게 된다.

 

하지만, User Entity에 @JsonIgnore 어노테이션만 추가해줬지만, 무시되지 않고 계속해서 순환참조가 발생했다.

 

 

✔  @JsonIdentityInfo

- @JsonIdentityInfo 어노테이션은 Jackson에게 직렬화/역직렬화하는 방법을 알려준다.

  직렬화/역직렬화 중에 객체 ID가 사용됨을 나타내는 데 사용된다.

- generator 속성: 객체의 identity 정보를 생성하는 데 사용할 클래스를 지정

- property 속성: identity 정보를 저장할 속성 이름 (default = "@id")

 

User Entity 자체에 @JsonIdentityInfo 어노테이션을 적용해준다.

 

 

 

다시 Postman으로 테스트를 진행하니, gildong에 대한 정보와 gildong이가 쓴 Boards 리스트까지 잘 불러와졌다!

 

 

 

 


+) 참고 사이트

https://www.toptal.com/javascript/bidirectional-relationship-in-json

 

Bidirectional Relationship Support in JSON | Toptal®

Ever tried to create a JSON data structure that includes entities with bidirectional relationships? If you have, you know that this often results in errors or exceptions being thrown. In this article, Toptal Freelance Software Engineer Nirmel Murtic provid

www.toptal.com

https://wildeveloperetrain.tistory.com/265

 

양방향 매핑 순환참조 문제 Cannot call sendError() after the response has been committed

양방향 매핑 순환참조 문제 해결 방법(@JsonIgnore, @JsonManagedReference, @JsonBackReference, @JsonIdentityInfo) "Cannot call sendError() after the response has been committed" //User Entity @NoArgsConstructor @Getter @Entity public class User

wildeveloperetrain.tistory.com

https://www.tutorialspoint.com/jackson_annotations/jackson_annotations_jsonidentityinfo.htm

 

Jackson Annotations - @JsonIdentityInfo

Jackson Annotations JsonIdentityInfo - @JsonIdentityInfo is used when objects have parent child relationship. @JsonIdentityInfo is used to indicate that object identity will be used during serialization/de-serialization.

www.tutorialspoint.com

 

Comments