KoreaIt Academy/Spring Boot

[Spring Boot] JS모듈화(Ajax, callback)를 통한 REST Controller와의 통신

hongeeii 2021. 10. 15.
728x90
반응형

댓글 RestController

package com.example.board.controller;

import com.example.board.beans.vo.Criteria;
import com.example.board.beans.vo.ReplyVO;
import com.example.board.services.ReplyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.UnsupportedEncodingException;
import java.util.List;

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/replies/*")
public class ReplyController {

    private final ReplyService replyService;

//    댓글 등록
//    브라우저에서 JSON타입으로 데이터를 전송하고 서버에서는 댓글의 처리 결과에 따라 문자열로 결과를 리턴한다.
//    consumes : Ajax를 통해 전달받은 데이터의 타입
//    produces : Ajax의 success:function(result)에 있는 result로 전달할 데이터 타입
//    @ResponseBody : @Controller에서 REST API를 구현하기 위해서 사용된다.

//    문자열을 전달할 때 한글이 깨지지 않게 하기 위해서는 text/plain; charset=utf-8을 작성한다.
//    ResponseEntity : 서버의 상태 코드, 응답 메세지 등을 담을 수 있는 타입이다.
   @PostMapping(value="/new", consumes="application/json", produces = "text/plain; charset= utf-8")
    public ResponseEntity<String> create(@RequestBody ReplyVO replyVO) throws UnsupportedEncodingException{

       int replyCount = replyService.register(replyVO);
       log.info("ReplyVO : "+  replyVO);
       log.info("REPLY INSERT COUNT  : "+ replyCount);

       return replyCount==1?
               new ResponseEntity<>(new String("댓글등록 완료".getBytes(),"utf-8"), HttpStatus.OK) :
               new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
   }

//   스프링 부트는 ResponseEntity를 무조건 써야하나 자동으로 바꿔줌 List->ResponseEntity
//   게시글 댓글 전체 조회
    @GetMapping("pages/{bno}/{page}")
    public List<ReplyVO> getList(@PathVariable("bno") Long bno, @PathVariable("page") int page){
       log.info("getList/.................");
       Criteria criteria = new Criteria(page,10);
       log.info(criteria.toString());
       return replyService.getList(bno, criteria);
    }

//    댓글 조회
//    URI에 댓글 번호만 작성한다.
//    전달받은 rno를 JSON으로 리턴한다.
    @GetMapping("{rno}")
    public ReplyVO get(@PathVariable("rno") Long rno){
       log.info("get......................");
       return replyService.get(rno);
    }

//    댓글 수정
//    PUT : 자원의 전체 수정, 자원 내 모든 필드를 전달해야 함, 일부만 전달할 경우 오류
//    PATCH : 자원의 일부 수정, 수정할 필드만 전송(자동 주입이 아닌 부분만 수정하는 쿼리문에서 사용)
//    PATCH가 PUT을 담고 있기 때문에 전체를 전달 받아서 전체를 수정하는 상황, 전체 중 부분만 수정하는 상황 모두 PATCH를 사용하는 것이 좋다.
    @RequestMapping(method={RequestMethod.PUT, RequestMethod.PATCH}, value="{rno}" ,
            consumes = "application/json", produces = "text/plain; charset=utf-8")
    public ResponseEntity<String> modify(@RequestBody ReplyVO replyVO, @PathVariable("rno") Long rno) throws UnsupportedEncodingException{

       replyVO.setRno(rno);
       return replyService.modify(replyVO) == 1 ? new ResponseEntity<>(new String("댓글 수정성공".getBytes(),"utf-8"),HttpStatus.OK) :
                                                new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

//  댓글 삭제
//    URI로 댓글 번호를 전달 받은 후 성공 시 댓글 삭제 성공 전달
    @DeleteMapping(value = "{rno}" , produces = "text/plain; charset=utf-8")
    public ResponseEntity<String> remove(@PathVariable("rno") Long rno) throws UnsupportedEncodingException{
       return replyService.remove(rno) == 1 ? new ResponseEntity<>(new String("댓글 삭제 성공".getBytes(),"utf-8"),HttpStatus.OK) :
                                                new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

 

js 모듈화

//Ajax모듈화-> 사용부의 코드가 깔끔해 집니다.

/*
 함수들을 하나의 모듈처럼 묶음으로 구성하는 것을 의미
 화면 내에서 Javascript 처리를 하다 보면 이벤트 처리와 DOM, Ajax 처리 등 복잡하게 섞여서 유지보수가 힘들다.
 따라서 javascript를 하나의 모듈처럼 구성하여 사용한다.


*/

console.log("Reply Module.....");
//일회성으로 사용되는 함수
let replyService = (function () {

    //callback, error는 외부에서 전달받을 함수이다.
    //함수의 파라미터 개수를 일치시킬 필요가 없기 때문에
    //사용시 callback이나 error와 같은 파라미터는 상황에 따라 작성한다.

    //댓글 등록
    function add(replyInfo, callback, error) {
        console.log("add reply................");
        $.ajax({
            url: "/replies/new",
            type: "post",
            data: JSON.stringify(replyInfo),// 전달할 JSON데이터에서 문자열 처리가 필요한 것들(key, dateType)을 자동으로 처리해준다.
            contentType: "application/json; charset=utf-8",
            // dataType: 생략가능 자동으로 됨
            success: function (result, status, xhr) {
                if (callback) {
                    //외부에서 전달받은 값이 있다면 결과를 해당 함수의 매개변수로 전달하여 사용한다.
                    callback(result);
                }
            },
            error: function (xhr, status, err) {
                if (error) {
                    error(err);
                }
            }
        });
    }


    //댓글 삭제
    function remove(rno, callback, error) {
        console.log("remove reply........");
        $.ajax({
                url: "/replies/" + rno,
                type: "delete",
                data: rno,
                success: function (result) {
                    if (callback) {
                        callback(result);
                    }
                },
                error: function (xhr, status, err) {
                    if (error) {
                        error(err);
                    }
                }
            }
        );
    }

    //댓글 수정
    function modify(replyInfo, callback, error) {
        console.log("MODIFY reply........");
        $.ajax({
            url: "/replies/" + replyInfo.rno,
            type: "patch",
            data: JSON.stringify(replyInfo),
            contentType: "application/json; charset=utf-8",
            success: function (result) {
                if (callback) {
                    callback(result);
                }
            },
            error: function (xhr, status, err) {
                if (error) {
                    error(err);
                }
            }

        });
    }


    //댓글 목록
    function getList(params, callback, error) {
        console.log("list...........");
        // let date = x | y : x가 없으면 y
        let page = params.page || 1;
        // get방식으로 요청 후 JSON으로 응답
        // $.getJSON(url, callback).fail(callback)
        //리턴값을 json으로 바꿔줌
        $.getJSON(
            "/replies/pages/" + params.bno + "/" + page,
            function (list) {
                if (callback) {
                    callback(list);
                }
            }
        ).fail(function (xhr, status, err) {
            if (error) {
                error(err);
            }
        });
    }


    // 댓글 하나 조회
    function get(rno, callback, error) {
        $.get(
            "/replies/" + rno,
            function (replyInfo) {
                if (callback) {
                    callback(replyInfo);
                }
            }
        ).fail(function (xhr, status, err) {
            if (error) {
                error(err);
            }
        });
    }



    return {add: add, remove: remove, modify: modify, getList: getList}
//    외부에서는 replyService.add(객체, 콜백)형식으로 사용하며,
//    Ajax 호출이 감춰져 있기 떄문에 사용부의 코드가 더 깔끔해진다.
})();

 

 

사용부(html)

    //이벤트 위임
    //작성된 HTML에는 이벤트 처리가 가능하지만,
    //DOM에서 새롭게 추가되는 HTML에는 이벤트가 반영되지 않는다.
    //따라서 미리 작성해놓은 HTML에 이벤트를 반영한 후,
    //해당 자식 태그를 선택하여 이벤트 위임을 진행해야 한다.
    $("div.paging").on("click", "a.changePage", function(e){
        e.preventDefault();
        pageNum = parseInt($(this).attr("href"));
        showList(pageNum);
    });

    function showList(page){
        replyService.getList({bno:bno, page:page || 1},
            function(replyCnt, list){
                if(list == null || list.length == 0){
                    replyUL.html("댓글이 없습니다.");
                    return;
                }
                let str = "";
                for(let i=0, len=list.length; i<len; i++){
                    str += "<li style='display: block' data-rno='" + list[i].rno + "'>"
                    str += "<strong>" + list[i].replier + "</strong>"
                    str += "<div>"
                    str += "<p class='reply" + list[i].rno +"'>" + list[i].reply + "</p>"
                    str += "<p><strong class='date'>" + replyService.displayTime(list[i].replyDate);
                    if(list[i].replyDate != list[i].updateDate){
                        str += "<br>수정된 날짜 " + replyService.displayTime(list[i].updateDate);
                    }
                    str += "</strong></p></div>"
                    str += "<div style='text-align: right'><a class='modify' href='" + list[i].rno + "'>수정</a>"
                    str += "<a class='finish' style='display: none' href='" + list[i].rno + "'>수정완료</a>"
                    str += "&nbsp;&nbsp;<a class='remove' href='" + list[i].rno + "'>삭제</a>"
                    str += "</div></li>"
                    str += "<div class='line'></div>"
                }
                replyUL.html(str);
                showReplyPage(replyCnt);
            }
        );
    }

    let check = false;

    $(".replies").on("click", "a.modify", function(e){
        e.preventDefault();
        let rnoValue = $(this).attr("href");
        $("p.reply" + rnoValue).html("<textarea class='" + rnoValue + "'>" + $("p.reply" + rnoValue).text() + "</textarea>")
        $(this).hide();

        let arFinish = $(".finish");
        for(let i=0; i<arFinish.length; i++){
            if($(arFinish[i]).attr("href") == rnoValue){
                $(arFinish[i]).show();
                check = true;
                break;
            }
        }
    });

    $(".replies").on("click", "a.finish", function(e){
        e.preventDefault();

        let rnoValue = $(this).attr("href");
        let newReply = $("." + rnoValue).val();

        if(newReply == ""){return;}

        replyService.update({rno:rnoValue, reply:newReply},
            function(result){
                alert(result);
                check = false;
                showList(pageNum);
            }
        )
    })

728x90
반응형

추천 글