Part 4) REST 방식과 Ajax를 이용하는 댓글 처리 (Ch 17-2)
* 이 카테고리의 글은 <코드로 배우는 스프링 웹 프로젝트> 에서 각 파트별로 진행하는 프로젝트의 흐름을 보기 위해 각 장의 내용을 간단히 요약한 것이다.
* 출처: 코드로 배우는 스프링 웹 프로젝트 개정판, 구멍가게 코딩단, 남가람북스
17.4 JavaScript 준비
- JSP 내에서 댓글에 대한 처리는 하나의 페이지 내에서 모든 작업이 이루어지기 때문에 조금 복잡하게 느껴지는 것이 사실이다. 또한 기존과 달리 JavaScript와 동적으로 만들어지는 HTML로 처리하기 때문에 각 단계를 주의해서 작성해야 한다.
17.4.1 JavaScript의 모듈화
- 화면에서 사용되는 jQuery는 막강한 기능과 다양한 플러그인을 통해서 많은 프로젝트에서 기본으로 사용된다.
- 특히 Ajax를 이용하는 경우에는 jQuery의 함수를 이용해서 너무나 쉽게 처리할 수 있기 때문에 많이 사용한다.
- 화면 내에서 JavaScript 처리를 하다보면 어느 순간 이벤트 처리와 DOM 처리, Ajax 처리 등이 마구 섞여서 유지보수 하기 힘든 코드를 만드는 경우가 많다.
- 이런 경우를 대비해서 좀 더 JavaScript를 하나의 모듈처럼 구성하는 방식을 이용하는 것이 좋다.
- JavaScript에서 가장 많이 사용하는 패턴 중 하나는 모듈 패턴이다.
- 모듈 패턴은 쉽게 말해서 관련 있는 함수들을 하나의 모듈처럼 묶음으로 구성하는 것을 의미한다. JavaScript의 클로저를 이용하는 가장 대표적인 방법이다.
- 예제 프로젝트의 webapp 내 resources 폴더에 js 폴더 내 reply.js 파일을 작성한다.
- 작성하는 reply.js는 아무 기능 없이 간단히 동작하는 코드만을 넣어서 확인하는 용도로 사용한다.
- reply.js 파일은 게시물의 조회 페이지에서 사용하기 위해서 작성된 것이므로, views/board/get.jsp 파일 내 이전에 만든 이벤트 처리 바로 위쪽으로 추가해둔다.
<!-- 게시물의 조회 페이지에서 사용하기 위해 작성된 reply.js 파일 추가 -->
<script type="text/javascript" src="/resources/js/reply.js"></script>
- reply.js 가 정상적으로 동작하는지를 확인하기 위해 브라우저에 '/board/get?bno=xxx'번호를 호출하면 reply.js가 실행되는지를 확인한다.
- 브라우저에서는 개발자 도구를 이용해서 reply.js가 아무 문제 없이 로딩되고 있는지 확인해야 하고, Console 메뉴에서는 console.log()의 내용이 실행되는지를 확인해야 한다.
1) 모듈 구성하기
- 모듈 패턴은 쉽게 말해서 Java의 클래스처럼 JavaScript를 이용해서 메서드를 가지는 객체를 구성한다.
- 모듈 패턴은 JavaScript의 즉시 실행함수와 '{ }'를 이용해서 객체를 구성한다. reply.js를 아래와 같이 수정하고 저장한다.
console.log("Reply Module.........");
var replyService = (function(){
return {name: "AAAA"};
})();;
- JavaScript의 즉시 실행함수는 ( )안에 함수를 선언하고 바깥쪽에서 실행해버린다. 즉시 실행함수는 함수의 실행 결과가 바깥쪽에 선언된 변수에 할당된다. 위의 코드에서는 replyService라는 변수에 name이라는 속성에 'AAAA'라는 속성값을 가진 객체가 할당된다.
- replyService의 확인은 reply.js를 사용하는 get.jsp를 이용해서 확인한다. 아래와 같이 개발자 도구 콘솔창에서 {name: 'AAAA'}가 잘 나와야 한다.
17.4.2 reply.js 등록 처리
- 모듈 패턴은 즉시 실행하는 함수 내부에서 필요한 메서드를 구성해서 객체를 구성하는 방식이다. 직접 코드로 보면 다음과 같다.
console.log("Reply Module.........");
var replyService = (function(){
function add(reply, callback){
console.log("reply.......");
}
return {add:add};
})();;
- 개발자 도구에서는 replyService 객체의 내부에는 add라는 메서드가 존재하는 형태로 보인다.
- 외부에서는 replyService.add(객체,콜백)를 전달하는 형태로 호출할 수 있는데, Ajax 호출은 감춰져 있기 때문에 코드를 좀 더 깔끔하게 작성할 수 있다.
- reply.js 내에 add 함수는 Ajax를 이용해서 POST 방식으로 호출하는 코드를 작성한다.
- add()에서 주의 깊게 봐야 하는 부분은 데이터 전송 타입이 'application/json;charset=utf-8' 방식으로 전송한다는 점과 파라미터로 callback과 error를 함수로 받을 것이라는 점이다.
- 만일 Ajax 호출이 성공하고, callback 값으로 적절한 함수가 존재한다면 해당 함수를 호출해서 결과를 반영하는 방식이다.
- JavaScript는 특이하게도 함수의 파라미터 개수를 일치시킬 필요가 없기 때문에 callback이나 error와 같은 파라미터는 필요에 따라서 작성할 수 있다. reply.js를 이용하는 get.jsp에서는 테스트를 위해 replyService.add()를 호출해본다.
- get.jsp 내부에서는 Ajax 호출은 replyService라는 이름의 객체에 감춰져 있으므로 필요한 파라미터들만 전달하는 형태로 간결해진다.
- replyService의 add()에 던져야 하는 파라미터는 JavaScript의 객체 타입으로 만들어서 전송해주고, Ajax 전송 결과를 처리하는 함수를 파라미터로 같이 전달한다.
- 프로젝트를 Tomcat에서 실행하고 결과를 확인해 보면 데이터베이스에는 정상적으로 댓글이 추가되어야 하고, 브라우저에서는 경고창이 보여야만 한다.
* 브라우저 경고창 확인
* SQL Developer에서 댓글 추가 확인
- 브라우저에서는 JSON 형태로 데이터가 전송되고 있는 것을 확인할 수 있어야 하고, 전송되는 데이터 역시 JSON 형태로 전송되는지 확인해야 한다.
- 그리고 서버에서는 (STS 콘솔창) JSON 데이터가 ReplyVO 타입으로 제대로 변환되는 것을 볼 수 있다.
17.4.3 댓글의 목록 처리
- 댓글 등록이 정상적으로 처리되었다면 다음 단계에서는 해당 게시물에 있는 댓글 전체 목록을 가져온다.
- 댓글 목록은 최종적으로는 페이징 처리가 되어야 하지만, 우선적으로는 전체 댓글을 가져오는 형태로 구현해본다.
- 프로젝트가 Tomcat 상에서 실행되고 있다면 'replies/pages/게시물번호/페이지번호' 혹은 '/replies/pages/게시물번호/페이지번호.json' 형태로 데이터를 먼저 확인할 수 있다.
1) getJSON()사용
- reply.js에서는 Ajax 호출을 담당하므로, jQuery의 getJSON()을 이용해서 처리할 수 있다.
- reply.js에 추가한 getList()는 param이라는 객체를 통해서 필요한 파라미터를 전달받아 JSON 목록을 호출한다. JSON 형태가 필요하므로 URL 호출 시 확장자를 '.json'으로 요구한다.
- 댓글 등록과 마찬가지로 get.jsp에서는 해당 게시물의 모든 댓글을 가져오는지 확인하는 코드를 작성한다.
- 그리고 웹 브라우저에서 실행해 개발자 도구 콘솔 창에서 정상적으로 동작하는지 확인한다.
17.4.4 댓글 삭제와 갱신
- 댓글 삭제는 DELETE 방식을 통해서 해당 URL을 호출하는 것뿐이므로 그다지 어려운 점은 없다.
- reply.js에서 remove() 라는 함수를 추가한다.
- remove()는 DELETE 방식으로 데이터를 전달하므로, $.ajax()를 이용해서 구체적으로 type 속성으로 'delete'를 지정한다.
- board/get.jsp에서는 반드시 실제 데이터베이스에 있는 댓글 번호를 이용해서 정상적으로 댓글이 삭제되는지를 확인한다.
- 삭제 전 데이터베이스와 삭제 후 데이터베이스를 비교해서 확인한다.
17.4.5 댓글 수정
- 댓글 수정은 수정하는 내용과 함께 댓글의 번호를 전송한다. 댓글의 내용은 JSON 형태로 전송하기 때문에 댓글 등록과 유사한 부분이 많다.
- reply.js에 댓글 수정 코드를 추가하고 get.jsp에도 댓글 수정 코드를 추가한다.
- 웹 브라우저 테스트 후 데이터베이스에서도 댓글 수정이 완료됐는지 확인한다.
17.4.6 댓글 조회 처리
- 특정 번호의 댓글 조회는 GET 방식으로 동작하므로 이를 고려하여 reply.js를 처리한다.
- get.jsp 에서는 단순히 댓글의 번호만을 전달한다.
17.5 이벤트 처리와 HTML 처리
- 앞의 과정을 그대로 진행했다면 이미 Ajax의 처리까지는 완료되는 것을 확인했다는 의미가 된다. 남은 작업은 화면에서 버튼 등에서 발생하는 이벤트를 감지하고, Ajax 호출의 결과를 화면에 반영하는 것이다.
17.5.1 댓글 목록 처리
- 댓글의 목록을 위해서는 별도의 <div>를 생성해서 처리해야한다. 게시글과 관련된 화면 아래쪽에 <div>를 추가한다.
- 추가하는 <div>에는 나중에 화면의 모습을 파악할 수 있도록 간단한 텍스트 등을 구성해둔다.
- 댓글의 목록은 <ul> 태그 내에 <li> 태그를 이용해서 처리한다. 각 <li>태그는 하나의 댓글을 의미하므로 수정이나 삭제 시 이를 클릭하게 된다.
- 수정이나 삭제 시에는 반드시 댓글 번호(rno)가 필요하므로 'data-rno' 속성을 이용해서 이를 처리한다.
1) 이벤트 처리
- 게시글의 조회 페이지가 열리면 자동으로 댓글 목록을 가져와서 <li> 태그를 구성해야 한다.
- 이에 대한 처리는 $(document).ready() 내에서 이루어 지도록 한다.
- showList()는 페이지 번호를 파라미터로 받도록 설계하고, 만일 파라미터가 없는 경우에는 자동으로 1페이지가 되도록 설정한다.
- 브라우저에서 DOM 처리가 끝나면 자동으로 showList()가 호출되면서 <ul> 태그 내에 내용으로 처리된다. 만일 1페이지가 아닌 경우라면 기존 <ul>에 <li> 들이 추가되는 형태이다.
2) 시간에 대한 처리
- XML이나 JSON 형태로 데이터를 받을 때는 순수하게 숫자로 표현되는 시간 값이 나오게 되어 있으므로, 화면에서는 이를 변환해서 사용하는 것이 좋다.
- 날짜 포맷의 경우 문화권마다 표기 순서 등이 다르기 때문에 화면에서 포맷을 처리하는 방식을 권장한다.
- 최근의 웹페이지들을 보면 해당일에 해당하는 데이터는 '시/분/초'를 보여주고, 전날에 등록된 데이터들은 '년/월/일'등을 보여주는 경우가 많다. 현재 시간을 기준으로 해서 화면에 내용이 달라지도록 하는 부분은 함수를 작성해서 사용할 수 있다. reply.js에 관련 기능을 함수로 추가한다.
17.5.2 새로운 댓글 처리
- 댓글 목록 상단에는 버튼을 하나 추가해서 사용자들이 새로운 댓글을 추가할 수 있도록 한다.
- 댓글의 추가는 모달창을 이용해서 진행한다. <script> 태그의 시작 전에 모달창 코드를 추가한다.
- 모달창은 브라우저에서 댓글에 대한 여러 작업(CRUD)에서 활용할 것이므로 필요한 모든 내용을 담도록 하고 각 작업에 맞게 버튼이나 입력창이 보이거나 감춰지도록 한다.
1) 새로운 댓글의 추가 버튼 이벤트 처리
- 댓글 목록 상단의 '댓글 등록'을 클릭하면 화면에서는 모달창을 띄운다.
- 모달과 관련된 객체들은 여러 함수에서 사용할 것이므로 바깥쪽으로 빼두어 매번 jQuery를 호출하지 않도록 한다.
2) 댓글 등록 및 목록 갱신
- 새로운 댓글의 추가는 필요한 댓글의 내용(Reply)과 댓글의 작성자(Replyer) 항목만으로 추가해서 모달창 아래쪽의 '등록'버튼을 클릭해서 처리한다.
- 댓글이 정상적으로 추가되면 경고창을 이용해서 성공했다는 사실을 알려주고, 등록한 내용으로 다시 등록할 수 없돋록 입력항목을 비우고 모달창을 닫아준다.
- 댓글이 정상적으로 처리되었지만 목록 자체는 갱신된 적이 없으므로 화면엔서 새로 등록된 댓글이 보이지 않는다. 이러한 문제 때문에 댓글을 추가한 후에는 다시 댓글의 목록 (showList(1))을 갱신할 필요가 있다.
- 기존의 코드에 showList!(1)을 추가해서 댓글이 추가된 후 그 사이에 추가되었을지 모르는 새로운 댓글도 가져오도록 한다.
17.5.3 특정 댓글의 클릭 이벤트 처리
- 댓글은 댓글의 목록에서 내용이 모두 출력되기 때문에 별도로 클릭한다는 것은 해당 댓글을 수정하거나 삭제하는 경우에 발생한다.
- 댓글의 수정과 삭제는 원칙적으로는 로그인한 사용자가 해당 댓글의 작성자인 경우에만 허락된다. 하지만 현재까지 로그인에 대해서는 처리된 적이 없으므로 코드에서는 어떠한 댓글도 수정/삭제가 되도록 작성한다.
- DOM에서 이벤트 리스너를 등록하는 것은 반드시 해당 DOM 요소가 존재해야만 가능하다.
- 위와 같이 동적으로 Ajax를 통해서 <li> 태그들이 만들어지면 이후에 이벤트를 등록해야 하기 때문에 일반적이 방식이 아니라 '이벤트 위임(delegation)'의 형태로 작성해야 한다.
- '이벤트 위임'이 말은 거창하지만 실제로는 이벤트를 동적으로 생성되는 요소가 아닌 이미 존재하는 요소에 이벤트를 걸어주고, 나중에 이벤트 대상을 변경해 주는 방식이다. jQuery는 on()을 이용해서 쉽게 처리할 수 있다.
$(".chat").on("click", "li", function(e){
var rno = $(this).data("rno");
console.log(rno);
});
- jQuery에서 이벤트를 위임하는 방식은 이미 존재하는 DOM 요소에 이벤트를 처리하고 나중에 동적으로 생기는 요소들에 대해서 파라미터 형식으로 지정한다.
- 위의 경우 <ul> 태그의 클래스 'chat'을 이용해서 이벤트를 걸고 실제 이벤트의 대상은 <li> 태그가 되도록 한다.
- 브라우저에서 확인해 보면 이벤트는 <ul>에 걸었지만, 각 댓글이 이벤트의 this가 된 것을 확인할 수 있다.
- 화면에서 댓글을 볼 수 있는 모달창을 처리해준다. 모달창을 브라우저에서 보여주는 코드는 특정한 댓글을 클릭했을 때 보여주도록 한다.
- 댓글을 조회하는 행위는 현재의 경우 모든 내용이 화면에 있기 때문에 별도로 조회할 필요는 없지만, 원칙적으로 Ajax로 댓글을 조회한 후 수정/삭제를 하는 것이 정상이다.
- 댓글을 가져온 후에는 필요한 항목들을 채우고 수정과 삭제에 필요한 댓글 번호(rno)는 'data-rno' 속성을 만들어서 추가해둔다.
- 브라우저에서 특정 댓글을 클릭하면 이제 필요한 내용들만 보이게 된다.
17.5.4 댓글의 수정/삭제 이벤트 처리
- 댓글의 삭제는 가장 간단하게 결과를 보여주는 작업만을 처리하면 되므로 가장 간단하게 처리할 수 있다.
- 삭제/수정 작업 모두 끝난 후에는 다시 댓글 목록을 갱신해야만 한다.
- 그리고 댓글 삭제 역시 모달창에 있는 'data-rno' 값을 이용하여 처리한다.