[Spring][쇼핑몰 프로젝트][47] 댓글 페이징 - 2
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188
목표
댓글 페이징 기능 구현
저번 포스팅에 이어서 이번 포스팅에선 댓글 페이징 뷰 측 구현을 목표로 합니다.
순서
1. 만들어낼 태그
2. 동적 태그 생성(Javascript)
3. 메서드 추출
1. 만들어낼 태그
저번 포스팅에서 말했듯이 서버로부터 반환받은 댓글 페이지 정보를 활용해서 동적으로 태그를 만들어 내야 합니다.
댓글을 만들어 내는 Javascript 코드 작업해 주기 전, 동적으로 어떠한 태그를 만들어 줄지 정적으로 먼저 만들어 보겠습니다.
"content_bottom" <div> 태그 내부에 작성해둔 <c:if> 태그 바로 다음 공간에 아래의 태그 코드를 작성해줍니다.
- "reply_not_div" <div> 태그에는 댓글이 없는 경우 '댓글이 없습니다' 문구가 삽입될 태그입니다.
- "reply_content_ul" <ul> 태그에는 댓글이 존재하는 경우 댓글 '페이징 댓글 정보'가 삽입될 태그입니다.
- "repy_pageInfo_div" <div> 태그에는 댓글 페이지 버튼이 삽입될 태그입니다.
<div class="reply_not_div">
</div>
<ul class="reply_content_ul">
</ul>
<div class="repy_pageInfo_div">
</div>
"reply_content_ul" 태그 내부에 아래의 코드를 추가해줍니다.
<li>
<div class="comment_wrap">
<div class="reply_top">
<span class="id_span">sjinjin7</span>
<span class="date_span">2021-10-11</span>
<span class="rating_span">평점 : <span class="rating_value_span">4</span>점</span>
<a class="update_reply_btn">수정</a><a class="delete_reply_btn">삭제</a>
</div>
<div class="reply_bottom">
<div class="reply_bottom_txt">
사실 기대를 많이하고 읽기시작했는데 읽으면서 가가 쓴것이 맞는지 의심들게합니다 문체도그렇고 간결하지 않네요 제가 기대가 크던 작았던간에 책장이 사실 안넘겨집니다.
</div>
</div>
</div>
</li>
"repy_pageInfo_div" 태그 내부에 아래의 코드를 추가해줍니다.
<ul class="pageMaker">
<li class="pageMaker_btn prev">
<a>이전</a>
</li>
<li class="pageMaker_btn">
<a>1</a>
</li>
<li class="pageMaker_btn">
<a>2</a>
</li>
<li class="pageMaker_btn active">
<a>3</a>
</li>
<li class="pageMaker_btn next">
<a>다음</a>
</li>
</ul>
서버를 구동시켜서 의도대로 출력이 되는지 확인합니다.
정상적으로 출력이 되는 것을 확인했고 "reply_content_ul", "pageMaker" 태그 내부에 작성 한 코드를 지우거나 주석 처리합니다. 지워준 태그 부분을 동적으로 만들어 줄 것입니다.
2. 동적 태그 생성(Javascript)
'상품 상세 페이지(goodsDetail.jsp)' 사용자가 이동했을 때 댓글 리스트가 출력되어 있어야 하기 때문에, '상품 상세 페이지'가 렌더링 될 때 동작하는 "$(document).ready(function(){" 메서드에 댓글 리스트를 출력하는 메서드를 작성하겠습니다.
본래 서버에 요청을 하기 위해서 ajax 코드를 작성해야 합니다. 하지만 JQUERY에서는 단순히 JSON 데이터를 GET 메서드 방식으로 서버에 요청할 때 간편히 사용할 수 있는 getJOSN() 메서드를 제공하고 있습니다. 이 메서드를 사용해보겠습니다.
getJSON 메서드의 파라미터에 대해 간단히 설명하면 다음과 같습니다.
- 첫 번째 파라미터 : 요청 URL
- 두 번째 파라미터 : 서버에 전송할 데이터
- 세 번째 파라미터 : 서버로부터 응답을 성공했을 때 동작할 코드
댓글 리스트 정보를 요청하고 반환받는 getJSON 메서드를 작성합니다.
- 세 번째 인자로 작성한 함수는 서버로부터 응답을 받았을 때 동작하는 코드입니다. 이 함수의 파라미터는 서버가 반환 해준 '댓글 리스트 정보(JSON 데이터)'입니다.
const bookId = '${goodsInfo.bookId}';
$.getJSON("/reply/list", {bookId : bookId}, function(obj){
});
댓글 없는 경우
먼저 댓글 정보가 없을 경우의 태그를 동적으로 생성하는 코드를 작성해보겠습니다. if문을 사용하여 댓글 데이터가 없는 경우 동작할 코드를 작성합니다.
- '댓글 리스트 정보'는 ReplyPagDTO객체가 JSON으로 변환된 데이터입니다. JSON은 Jvascript 객체 리터럴 문법을 따르고 있기 때문에 그 자체로 Jvascript 객체입니다.
- 따라서 객체의 프로퍼티 접근 방식으로 '댓글 리스트 정보'의 list(댓글 정보)와 pageInfo (페이지 정보)에 접근할 수 있습니다.
※ Javascript 객체 기본적 사용법, 용어 "모던자바스크립트" 참고
- list프로퍼티의 값에 아무 데이터가 없을 경우 댓글이 없는 경우 이기 때문에 ". length"속성을 사용하여 데이터가 있는지 없는지를 판단하였습니다.
- 동적으로 생성하는 코드는 ".reply_not_div" <div> 태그 내부에 댓글이 없다는 문구가 추가되도록 하였습니다.
- ".reply_content_ul", "pageMaker" 태그 내부의 데이터를 모두 지우도록 하는 코드를 추가하였습니다. (작성 중인 댓글 리스트를 동적으로 만드는 코드는 '회원이 댓글을 추가했을 때', '수정, 삭제했을 때' 도 댓글 최신화를 위해 그대로 사용할 것인데 기존의 남아 있는 태그들을 지워 주기 위함입니다.)
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
}
댓글 있는 경우
앞서 작성한 if문 이외의 상황(length가 0이 아닌 경우)은 댓글 데이터가 있는 경우입니다. 따라서 else를 추가하여 else의 구현부에 댓글이 있는 경우 태그를 동적으로 추가하는 코드를 작성하겠습니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
}
먼저 댓글이 없을 경우의 문구가 출력되어 잇는 ".reply_not_div" <div> 태그 내부 코드를 지워줍니다. 그리고 서버로부터 반환받은 '댓글 리스트 정보(obj)'에 있는 list, pageInfo 프로퍼티의 데이터를 쉽게 꺼내 쓰기 위해서 상수를 선언 후 해당 프로퍼티들을 대입해줍니다.
$.getJSON("/reply/list", {bookId : bookId}, function(obj){
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
}
회원 id도 사용할 것이기 대문에 상수를 추가해주고 초기화해줍니다.
$.getJSON("/reply/list", {bookId : bookId}, function(obj){
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
}
먼저 "reply_content_ul" <ul> 태그에 추가될 댓글 리스트 정보를 작업해주겠습니다. 댓글 리스트 태그 문자열이 저장될 reply_list" 변수를 선언한 후 빈 문자열로 초기화합니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
}
변수 list에 담긴 데이터들은 List<ReplyDtO>가 JSON형식으로 변했기 때문에 Javascript 배열 객체가 들어 있습니다. list에 담긴 배열 길이만큼 순환하여 동작하는 Jquery의 each 메서드를 작성합니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
});
}
each 구현부에는 list배열을 순환하면서 만들어내야 할 태그 문자들을 reply_list변수에 추가해주는 코드를 작성합니다.
- 추가하는 코드의 틀은 우리가 앞서 정적으로 만들어 보았던 "reply_content_ul" 태그 내부의 코드입니다.
- 내부에 if문이 작성되어 있는데 로그인한 회원과 댓글 아이디 가 일치할 때 '수정', '삭제' 버튼이 보이게 하기 위함입니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
reply_list += '<li>';
reply_list += '<div class="comment_wrap">';
reply_list += '<div class="reply_top">';
/* 아이디 */
reply_list += '<span class="id_span">'+ obj.memberId+'</span>';
/* 날짜 */
reply_list += '<span class="date_span">'+ obj.regDate +'</span>';
/* 평점 */
reply_list += '<span class="rating_span">평점 : <span class="rating_value_span">'+ obj.rating +'</span>점</span>';
if(obj.memberId === userId){
reply_list += '<a class="update_reply_btn" href="'+ obj.replyId +'">수정</a><a class="delete_reply_btn" href="'+ obj.replyId +'">삭제</a>';
}
reply_list += '</div>'; //<div class="reply_top">
reply_list += '<div class="reply_bottom">';
reply_list += '<div class="reply_bottom_txt">'+ obj.content +'</div>';
reply_list += '</div>';//<div class="reply_bottom">
reply_list += '</div>';//<div class="comment_wrap">
reply_list += '</li>';
});
}
each() 메서드가 다 순환되면 "reply_content_ul"태그 내부에 추가되어야 할 문자열 값들이 저장되어 있습니다. 이 문자열 값들을 "reply_content_ul"태그 내부에 추가해주는 코드를 작성합니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
reply_list += '<li>';
reply_list += '<div class="comment_wrap">';
reply_list += '<div class="reply_top">';
/* 아이디 */
reply_list += '<span class="id_span">'+ obj.memberId+'</span>';
/* 날짜 */
reply_list += '<span class="date_span">'+ obj.regDate +'</span>';
/* 평점 */
reply_list += '<span class="rating_span">평점 : <span class="rating_value_span">'+ obj.rating +'</span>점</span>';
if(obj.memberId === userId){
reply_list += '<a class="update_reply_btn" href="'+ obj.replyId +'">수정</a><a class="delete_reply_btn" href="'+ obj.replyId +'">삭제</a>';
}
reply_list += '</div>'; //<div class="reply_top">
reply_list += '<div class="reply_bottom">';
reply_list += '<div class="reply_bottom_txt">'+ obj.content +'</div>';
reply_list += '</div>';//<div class="reply_bottom">
reply_list += '</div>';//<div class="comment_wrap">
reply_list += '</li>';
});
$(".reply_content_ul").html(reply_list);
}
이번엔 댓글 페이지 번호 버튼을 만들어 내는 코드를 작성해보겠습니다. 먼저 페이지 번호 태그 문자열 값들을 저장할 reply_pageMaker를 변수로 선언하고 빈 문자열로 초기화합니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
reply_list += '<li>';
reply_list += '<div class="comment_wrap">';
reply_list += '<div class="reply_top">';
/* 아이디 */
reply_list += '<span class="id_span">'+ obj.memberId+'</span>';
/* 날짜 */
reply_list += '<span class="date_span">'+ obj.regDate +'</span>';
/* 평점 */
reply_list += '<span class="rating_span">평점 : <span class="rating_value_span">'+ obj.rating +'</span>점</span>';
if(obj.memberId === userId){
reply_list += '<a class="update_reply_btn" href="'+ obj.replyId +'">수정</a><a class="delete_reply_btn" href="'+ obj.replyId +'">삭제</a>';
}
reply_list += '</div>'; //<div class="reply_top">
reply_list += '<div class="reply_bottom">';
reply_list += '<div class="reply_bottom_txt">'+ obj.content +'</div>';
reply_list += '</div>';//<div class="reply_bottom">
reply_list += '</div>';//<div class="comment_wrap">
reply_list += '</li>';
});
$(".reply_content_ul").html(reply_list);
/* 페이지 버튼 */
let reply_pageMaker = '';
}
댓글 페이지 버튼 태그 문자열이 reply_pageMaker 변수에 저장되는 코드를 작성합니다.
- 기존 '페이지 버튼 태그'를 추가하기 위해 JSTL의 <c:if>와 <c:foreach>를 사용했었는데, 단지 앞의 조건문을 Javascript의 if와 for문으로 바꾸어 주었습니다.
$.getJSON("/reply/list", {bookId : bookId}, function(obj){
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
reply_list += '<li>';
reply_list += '<div class="comment_wrap">';
reply_list += '<div class="reply_top">';
/* 아이디 */
reply_list += '<span class="id_span">'+ obj.memberId+'</span>';
/* 날짜 */
reply_list += '<span class="date_span">'+ obj.regDate +'</span>';
/* 평점 */
reply_list += '<span class="rating_span">평점 : <span class="rating_value_span">'+ obj.rating +'</span>점</span>';
if(obj.memberId === userId){
reply_list += '<a class="update_reply_btn" href="'+ obj.replyId +'">수정</a><a class="delete_reply_btn" href="'+ obj.replyId +'">삭제</a>';
}
reply_list += '</div>'; //<div class="reply_top">
reply_list += '<div class="reply_bottom">';
reply_list += '<div class="reply_bottom_txt">'+ obj.content +'</div>';
reply_list += '</div>';//<div class="reply_bottom">
reply_list += '</div>';//<div class="comment_wrap">
reply_list += '</li>';
});
$(".reply_content_ul").html(reply_list);
/* 페이지 버튼 */
let reply_pageMaker = '';
/* prev */
if(pf.prev){
let prev_num = pf.pageStart -1;
reply_pageMaker += '<li class="pageMaker_btn prev">';
reply_pageMaker += '<a href="'+ prev_num +'">이전</a>';
reply_pageMaker += '</li>';
}
/* numbre btn */
for(let i = pf.pageStart; i < pf.pageEnd+1; i++){
reply_pageMaker += '<li class="pageMaker_btn ';
if(pf.cri.pageNum === i){
reply_pageMaker += 'active';
}
reply_pageMaker += '">';
reply_pageMaker += '<a href="'+i+'">'+i+'</a>';
reply_pageMaker += '</li>';
}
/* next */
if(pf.next){
let next_num = pf.pageEnd +1;
reply_pageMaker += '<li class="pageMaker_btn next">';
reply_pageMaker += '<a href="'+ next_num +'">다음</a>';
reply_pageMaker += '</li>';
}
}
페이지 버튼 태그 문자열이 저장된 reply_pageMaker를 ".pageMaker" <ul> 태그에 추가하는 코드를 작성합니다.
if(obj.list.length === 0){
$(".reply_not_div").html('<span>리뷰가 없습니다.</span>');
$(".reply_content_ul").html('');
$(".pageMaker").html('');
} else{
$(".reply_not_div").html('');
const list = obj.list;
const pf = obj.pageInfo;
const userId = '${member.memberId}';
/* list */
let reply_list = '';
$(list).each(function(i,obj){
reply_list += '<li>';
reply_list += '<div class="comment_wrap">';
reply_list += '<div class="reply_top">';
/* 아이디 */
reply_list += '<span class="id_span">'+ obj.memberId+'</span>';
/* 날짜 */
reply_list += '<span class="date_span">'+ obj.regDate +'</span>';
/* 평점 */
reply_list += '<span class="rating_span">평점 : <span class="rating_value_span">'+ obj.rating +'</span>점</span>';
if(obj.memberId === userId){
reply_list += '<a class="update_reply_btn" href="'+ obj.replyId +'">수정</a><a class="delete_reply_btn" href="'+ obj.replyId +'">삭제</a>';
}
reply_list += '</div>'; //<div class="reply_top">
reply_list += '<div class="reply_bottom">';
reply_list += '<div class="reply_bottom_txt">'+ obj.content +'</div>';
reply_list += '</div>';//<div class="reply_bottom">
reply_list += '</div>';//<div class="comment_wrap">
reply_list += '</li>';
});
$(".reply_content_ul").html(reply_list);
/* 페이지 버튼 */
let reply_pageMaker = '';
/* prev */
if(pf.prev){
let prev_num = pf.pageStart -1;
reply_pageMaker += '<li class="pageMaker_btn prev">';
reply_pageMaker += '<a href="'+ prev_num +'">이전</a>';
reply_pageMaker += '</li>';
}
/* numbre btn */
for(let i = pf.pageStart; i < pf.pageEnd+1; i++){
reply_pageMaker += '<li class="pageMaker_btn ';
if(pf.cri.pageNum === i){
reply_pageMaker += 'active';
}
reply_pageMaker += '">';
reply_pageMaker += '<a href="'+i+'">'+i+'</a>';
reply_pageMaker += '</li>';
}
/* next */
if(pf.next){
let next_num = pf.pageEnd +1;
reply_pageMaker += '<li class="pageMaker_btn next">';
reply_pageMaker += '<a href="'+ next_num +'">다음</a>';
reply_pageMaker += '</li>';
}
$(".pageMaker").html(reply_pageMaker);
}
REFERENCE
DATE
- 2020.01.12