구현
CommentDto에 해당하는 UserDto를 가져오고자 G_Comment 테이블과 G_User 테이블을 조인
CommentDto Class
package com.gld.model.dto;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity(name="G_COMMENT")
@IdClass(CommentId.class)
public class CommentDto {
@Id
@Column(name="SEQ")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long seq;
@Id
@Column(name="ID")
private Integer id;
@Id
@Column(name="COMMENT_DATE")
private LocalDate commentDate;
@Column(name="COMMENT")
private String comment;
@Column(name="ISDONE")
private String isDone;
// table join (G_USER 일대다 G_COMMENT)
@ManyToOne
@JoinColumn(name = "ID", insertable = false, updatable = false)
private UserDto userDto;
// getter&setter&constructor
}
UserDto Class
package com.gld.model.dto;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.gld.model.repository.RegisteredMemberRepository;
@Entity
@Table(name = "G_USER")
public class UserDto {
@Id
@Column(name = "ID",nullable = false, unique = true)
private Long id;
// table join (G_USER 일대다 REGISTERED_MEMBER)
@OneToMany(mappedBy = "userDto", fetch = FetchType.LAZY)
private List<RegisteredMemberDto> list = new ArrayList<>();
//table join
@OneToMany(mappedBy = "userDto")
private List<CommentDto> list2 = new ArrayList<>();
@Column(name = "USER_ID", nullable = false, unique = true)
private String userId;
@Column(name = "USER_PW", nullable = false)
private String userPw;
@Column(name = "USER_NAME", nullable = false)
private String userName;
@Column(name = "COMPLETED_CHALLENGE", nullable = false)
private int completedChallenge;
@Column(name = "ONOFF_NOTY", nullable = false)
private String onOffNoty;
@Column(name = "USER_LOGINTYPE", nullable = false)
private String userLoginType;
@Column(name = "USER_PHONE", nullable = false)
private String userPhone;
@Column(name = "USER_BIRTH", nullable = false)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date userBirth;
// getter&setter&constructor
}
Challenge Controller
package com.gld.model.controller;
//import 생략
@Controller
@RequestMapping("/challenge")
public class ChallengeController {
@Autowired
private LoginBiz loginBiz;
@Autowired
private ChallengeBiz challengeBiz;
@Autowired
private RegisteredBiz registeredBiz;
@Autowired
private CommentBiz commentBiz;
@PostMapping("/ajaxComment")
@ResponseBody
public Map<String, Object> commentDate(@RequestBody CommentId commentid) {
System.out.println(commentid.getSeq()+" " +commentid.getId()+" "+commentid.getCommentDate());
Map<String, Object> res = new HashMap<>();
CommentDto comment = commentBiz.selectComment(commentid.getSeq(), commentid.getId(), commentid.getCommentDate());
List<CommentDto> list = commentBiz.selectComments(commentid.getSeq(), commentid.getCommentDate());
int i=0;
List<String> users = new ArrayList<>();
for(CommentDto rmDto : list) {
users.add(rmDto.getUserDto().getUserId());
System.out.println(users.get(i));
i++;
}
Map<String, CommentDto> map = new HashMap<>();
if(comment != null) {
map.put("comment", comment);
}else {
map.put("comment", null);
}
res.put("comment", map);
if(list.size() != 0) {
res.put("list", list);
}else {
res.put("list", null);
}
if(users.size() != 0) {
res.put("users", users);
}else {
res.put("users", null);
}
System.out.println(res);
return res;
}
}
문제 상황
Cannot call sendError() after the response has been committed
해당 오류는 JAVA에 Json 타입 변환 과정 중 일어난 오류이다. 이는 테이블과 테이블이 연관 관계에 있으며 데이터를 Front로 보낼때 Json으로 변환 과정중에 무한으로 참조가 순환되어 발생된 오류이다.
문제 해결
JPA 순환 반복이 CommentDto에서 일어난 듯 하다. CommentDto에는 하위 연관 관계가 당연히 추가로 있으며 이 때문에 참조에 참조를 반복하다가 JSON이 오류가 난 듯 싶다. Spring은 Front로 데이터를 보낼 때 Json으로 보내야하는 상황이면 Jackson을 통해 Json 형태로 변환하는데 순환구조일 경우 에러가 떠버린다.
// table join (G_USER 일대다 G_COMMENT)
@JsonIgnore
@ManyToOne
@JoinColumn(name = "ID", insertable = false, updatable = false)
private UserDto userDto;
@JsonIgnore을 사용하면 해당 필드는 직렬화 또는 역직렬화 대상에서 제외되므로, 해당 필드에 저장된 정보는 JSON 데이터로 변환되지 않는다. 즉, 위의 코드처럼 해당 연관관계 매핑 부분에 @JsonIgnore을 붙여주면 순환 참조 관계를 막을 수 있다.
@JsonIgnore은 Jackson 라이브러리에서 사용되는 어노테이션으로, JSON 직렬화 또는 역직렬화 시에 특정 필드를 제외하고 처리하고자 할 때 사용되므로 해당 어노테이션으로 문제를 해결하였다.
'Spring' 카테고리의 다른 글
[Spring Security] 비밀번호 암호화 (0) | 2023.06.12 |
---|---|
[Spring + JPA] No Property Found for Type Exception 에러 해결 (2) | 2023.06.03 |
[Spring + Jpa] 로그인 구현하기 & 문제해결 (0) | 2023.04.29 |