한 게시물에 댓글이 여러개 달려 있고, 각 댓글은 0~n개의 이미지를 첨부할 수 있다고 해보겠습니다. 게시물-댓글, 댓글-댓글이미지 테이블이 각각 1:n (one-to-many) relationship을 갖고 있습니다.

쿼리를 이용해 특정 id를 갖는 게시물의 댓글 목록 및 각 댓글별 이미지까지 불러오려면 어떻게 할까요? JOIN으로 연결하면 되겠죠. 이건 뭐 어렵지 않습니다.

문제는 이걸 JdbcTemplate에서 어떻게 자동으로 DTO(or VO)에다가 매핑을 시켜주냐는 것인데요, 위 예시를 예로 들면 DTO가 세개 나옵니다. 게시물(Post), 댓글(Comment), 댓글 이미지(CommentImage) 이런식으로 나올 수 있겠습니다

public class Post {
	private List<Comment> comments;
	// ...
}

public class Comment {
	private List<CommentImage> commentImages;
	// ...
}

public class CommentImage {
	private int imageId;
	private String fileName;
	// ...
}

그런데 DAO를 만들다보니 저같은 스프링 뉴비는 고민에 빠지게 됩니다.

"이걸 어떻게 RowMapper에서 매핑시키지?"

RowMapper 인터페이스는 row 단위 매핑만 해주는데, 조인을 하면 여러 행이 한 객체 안에 들어가는 식이 되기 때문에 매핑이 안됩니다.

@Override
public List<Comment> getAllCommentsOfDisplay(int displayInfoId) {
	List<Comment> comments = commentDao.getAllCommentsOfDisplay(displayInfoId);
	for (Comment c : comments) {
		c.setCommentImages(commentImageDao.getCommentImages(c.getCommentId()));
	}
	return comments;
}

가장 단순해보이는 해법은 서비스에서 Post의 id로 Comment 목록을 갖고오고, 각 Comment의 id를 이용해 CommentImage를 갖고와서 직접 붙여주는 이런식으로 조인을 사용하지 않고 한 행씩 만들어가는 것입니다.

문제는 쿼리가 여러번 실행되기 때문에 너무 비효율적입니다. N+1 Problem이라고도 하는데 왜 이렇게 되냐면 Post의 id를 이용해 Comment 목록을 갖고오는 쿼리 1번, 그리고 각 Comment마다 CommentImage 목록을 갖고오는 쿼리가 N번 실행되니 총 N+1번의 쿼리가 실행되는 겁니다. 당연히 비효율적이고 느립니다

List<Comment> comments = new ArrayList<>();
Comment currentComment = null;
while (rs.next()) {
	long id = rs.getLong("id");
	if (currentComment == null) { // initial object
		currentComment = mapComment(rs);
	} else if (currentComment.getId() != id) { // break
		comments.add(currentComment);
		currentComment = mapComment(rs);
	}
	currentComment.addImage(mapImage(rs));
}
if (currentComment != null) { // last object
	users.add(currentComment);        
}

그 다음 고려해볼 것은 RowMapper 대신 ResultSetExtractor를 만들어서 ResultSetExtractor를 직접 만들어볼 수 있겠습니다. 그럴듯 해 보입니다.

하지만 ResultSet을 직접 다루고 있고 Mapper를 직접 구현해줘야 합니다. 또 Join 중첩이 늘어나면 이런 과정을 여러번 더 반복해줘야 합니다. 여러모로 번거롭습니다. 유지보수도 어렵습니다.

 

결론?

이런 문제점을 해결하기 위해 나온 것이 ORM(Object Relational Mapping)으로 객체와 관계형 DB를 매핑해주는 기술입니다. 이런걸 찾아보시는걸 추천합니다

다음 찾아볼 키워드는 Mybatis, JPA, Hibernate, QueryDSL입니다. 아직 이건 하나도 안써봤기 때문에 각각 뭐 하는건진 모릅니다..; 공부 후 글을 써볼 예정입니다

만약 ORM을 도입하지 않을 예정이다, 죽어도 못쓴다! 하는 분은 SimpleFlatMapper라는 라이브러리를 사용해보시기 바랍니다.

 

참고자료

반응형

'프로그래밍 > Java' 카테고리의 다른 글

JdbcTemplate Nested Object를 가져오려면?  (0) 2020.11.23
IoC / DI란  (0) 2020.11.22
[Maven] JSTL 설정 방법  (0) 2020.10.29
[Java] JSP란  (0) 2020.10.29
[Maven] Dependency Missing artifact 오류 발생시  (0) 2020.10.28