Spring 3주차. Lv.5과제에 추가기능을 구현했다. 회원탈퇴, 대댓글 기능을 구현했는데 일단은 무한 대댓글이 아닌 글-댓글-대댓글 정도의 계층까지만 구현했다. 구현까지는 쉬웠지만 그후 순환에러가 다시 발생했다. 해당 문제에 대해 정리해보려 한다.
문제 : 대댓글 기능을 구현한 후, 대댓글을 달면 대댓글의 갯수만큼 댓글이 PostResponseDto에서 늘어난다. 물리적 DB 테이블에서는 문제가 없었지만 로그로 찍어보니
@OneToMany로 달아둔 Post의 List<Comment> 에서 대댓글의 갯수만큼 댓글이 늘어나는 현상이 있었다.
시도 : 일단 댓글작성이나 대댓글 작성시 생성자에 양방향으로 값을 주는 것 때문이라 판단하여 양방향으로 주는 코드를 없에 봤지만 해결되지 않았다.
양방향 연관관계를 끊고 단방향으로 시도해보려 했으나 Dto에서 Repository를 불러와 다시 정렬하는 것은 원하던 구현 방향이 아니라 시도하지 않았다.
해결 : 여러 방법을 시도하던 중 Post에서 @OneToMany로 받은 리스트의 fetchtype을 EAGER에서 LAZY로 바꿨더니 해결되었다.
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
private List<Comment> commentList = new ArrayList<>();
UserDetails는 영속성 컨텍스트 밖에 존재한다. Filter단에서 Authentication 인증 객체를 만드는 과정에서 생성된다. 반면 영속성 컨텍스트는 컨트롤러의 안쪽, 즉 서비스부터 서비스까지가 범위인데 UserDetails는 컨트롤러의 밖, 필터에서 나온 객체이다.
지연로딩 문제는 컨트롤러에서 UserDetails.getUser()로 서비스에 User 값을 넣어줬기 때문에 발생한 문제이다. 영속성 컨텍스트는 여기서 User를 뽑아 내면 지연로딩으로 처리 된 상태에서는 진짜 객체를 불러오지 못하고 proxy객체만 줄 수 있다. 그러므로 양쪽에 넣는 값을 지우던가, 아니면 UserDetails.getUser().getId()로 User를 Repository에서 새로 찾아서 넣어주면 된다.
동일한 유저로 인식불가문제는 UserDetails.getUser()와 User가 내부에 담고있는 값은 같더라도 실제로는 주소값이 다른 객체이기 때문이다.
.equals()는 @Override로 재정의 하지 않으면 오직 자기자신만 같다고 인식한다. 그래서 .equals()와 .hashcode()를 재정의해서 쓰던가(참고사이트), id끼리 비교하면 된다. 후자로 해도 검증절차에는 이상이 없다.
그리고 지금까지 User로 객체를 만들어써왔기때문에 너무 당연시하게 썼었는데, User 객체는 만들어 쓴거지 자바나 스프링에서 기본제공하는 개념이 아니다. UserDetails는 아래와 같이 User 말고 다른방식으로도 구현이 가능하다.
public class CustomUserDetails implements UserDetails {
private String ID;
private String PASSWORD;
private String AUTHORITY;
private boolean ENABLED;
private String NAME;