[싱글이의 영수증] 상세 조회

업데이트:

싱글이의 영수증 상세 조회

  • 출력화면

image



상세조회 이동 script

게시글 클릭 시 목록의 숨겨진 게시글 번호를 통해서 해당 번호와 일치하는 게시글의 상세조회 페이지로 이동한다.

<script>
$(".viewdetail").on("click", function() {
    var boardNo = $(this).children("span#boardNo").text();

    var boardViewURL = "${contextPath}/review/" + boardNo;

    location.href = boardViewURL;
});
</script>

controller

해당 게시글 번호에 맞는 게시글 정보들을 조회해오고, 조회에 성공하면 조회수를 증가시킨다.

게시글 정보가 성공적으로 출력이 되면 조회수 상위 3개의 게시글 목록을 조회해오고, 그것에 해당하는 썸네일도 불러온다.

@RequestMapping("{boardNo}")
public String reviewView(@PathVariable("boardNo") int boardNo, Model model, @RequestHeader(value="referer",required=false) String referer,
                        RedirectAttributes ra, @ModelAttribute(name="loginMember", binding=false) Member loginMember) {
    
    Review review = service.selectReview(boardNo);
    
    String url = null;
    
    if(review!=null) { // 상세조회 성공시
        
        // 조회수 상위3  게시글 출력
        List<Review> reviewList = service.reviewListTop3();
        
        // 조회수 상위3 썸네일
        if(reviewList!=null && !reviewList.isEmpty()) {
            /* 썸네일 출력 */
            List<ReviewAttachment> thumbnailList = service.selectThumbnailList(reviewList);
    
            
            if(thumbnailList!=null) {
                model.addAttribute("thList", thumbnailList);
            }
        }
        
        // 좋아요 정보 출력
        int memberNo = loginMember.getMemberNo();
        
        List<ReviewLike> likeInfo = service.selectLike(memberNo);
        
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("memberNo", memberNo);
        map.put("boardNo", boardNo);
        
        int like = service.selectLikePushed(map);
        
        
        model.addAttribute("review",review);
        model.addAttribute("reviewList",reviewList);
        model.addAttribute("likeInfo",likeInfo);
        model.addAttribute("like", like);
        
        url = "review/reviewView";
    }else {
        
        if(referer == null) { //이전 요청주소가 없는 경우
            url = "redirect:../list";
        }else {
            url="redirect:"+referer;
        }
        
        ra.addFlashAttribute("swalicon","error");
        ra.addFlashAttribute("swalTitle","존재하지 않는 게시글입니다.");
    }
    
    return url;
}



  • 게시글 상세 조회

Service

Review selectReview(int boardNo);

ServiceImpl

@Transactional(rollbackFor = Exception.class)
@Override
public Review selectReview(int boardNo) {

    Review review = dao.selectReview(boardNo);

    if (review != null) {
        int result = dao.increaseReadCount(boardNo);

        if (result > 0) {
            review.setReadCount(review.getReadCount() + 1);
        }
    }
    return review;
}

DAO

public Review selectReview(int boardNo) {

    return sqlSession.selectOne("reviewMapper.selectReview",boardNo);
}

Mapper

<select id="selectReview" parameterType="_int" resultMap="review_rm">
    SELECT * FROM V_BOARD
    WHERE STATUS = 'N'
    AND BOARD_NO = #{boardNo}
    AND BOARD_CD = '2'
</select>



  • 조회 수 증가

DAO

public int increaseReadCount(int boardNo) {
    return sqlSession.update("reviewMapper.increaseReadCount", boardNo);
}

Mapper

<select id="increaseReadCount" parameterType="_int">
    UPDATE BOARD SET
    READ_COUNT = READ_COUNT + 1
    WHERE BOARD_NO = #{boardNo}
</select>



  • 조회수 TOP3 게시글 조회

Service

List<Review> reviewListTop3();

ServiceImpl

@Override
public List<Review> reviewListTop3() {
    return dao.reviewListTop3();
}

DAO

public List<Review> reviewListTop3() {
    return sqlSession.selectList("reviewMapper.reviewListTop3");
}

Mapper

<select id="reviewListTop3" resultMap="review_rm">
    SELECT * FROM
    (SELECT ROWNUM RNUM, R.* FROM
    (SELECT * FROM V_BOARD
    WHERE STATUS='N'
    AND BOARD_CD = '2'
    ORDER BY READ_COUNT DESC) R)
    WHERE RNUM BETWEEN 1 AND 3
    ORDER BY READ_COUNT DESC
</select>



  • 조회 수 TOP3 썸네일 조회

Service

List<ReviewAttachment> selectThumbnailList(List<Review> rList);

ServiceImpl

@Override
public List<ReviewAttachment> selectThumbnailList(List<Review> rList) {
    return dao.selectThumbnailList(rList);
}

DAO

public List<ReviewAttachment> selectThumbnailList(List<Review> rList) {
    return sqlSession.selectList("reviewMapper.selectThumbnailList",rList);
}

Mapper

<select id="selectThumbnailList" parameterType="list" resultMap="attachment_rm">
    SELECT * FROM BOARD_FILE
    WHERE FILE_LEVEL = 1
    AND PARENT_BOARD_NO IN
    <foreach collection="list" item="review" open="(" close=")"	separator=",">
        #{review.boardNo}
    </foreach>
</select>



  • 좋아요 정보 조회

Service

List<ReviewLike> selectLike(int memberNo);

ServiceImpl

@Override
public List<ReviewLike> selectLike(int memberNo) {
    return dao.selectLike(memberNo);
}

DAO

public List<ReviewLike> selectLike(int memberNo) {
    return sqlSession.selectList("reviewMapper.selectLike",memberNo);
}

Mapper

<select id="selectLike" parameterType="_int" resultMap="like_rm">
    SELECT
    BOARD_NO, MEM_NO
    FROM BOARD_LIKE
    WHERE MEM_NO = #{memberNo}
</select>



  • 좋아요 여부 조회

Service

int selectLikePushed(Map<String, Integer> map);

ServiceImpl

@Override
public int selectLikePushed(Map<String, Integer> map) {
    return dao.selectLikePushed(map);
}

DAO

public int selectLikePushed(Map<String, Integer> map) {
    return sqlSession.selectOne("reviewMapper.selectLikePushed",map);
}

Mapper

<select id="selectLikePushed" parameterType="map" resultType="_int">
    SELECT COUNT(*) FROM BOARD_LIKE
    WHERE MEM_NO = #{memberNo}
    AND BOARD_NO = #{boardNo}
</select>



  • 데이터 화면에 뿌려주기

CSS

<style>
.boardInfo {
	display: inline-block;
	margin-right: 15px;
}

.image {
	width: 30px;
	height: 30px;
}

/* 좋아요/댓글 */
.viewArea, .replyArea {
	display: inline-block;
	font-size: 11px;
	margin-right: 5px;
}

#likeBtn {
	border: 0px solid #ddd;
	background-color: rgba(255, 255, 255, 0);
}

.likeCnt {
	color: #6c757d;
}

.like {
	background-image: url('${contextPath}/resources/img/like2.png');
	background-repeat: no-repeat;
}


.like2 {
	background-size : 15px;
	background-image: url('${contextPath}/resources/images/like2.png');
	background-repeat: no-repeat;
}
.like3 {
	background-size : 11px;
	background-image: url('${contextPath}/resources/images/like2.png');
	background-repeat: no-repeat;
}

/* TOP3 출력 */
body {
	background: #f4f4f4;
}

.boardName {
	margin-right: 40px;
}

.card-img-top {
	height: 15rem;
}

.categoryArea, .arrayArea {
	display: inline-block;
}

.category, .array {
	text-decoration: none;
	color: black;
	line-height: 54px;
	margin-right: 5px;
}

/* 제목 */
.text-dark {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: bold;
}

/* 좋아요/댓글 */
.viewArea, .replyArea {
	display: inline-block;
	font-size: 11px;
	margin-right: 5px;
}

.nickNameArea {
	clear: both;
}

.icon {
	width: 13px;
}

/* 인기게시글 */
.viewdetail:hover {
	cursor: pointer;
}

/******* 페이징 *******/
.flex {
	-webkit-box-flex: 1;
	-ms-flex: 1 1 auto;
	flex: 1 1 auto
}

#page-content {
	margin-top: 20px;
}

.pagination, .jsgrid .jsgrid-pager {
	display: flex;
	padding-left: 0;
	list-style: none;
	border-radius: 0.25rem
}

.page-item>a, .page-item>a:hover {
	color: black;
}

.pagination.pagination-rounded-flat .page-item {
	margin: 0 .25rem
}

.pagination-success .page-item.active .page-link {
	background: #00c689;
	border-color: #00c689
}

.pagination.pagination-rounded-flat .page-item .page-link {
	border: none;
	border-radius: 50px;
}


</style>



JSP

  • 목록으로 주소 조합
<c:set var="referer" value="${header.referer}" />
<c:set var="a" value="${fn:indexOf(referer,'/')}" />
<c:set var="l" value="${fn:length(referer)}" />
<c:set var="referer" value="${fn:substring(referer, a+2, l)}" />

<c:set var="a" value="${fn:indexOf(referer,'/')}" />
<c:set var="l" value="${fn:length(referer)}" />
<c:set var="referer" value="${fn:substring(referer, a, l-1)}" />

<c:if test="${referer == contextPath}">
    <c:set var="returnListURL" value="list" />
</c:if>



  • 목록 버튼, 본인의 게시글이 아닐 경우 신고 버튼
<span class="bn" style="visibility: hidden">${review.boardNo}</span>
<a type="button" class="btn btn-primary ml-2 returnBtn maincolor" href="${returnListURL }" >목록</a>
<c:if test="${loginMember.memberNo != review.memberNo }">
    <button type="button" class="btn btn-primary ml-2 report maincolor">
        신고
    </button>
</c:if>



  • 카테고리, 제목
<h2 style="margin-top: 5px;">
    <div class='badge badge-danger px-3 rounded-pill font-weight-normal' style='
                <c:if test="${review.categoryCode == '21'}">background-color: burlywood;</c:if>
                <c:if test="${review.categoryCode == '22'}">background-color: #8dbe88;</c:if>
                <c:if test="${review.categoryCode == '23'}">background-color: #5d8eb6d5;</c:if>
                <c:if test="${review.categoryCode == '24'}">background-color: #d48a9a;</c:if> '>${review.categoryName }</div>
    ${review.boardTitle }
</h2>



  • 아이콘, 닉네임
<div class="boardInfo" id="writer" >
    <c:if test="${review.memeberGrade=='F' }">
            <img class="image" src="${contextPath}/resources/images/grade1.png"/>
    </c:if>
    <c:if test="${review.memeberGrade=='S' }">
            <img class="image" src="${contextPath}/resources/images/grade2.png" />
    </c:if>
    <c:if test="${review.memeberGrade=='T' }">
            <img class="image" src="${contextPath}/resources/images/grade3.png" />
    </c:if>
        ${review.nickName }
</div>



  • 작성일 (오늘이면 시간, 오늘이 아니면 날짜)
<div class="boardInfo" id="createDt" style="color: gray">
    <jsp:useBean id="now" class="java.util.Date" />
    <fmt:formatDate var="createDate" value="${review.createDate }" pattern="yyyy-MM-dd" />
    <fmt:formatDate var="today" value="${now }" pattern="yyyy-MM-dd" />
    <c:choose>
        <c:when test="${createDate != today }">
                ${createDate }
            </c:when>
        <c:otherwise>
            <fmt:formatDate value="${review.createDate}" pattern="HH:mm" />
        </c:otherwise>
    </c:choose>
</div>



  • 조회 수, 좋아요 수

좋아요 버튼의 이미지는 좋아요 여부의 반환값이 1인 경우 class에 like2를 추가해준다.

<div class="infoArea float-right">
    <i class="far fa-eye"></i> ${review.readCount }
        
    <!-- 좋아요  -->
    <span>
        <button type="button" id="likeBtn" class="likeBtns" >
            <img src="${contextPath}/resources/images/like1.png" 
            width="15" height="15" id="heart" class='likeImgs 
            <c:if test="${like == 1}">like2</c:if>'>
            <span class="likeCnt">${review.likeCount}</span>
        </button>					</span>
</div>


  • 좋아요 클릭 시 상태에 따라 증가,감소 AJAX

좋아요 버튼을 클릭 할 경우 AJAX를 통해 비동기적으로 좋아요 상태를 변경해준다.
만약 크래스 배열 중 1번째 인덱스의 값이 like2라면 (좋아요가 눌러져 있는 경우) like1로 지정되어 있던 클래스 이름을 like2로 대입한다
만약 클래스가 like2가 아닌 경우 클릭시 좋아요 증가 메서드를 실행
만약 클래스가 like2인 경우 클릭 시 좋아요 감소 메서드를 실행

<script>
    $(".likeBtns").on("click", function(){
    var boardNo = Number($(this).parents('.no').prev().find("span.bn").text());
    var likeClassArray = $(this).children().attr('class').split(" ");
    var likeClass = "like1";
    var likeImg = $(this).children(".likeImgs");
    var likeCnt = $(this).children(".likeCnt");

    if(likeClassArray[1] == "like2") {
        likeClass = "like2"; 
    }
    
    if(!$(this).children("img").hasClass("like2")) {
        $.ajax({
            url : "increaseLike",
            type : "post",
            data : {"boardNo" : boardNo},
            success : function(result){
                if(result > 0) {
                    likeCnt.text(Number(likeCnt.text()) + 1);
                    likeImg.toggleClass("like2");
                }
            }, 
            error : function(result){
                console.log("ajax 통신 오류 발생");
            }
        });
    } else{
        $.ajax({
            url : "decreaseLike",
            type : "post", 
            data : {"boardNo" : boardNo},
            success : function(result){
                if(result > 0){ // 삭제 성공
                    likeCnt.text(Number(likeCnt.text()) - 1);
                    likeImg.removeClass("like2");
                }
            },
            error : function(result){
                console.log("ajax 통신 오류 발생");
            }
        });
    }
});
</script>



  • 좋아요 증가 AJAX

Controller

@ResponseBody
@RequestMapping("increaseLike")
public int increaseLike(@RequestParam("boardNo") int boardNo, @ModelAttribute(name="loginMember", binding=false) Member loginMember) {
    
    int memberNo = loginMember.getMemberNo();
    
    Map<String, Object> map = new HashMap<String,Object>();
    map.put("memberNo", memberNo);
    map.put("boardNo", boardNo);
    
    int result = service.increaseLike(map);
    
    return result;
}

Service

int increaseLike(Map<String, Object> map);

ServiceImpl

@Transactional(rollbackFor = Exception.class)
@Override
public int increaseLike(Map<String, Object> map) {
    return dao.increaseLike(map);
}

DAO

public int increaseLike(Map<String, Object> map) {
    return sqlSession.insert("reviewMapper.increaseLike", map);
}

Mapper

<insert id="increaseLike" parameterType="map">
    INSERT INTO BOARD_LIKE
    VALUES (#{memberNo},#{boardNo})
</insert>



  • 좋아요 감소 AJAX

Controller

@ResponseBody
@RequestMapping("decreaseLike")
public int decreaseLike(@RequestParam("boardNo") int boardNo,
        @ModelAttribute("loginMember") Member loginMember) {
    
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("memberNo", loginMember.getMemberNo());
    map.put("boardNo", boardNo);
    
    int result = service.decreaseLike(map);
    return result;
}

Service

int decreaseLike(Map<String, Object> map);

ServiceImpl

@Transactional(rollbackFor = Exception.class)
@Override
public int decreaseLike(Map<String, Object>map) {
    return dao.decreaseLike(map);
}

DAO

public int decreaseLike(Map<String, Object> map) {
    return sqlSession.delete("reviewMapper.decreaseLike", map);
}

Mapper

<delete id="decreaseLike" parameterType="map">
    DELETE FROM BOARD_LIKE
    WHERE MEM_NO = #{memberNo}
    AND BOARD_NO = #{boardNo}
</delete>



  • 게시글 내용
<div class="row">
    <div class="col-md-12 contentArea">${review.boardContent }</div>
</div>



  • 댓글 JSP 연결
<jsp:include page="reviewReply.jsp"/>



  • 수정,삭제 버튼
<c:if test="${loginMember.memberNo == review.memberNo }">
    <div class="row float-right mt-3">
        <div class="col-md-12">
            <div class="row">
                <div class="col-md-12">
                    <button type="button" id="updateBtn" class="btn btn-success maincolor-re">수정</a>
                    <button type="button" id="deleteBtn" class="btn btn-danger maincolor-re">삭제</button>
                </div>
            </div>
        </div>
    </div>
</c:if>


<>
$("#deleteBtn").on("click", function(){
    if(confirm("삭제 하시겠습니까?")){
        
        location.href = "${contextPath}/review/${review.boardNo}/delete";
    }
});

$("#updateBtn").on("click",function(){
    location.href= "${contextPath}/review/${review.boardNo}/update";
});
</>



  • 목록으로 버튼
<div class="row  py-3" style="clear: both;">
    <div class="col-md-12 text-center ">
        <c:if test="${empty sessionScope.returnListURL }">
            <c:set var="returnListURL" value="../" scope="session" />
        </c:if>
        <a type="button" class="btn btn-success returnBtn maincolor" href="${returnListURL }" style="width: 100px; height: 40px;" >목록으로</a>
    </div>
</div>


<script>
	$(".returnBtn").on("click", function(){
		location.href = "${sessionScope.returnListURL}"
	});
<script>



  • TOP3 게시글
<h7 style="font-weight:bold;">싱글이의 영수증 인기 게시글</h7>
<hr>
<div class="row" style="margin-bottom: 25px;">

    <c:if test="${!empty reviewList }">
        <c:forEach var="review" items="${reviewList}" varStatus="vs">

            <!-- Gallery item -->
            <div class="col-xl-4 col-lg-4 col-md-6 mb-4">
                <div class="bg-white rounded shadow-sm viewdetail">
                    <!-- 썸네일 영역 -->
                    <div class="embed-responsive embed-responsive-4by3">
                        <c:set var="flag" value="true" />
                        <c:forEach var="thumbnail" items="${thList}">
                            <c:if test="${review.boardNo == thumbnail.parentBoardNo }">
                                <img src="${contextPath}${thumbnail.filePath}/${thumbnail.fileName}" class="img-fluid card-img-top embed-responsive-item">
                                <c:set var="flag" value="false" />
                            </c:if>
                        </c:forEach>

                        <c:if test="${flag=='true'}">
                            <img src="${contextPath}/resources/images/ReviewNonImages.png" class="img-fluid card-img-top embed-responsive-item">
                        </c:if>
                    </div>

                    <div class="p-4">
                        <h5>
                            <a class="text-dark">${review.boardTitle }</a>
                        </h5>
                        <div class="infoArea">
                            <div class="viewArea float-left">
                                <jsp:useBean id="now2" class="java.util.Date" />
                                    <fmt:formatDate var="createDate" value="${review.createDate }" pattern="yyyy-MM-dd" />
                                    <fmt:formatDate var="today" value="${now2 }" pattern="yyyy-MM-dd" />
                                    <c:choose>
                                        <c:when test="${createDate != today }">
                                                ${createDate }
                                            </c:when>
                                        <c:otherwise>
                                            <fmt:formatDate value="${review.createDate}" pattern="HH:mm" />
                                        </c:otherwise>
                                    </c:choose>
                            </div>								
                            <div class="viewArea mb-2 float-right">
                                <img class="icon" src="${contextPath}/resources/images/view.png" /> ${review.readCount }
                                    <span>
                                            <img src="${contextPath}/resources/images/like1.png" 
                                            width="11" height="11" id="heart" class='likeImgs 
                                            <c:forEach var="like" items="${likeInfo}">
                                            <c:if test="${like.boardNo == review.boardNo}">like3</c:if>
                                            </c:forEach>'>
                                            <span class="likeCnt" style="font-size:12px;">${review.likeCount}</span>
                                        </button>					
                                    </span>
                            </div>

                        </div>
                        <div class="nickNameArea d-flex  align-items-center justify-content-between rounded-pill bg-light px-3 py-2 mt-4">
                            <p class="small mb-0">
                                <span class="font-weight-bold price">${review.nickName }</span>
                            </p>
                            <div class='badge badge-danger px-3 rounded-pill font-weight-normal' style='
                    <c:if test="${review.categoryCode == '21'}">
                    background-color: burlywood;
                    </c:if>
                    <c:if test="${review.categoryCode == '22'}">
                    background-color: #8dbe88;
                    </c:if>
                    <c:if test="${review.categoryCode == '23'}">
                    background-color: #5d8eb6d5;
                    </c:if>
                    <c:if test="${review.categoryCode == '24'}">
                    background-color: #d48a9a;
                    </c:if> 
                    '>${review.categoryName }
                    </div>
                        </div>
                    </div>
                    <span id="boardNo" style="visibility: hidden">
                    ${review.boardNo }
                    </span>
                </div>
            </div>

        </c:forEach>
    </c:if>
</div>


<>
$(".viewdetail").on("click",function(){
    var boardNo = $(this).children("span#boardNo").text();
    
    var boardViewURL = "${contextPath}/review/"+boardNo;
    
    location.href = boardViewURL;
});
</>



댓글남기기