유저 등록을 한 후 로그인을 받고 토큰을 받은걸 AUthorization에 토큰값을 넣어준 뒤 로그인 된 유저 정보 바탕으로 단건 다건 조회가 된다. 근데 post에서 request 정보 넣고 send를 했지만 해당 메세지가 뜬다.
Could not write JSON: failed to lazily initialize a collection of role: com.sparta.newsfeed19.user.User.followerList: could not initialize proxy - no Session
"이 에러 메시지는 Hibernate와 관련된 Lazy Loading 문제로, Session이 없는 상태에서 지연 로딩(Lazy Loading)된 엔티티를 초기화하려고 할 때 발생합니다. 구체적으로는 User 엔티티의 followerList 컬렉션을 조회하려고 할 때, 해당 세션이 이미 닫혀 있어서 발생한 문제입니다." 라는 오류 발생이유라길래 follow와 post에 EAGER로 바꿔도 문제 해결이 되지 않았다.
status 500이 뜨지만 게시물 등록은 된다.
알아보니 .yml에서 open-in-view를 false에서 true로 바꾸면서 세션이 종료되는 것을 방지한다.
(허나 이 방법은 임시방편이지 완벽한 해결법은 아니다.)
일단 open-in-view의 목적은...
open-in-view 설정의 목적
- Lazy Loading 지원: JPA에서는 연관된 엔티티를 필요할 때만 로드하는 Lazy Loading을 지원한다. 그러나, Lazy Loading이 발생하는 시점에 세션이 종료되면, 연관된 데이터를 로드할 수 없게 되어 LazyInitializationException이 발생할 수 있다. open-in-view는 이러한 문제를 방지하기 위해 요청 처리 동안 세션을 열어두어 Lazy Loading을 지원한다.
- 편리한 데이터 접근: 데이터베이스 세션이 요청의 전체 처리 과정 동안 열려 있으므로, 뷰 템플릿에서 데이터 접근이 더 간편해집니다. 이는 특히 Thymeleaf나 JSP와 같은 뷰 템플릿에서 Lazy Loaded 데이터에 접근할 때 유용합니다.
만일 true로 값을 변경 후 게시물 등록을 할때 순환 참조가 일어나는 경우가 있다
문제의 원인은..
- Lazy Loading과 세션 관리: open-in-view를 true로 설정하면 요청 처리 동안 데이터베이스 세션이 열려 있어 Lazy Loading이 가능하다. 하지만, 이 설정이 순환 참조 문제를 해결하는 것은 아니다. 순환 참조는 JSON 직렬화 과정에서 발생할 수 있으며, 이는 엔티티 간의 상호 참조로 인해 JSON 응답이 무한히 반복될 수 있다.
- 순환 참조 문제: 순환 참조 문제는 엔티티 간에 서로를 참조하는 경우 발생한다. 예를 들어, User 엔티티가 Post를 참조하고, Post가 다시 User를 참조하는 경우 JSON 직렬화 시 무한 루프가 발생할 수 있다.
그래서 순환참조를 방지하기 위해!
DTO (Data Transfer Object) 사용: DTO를 사용하여 필요한 데이터만 포함시키고, 순환 참조를 방지할 수 있다. DTO는 데이터베이스 엔티티와는 별도로 설계되어, 필요한 정보만을 클라이언트에게 전달할 수 있다. 이를 통해 순환 참조 문제를 효과적으로 해결할 수 있다.
DTO 사용 예시
다음은 순환 참조 문제를 해결하기 위해 필요한 정보만 포함한 DTO를 사용하는 예시이다:
public class PostUpdateResponseDto {
private final Long id;
private final User user
private final String email;
private final String title;
private final String content;
private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;
public PostUpdateResponseDto(
Long id,
User user,
String title,
String content,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
this.id = id;
this.user = user;
this.title = title;
this.content = content;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
}
해당 입력되어 있는 코드처럼 User에 대한 정보를 다 가져오면 user 엔티티에 있는 정보가 계속 참조 된다. 그래서
일부만 참조하게
package com.sparta.newsfeed19.post.dto.response;
import com.sparta.newsfeed19.user.User;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
public class PostSaveResponseDto {
private final Long id;
private final String email;
private final String title;
private final String contents;
private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;
public PostSaveResponseDto(
Long id,
User user,
String title,
String contents,
LocalDateTime createdAt,
LocalDateTime updatedAt
){
this.id = id;
this.email = user.getEmail();
this.title = title;
this.contents = contents;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
}
이 DTO는 User 객체의 email 정보만을 포함하고 있으며, User 객체 전체를 포함하지 않는다. 이를 통해 JSON 직렬화 과정에서 발생할 수 있는 순환 참조 문제를 방지할 수 있다.
3줄 요약
- open-in-view 설정은 Lazy Loading 문제를 해결할 수 있지만, 순환 참조 문제를 해결하지는 않는다.
- 순환 참조를 방지하기 위해 DTO를 사용하여 필요한 데이터만을 포함시키는 것이 바람직하다.
- DTO를 활용하면 데이터베이스 엔티티와 클라이언트 간의 명확한 데이터 전송 계약을 정의할 수 있으며, 순환 참조 문제를 효과적으로 해결할 수 있다.
'Spring > Spring 트러블 슈팅' 카테고리의 다른 글
채팅방 목록 순환참조 문제 (0) | 2024.11.16 |
---|---|
Spring Port 8080 is already in use (0) | 2024.08.15 |