게시글 댓글 만들기

 

 

[details.jsp]

<!-- 게시글 댓글  -->
<div class="card">
    <div class="card-body"><textarea class="form-control" rows="1" ></textarea></div>
    <div class="card-footer"><button class="btn btn-primary">등록</button></div>
</div>

<div class="card">
    <div class="card-header">댓글 리스트</div>

    <ul id="reply--box" class="list-group">
        <li id="reply--1" class="list-group-item d-flex justify-content-between">
            <div>댓글 내용!</div>
            <div class="d-flex">
                <div class="font-italic">작성자 : user &nbsp;</div>
                <button class="badge">삭제</button>
            </div>
        </li>
    </ul>
</div>
  • &nbsp : 한 칸 띄어쓰기, 공백을 의미하는 특수문자
  • class="badge" : 크기를 작게 만듬

 

 

 

테스트로 DB에 직접 댓글 넣기

insert into reply(content, boardId, userId, createDate)
values('첫번재 댓글',15,2,now());
insert into reply(content, boardId, userId, createDate)
values('두번재 댓글',15,2,now());
insert into reply(content, boardId, userId, createDate)
values('세번재 댓글',15,2,now());
commit;

 

 

 

 

게시글 상세보기를 클릭했을때 댓글이 보이는거라서 BoardService 에서 처리하는데

findById 해서 Board 를 들고 BoardController 에서 받는다

 

 

 

1. 

[BoardService]

@Transactional(readOnly = true) 
public Board 글상세보기(int id){
    return boardRepository.findById(id)
            .orElseThrow(()->{
                return new IllegalArgumentException("글 상세보기 실패 : 아이디를 찾을 수 없습니다.");
            });
}

 

 

2. 

ReplyRepository 인터페이스 생성하기

public interface ReplyRepository extends JpaRepository<ReplyInteger>{   // Reply 의 id가 int니까 Integer

}

 

 

3.

[BoardController]

@GetMapping("/board/{id}")
public String findById(@PathVariable int id, Model model) {
    model.addAttribute("board",boardService.글상세보기(id));
    return "board/detail";
}

boardService.글상세보기(id) ▶ BoardService에서 findById 한 Board 를 들고 BoardController 에 받는다. 

Board 객체에는 reply를 들고있어서 뷰페이지(board/detail)로 리턴될때 reply를 반복문으로 forEach 해서 들고온다.

 

 

[board] 

reply 를 EAGER전략으로 패치해서 들고옴

@OneToMany(mappedBy = "board",fetch = FetchType.EAGER) 
@JsonIgnoreProperties({"board"})
private List<Reply> replys;

 

 

 

 

[details.jsp]

 

* model.addAttribute("board",boardService.글상세보기(id));

<!-- 게시글 댓글  -->
<div class="card">
    <div class="card-body"><textarea class="form-control" rows="1" ></textarea></div>
    <div class="card-footer"><button class="btn btn-primary">등록</button></div>
</div>

<div class="card">
    <div class="card-header">댓글 리스트</div>
    <ul id="reply--box" class="list-group">
        <c:forEach var="reply" items="${board.replys}">  
            <li id="reply--1" class="list-group-item d-flex justify-content-between">
                <div>${reply.content}</div>
                <div class="d-flex">
                    <div class="font-italic">작성자 : ${reply.user.username} &nbsp;</div>
                    <button class="badge">삭제</button>
                </div>
            </li>
        </c:forEach>
    </ul>
</div>

 

작성자는 ${reply.user.username} 

Reply 객체 안에 ▶ User 객체 ▶  User 객체 안에 username 

 

 

 

댓글들이 forEach문으로 나온다

댓글

 

 

하지만 여기까지는 문제가 없을지라도 나중에 무한참조의 문제가 생긴다. 

 

 

 

 

위처럼 board 객체를 select하면 board 객체에 reply, user 가 포함돼 있으니 자동으로 가져오고 

public class Board {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment
	private int id; 
	
	@Column(nullable = false, length = 100)
	private String title;
	
	@Lob // 대용량 데이터
	private String content; // 섬머노트 라이브러리 <html>태그가 섞여서 디자인이 됨.
	
	private int count; // 조회수
	
	@ManyToOne(fetch = FetchType.EAGER)  // Many = Many,  User = One
	@JoinColumn(name="userId")
	private User user; // DB는 오브젝트를 저장할 수 없다. FK, 자바는 오브젝트를 저장할 수 있다. 
	
	@OneToMany(mappedBy = "board", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) // mappedBy 연관관계의 주인이 아니다 (난 FK가 아니에요) DB에 칼럼을 만들지 마세요.
	@JsonIgnoreProperties({"board"})
	@OrderBy("id desc")
	private List<Reply> replys;
	
	@CreationTimestamp
	private LocalDateTime createDate;
}

 

 

가져온 reply 객체에는 board, user 가 있어서 또 중복되게 들고오는 무한참조의 문제가 생긴다

public class Reply {
	@Id //Primary key
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 프로젝트에서 연결된 DB의 넘버링 전략을 따라간다.
	private int id; // 시퀀스, auto_increment

	@Column(nullable = false, length = 200)
	private String content;
	
	@ManyToOne
	@JoinColumn(name="boardId")
	private Board board;
	
	@ManyToOne
	@JoinColumn(name="userId")
	private User user;
	
	@CreationTimestamp
	private LocalDateTime createDate;

	@Override
	public String toString() {
		return "Reply [id=" + id + ", content=" + content + ", board=" + board + ", user=" + user + ", createDate="
				+ createDate + "]";
	}
}

 

 


 

 

에러 StackOverflowError 

 

 

해결

오류메세지를 자세히 보니 @Data를 가르키고 있어서 

Board, User 엔티티에서 @Data 빼고 @Getter, @Setter 를 추가를 했더니 해결되었다. 

 

 


 

 

언제 문제가 되는지 무한참조 테스트하기

 

[ReplyControllerTest]

@RestController
public class ReplyControllerTest {

	@Autowired
	private BoardRepository boardRepository;
	
	@GetMapping("/test/board/{id}")
	public Board getBord(@PathVariable int id) {
		return boardRepository.findById(id).get();
	}
	
}

 

 

- 결과

리턴을 하면 무한참조가 되어버림.

* http://localhost:8000/test/board/15

 

 

이유는?? 

 

리턴할때 jackson 라이브러리가 발동하는데

( jackson 라이브러리가 발동한다는 것은 오브젝트를 json으로 리턴해준다는 말 )

그때 모델이 들고있는 getter를 호출하고 값들을 json으로 바꾼다.

 

 

@GetMapping("/test/board/{id}")
public Board getBord(@PathVariable int id) {
   return boardRepository.findById(id).get();
}

Board 객체가 실행되는거니까

 

Board 오브젝트 안에 있는

▷ getId, getTitle, getContent, getCount, getUser, getReply, getCreateDate 가 호출된다

 

 

getUser 를 호출하면 User 오브젝트가 실행.

▷ getId, getPassword, getEmail, getRole, getOauth, getCreateDate

= User는 연관관계가 없어서 깔끔하게 들고온다.

 

 

getReply 를 호출하면 Reply 오브젝트가 실행.

▷ getId, getContent, getBoard, getUser, getCreateDate

 

이때 다시 getBoard를 리턴하니까 또 Board 객체가 실행된다! 

 

 

그럼 또다시 Board  ▷ getId, getTitle, getContent, getCount, getUser, getReply, getCreateDate 리턴.

다시 Reply getId, getContent, getBoard, getUser, getCreateDa 호출... 이렇게 무한참조가 된다. 

 

 

 

 

다음 글에 무한참조 해결법..

반응형
LIST

+ Recent posts