반응형
순환 참조 란?
- 1:1 연관관계 매핑 예제 참고
- Author라는 객체와 Book이라는 객체가 있다고 생각해보자
- 둘은 1:1 관계로 매핑되어 있음
- Author은 name, age 정보 포함
- Book은 name, price 정보 포함
- Book을 저장할 때 책의 저자(Author)도 같이 저장
- 이 상황에서 아래와 같이 Book을 조회한다면 아래와 같이 출력됨 (StackOverflow Error 발생)
@GetMapping("/book/{bookId}")
public Book showBook(@PathVariable Long bookId) {
return bookRepository.findById(bookId).get();
}
- 이렇게 출력되는 이유는 1번 Book 조회시 book1에 저장되어 있는 이름, 가격, author1을 출력
- 이 때 author1의 정보를 출력하는데, author1의 이름, 나이, book1을 출력
- book1이 출력한 author1이 출력한 book1을 출력 ....
- 이와 같이 Book과 Author가 서로 참조하기 때문에 무한 루프(순환 참조 문제)가 발생함
해결방법 1 - @JsonIgnore 사용
- @JsonIgnore 어노테이션을 통해 Json 타입으로 출력될 때 Author은 출력하지 않음
- 간편하게 순환 참조 문제를 해결할 수 있지만, Author에 대한 정보를 출력할 수 없다는 단점 존재
@JsonIgnore
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
결과
해결방법 2 - @JsonManagedReference, @JsonBackReference 사용
- @JsonManagedReference는 부모 클래스에 @JsonBackReference는 자식 클래스에 적용
- 이 예제에서는 Book = 자식, Author = 부모
- 적용하는 위치와 결과는 @JsonIgnore과 같지만 @JsonIgnore은 JSON 타입 출력할 때 값을 nul는로 바꿔 출력해주는 반면 @JsonManagedReference, @JsonBackReference는 순환참조를 대비하기 위해 만들어진 어노테이션이라 생각하면 됨
// Book
@JsonBackReference
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
// Author
@OneToOne(mappedBy = "author",fetch = FetchType.LAZY)
@JsonManagedReference
private Book book;
결과
해결방법 3 - DTO (Data Transfer Object) 사용 (추천)
- 이 방법은 Book 객체 자체를 출력하는 대신 BookDto 클래스를 생성 후 원하는 데이터만 추출해서 넣어주고 BookDto를 출력하는 방법
- BookDto를 활용해서 Author을 출력하는 대신 Author의 name만 출력해 보자
- 이 방법이 가장 좋은 방법이라 생각함
// BookDto
@Getter
@AllArgsConstructor
public class BookDto {
private Long id;
private String name;
private int price;
private String authorName;
public static BookDto of(Book book) {
return new BookDto(book.getId(), book.getName(), book.getPrice(), book.getAuthor().getName());
}
}
- 위와 같이 BookDto를 생성하는 코드를 BookDto에 static으로 생성해주면 나중에 BookDto를 생성할 때 사용하기 편함
@GetMapping("/book/{bookId}")
public BookDto showBook(@PathVariable Long bookId) {
Book book = bookRepository.findById(bookId).get();
return BookDto.of(book);
}
- 아까와는 달리 Book을 return하지 않고 BookDto를 return
- BookDto의 of를 사용
- bookId를 통해 찾은 Book을 넘겨주면 BookDto로 변환해서 return 해주는 메소드
결과
- DTO를 활용하면 DTO 클래스를 만들어줘야 하는 귀찮음이 있을 수 있지만, 필요한 정보만 추출해 출력하는데 유리함
- ex) Book에 대한 정보를 출력할 때 어떤 요청에서는 책의 이름과 가격만 출력해도 되는데 다른 요청에서는 책의 이름, 가격, 저자 이름, 저자 나이 까지 출력해야 된다면 DTO를 따로 만들어서 return 하는 방식 사용 가능
해결방법 4 - 양방향 매핑을 단방향 매핑으로 수정
- 현재는 두 객체가 양방향으로 매핑되어 있음
- 이 방법은 엔티티가 반대쪽 엔티티를 참조할 일이 없을 때 단방향으로 매핑을 하는 방법
- 순환참조가 일어날 일은 없지만, 나중에 반대 방향의 참조가 일어날 것인지 잘 생각해보고 적용해야 함
반응형
'Spring Boot > 문법 정리' 카테고리의 다른 글
[Spring Boot] Swagger 3.0 적용 (0) | 2022.07.09 |
---|---|
[Spring Boot] JpaRepository를 사용한 CRUD 예제 (0) | 2022.07.08 |
[Spring Boot] Entity Manager을 사용한 MySQL CRUD 예제 (1) | 2022.07.01 |
[Spring Boot] JPA 관련 개념 정리 (0) | 2022.07.01 |
[Spring Boot] 파일 업로드 (6) | 2022.06.29 |