본문 바로가기

_Programming/Spring

Spring.Comment

Comment (+Paging)

페이지 이동 없이 ajax기술을 이용함.

ajax는 responsbody이용.

 

방법

1. JPS에서 썻던 방법

ResponseBody를 이용한 방법.

 

2. @RestController :  ResponseBody + Control

Restful(Repressentational State Transfer)을 이용하는 방법.

(Rest란 자원의 이름으로 구분하여 해당 자원의 상태를 주고 받는 모든 것을 의미한다.)

 

/product/list/1 : list 1번

/product/detail/1 : detail 1번

이런식으로 url 주소자체가 의미를 가지고 있어 표현이 가능하다.

 

Spring이 Restful방식을 이용하는 것이다.

 

우리는 항상 새로운 기술에 잘 적응해야하므로

Resful 방식을 이용하여 Comment를 만들어 보겠다.

 

1. Comment DB Table 만들기

create table tbl_comment
cno number(10.0),
pno number(10,0) not null,
content varchar2(1000) not null,
writer varchar2(100) not null,
regd8 date default sysdate,
modd8 date default sysdate); 

alter table tbl_comment
add constraint pk_comment primary key(cno);

alter table tbl_comment
add constraint fk_comment_product
foreign key(pno) references tbl_product(pno);

create sequence seq_comment_cno
start with 1
increment by 1
nocycle cocache;

2. CommentVO.java 셋팅

DBTable로 만든 내용을 기반으로 변수와 getter/setter를 생성한다.

3. CommentMapper 셋팅

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommentMapper">
  <insert id="add" parameterType="commentVO">
   insert into tbl_comment(cno,pno,content,writer)
   values(seq_comment_cno.nextval,#{pno},#{content},#{writer})
  </insert>
  <select id="list" parameterType="java.lang.Integer" resultType="CommentVO">
    select cno,pno,content,writer,modd8 
    from tbl_comment where pno = #{pno}
  </select>
  <update id="modify" parameterType="CommentVO">
   update tbl_comment set content = #{content}, modd8 = sysdate
   where cno = #{cno}
  </update>
  <delete id="remove">
    delete tbl_comment where cno = #{cno}
  </delete>
</mapper>

4. DAO/ Service

먼저 interface단을 빌드해주고 class가 implements받게 한다.

CommentDAO.java(interface) --> CommentDAOImlp.java

--> CommentServeice.java(interface) --> CommentServiceImlp.java

 

Implements 받는 이 두 class는 꼭 @Repository를 적고 사용해야 한다.

CommentDAOImlp에서는 @Inject SqlSession sql을 선언하고

CommentServiceImlp에서는 @Inject CommentDAO cdao를 선언하여야 한다.

5.CommentCtrl

이번 방식은 JSP에서 했던 방식(Parameter를 각각으로 받는 경우)과 다르게

Restful기술을 이용할 것이기 때문에

기술을 쓸 수 있게 해주는 어노테이션@RestController을 추가한다.

6. product의 detail.jsp에서 모든 일은 이루어 지지.

- 먼저 bootstrap에서 사용할 form을 가져온다.

//detail.jsp
<c:if test="${ses.Info.email ne '' }">
  <div class="input-group mt-3">
    <input type="text" id="cmtInput" class="form-control" placeholder="댓글대댓글">
    <div class="input-group-append">
	<button class="btn btn-primary" type="button" id="cmtOkBtn">OK</button>
	<button class="btn btn-danger" type="button" id="cmtResetBtn">Cancel</button>
    </div>	
  </div>
</c:if>
<!-- 댓글리스트 -->
<div id="cmtList">
  <ul>
    <li>작성자</li>
    <li>댓글내용</li>
    <li>작성날짜</li>
    <li>기능</li>
  </ul>
</div>

detail.jsp에 <script></script>안에 넣는 내용

content는 처음에 읽히면 빈값을 가져오게 되므로 cmt0kBtn을 눌렀을 때 읽어 올 수 있게 해줘야 댓글이 불러와진다.

 

 

원래는 ajax를 이용하여 데이터를 보낼때 key : value 로 이동하는데

 

{pno = "1", content = "감자", writer = "표고"} 를

servlet으로 보내서 Ctrl단에서

int content = reqeust.getParameter("pno")

String content = reqeust.getParameter("content")

String writer = reqeust.getParameter("writer ")로 받아서 처리했으나

 

이제는 JSON을 이용하여

"{"pno":"1","content":"감자","writer":"표고"}"를 하나의 String 객체로 생성한다.

 

제일큰 차이는 전의 방식은 3개의 parameter가 전달되어 3개의 변수를 받아야하고

두번째 방법은 1개의 parameter로 JSON데이터로 받게 된다는 것이다.

 

두번째 방법을 봤을 때 고민점이 생길 수 있다

아니,, 어차피 객체로 받아봤자 결국 또 key : value 하나씩 다 끄집어 내서 풀어야 하는거 아냐?

-응 아냐.

 

왜냐하면

Spring은 이러한 것들을 자동으로 처리해주는 ResponseEntity가 존재하기 때문이다.

WOW!

이 방법이 대세다.

 

사용할때 유의사항은 아래와 같다.

- JSON의 String형태로 데이터들을 보내기 때문에 꼭 contentType을 지정해줘야 한다.

- url의 이름은 "/comment/맘대로" 맘대로에서 내 맘대로 지으면됌.

간단하쥬~~??

//detail.jsp의 <script>
/* Comment Part */
let cmt_writer = '<c:out value="${sesInfo.nickname}"/>';
let cmt_pno = '<c:out value="${pvo.pno}"/>';
$("#cmtOkBtn").on("click",function(){
  let cmt_content = $("#cmtInput").val();
  if(cmt_content == null || cmt_content ==''){
    alert("댓글 입력해라잉");
	return false;
  }else{
    let cmtData = {pno:cmt_pno,writer:cmt_writer,content:cmt_content};
	$.ajax({
		type:"post",
		url:"/comment/new",
		data:JSON.stringify(cmtData),
		contentType:"application/json; charset=utf-8"
	}).done(function(result){
		alert(result);
	});
  }
});

7. CommentCtrl에서 보내준걸 받는다.

전에 했던 방식들과는 좀 다르게 내가 받는 것들에 대한 정보를 입력해줘야 한다.

(value를 /new로 받는 이유는 우리가 앞서 detail.jsp에서 /new로 보내줬기때문이다.)

 

@RequestBody로 데이터가 json타입의 text가 utf-8의 모양으로

Request로 온 걸 받아서 처리한 다음 CommentVO cvo에 담아준다.

@PostMapping(value="/new", consumes="application/json", produces = "application/text; charset=utf-8")
public ResponseEntity<String> write(@RequestBody CommentVO cvo){
  int isOk = csv.write(cvo);
  return isOk == 1? return new ResponseEntity<>("댓글등록됌 오홍홍홍",HttpStatus.OK) 
  : return new ResponseEntity<>("댓글실패실패실패",HttpStatus.INTERNAL_SERVER_ERROR);	
}

Spring에서는 기본적으로 HttpEntity라는 클래스를 제공한다.

HttpStatus :  하나의 클래스로 무수히 많은 메소드들을 품고 있다.

즉 HttpStatus.필요한메소드 이렇게 사용할 수 있다는 얘기다.

이 클래스는 Http프로토콜을 이용하는 통신의 data를 저장할 수 있게한다.

그런 HttpEntity클래스를 상속받은 놈이 바로  ResponseEntity이다.

따라서 통신하여 온 data의 값들을 하나의 객체로 받아서 cvo에 담아주는 것이다.

 

* 이부분 공부 더!! 여기서 ResponseBody와 ResponseEntity를 비교해보자면,,

ResponseBody나 ResponseEntity를 return하는 기능은 결과적으로 같지만 구현하는 방법이 다르다고 볼 수 있다.

 header 값을 변경시켜야 할 경우엔 @ResponseBody의 경우 파라미터로 Response 객체를 받아서 이 객체에서 header를 변경시켜야 하고ResponseEntity에서는 이 클래스 객체를 생성한뒤 객체에서 header 값을 변경시킨다.

8. detail.jsp의 ajax에 응답결과를 받는다.

다시 detail.jsp의 <script>로 이동하여 응답에 대한걸 받는 부분들 ajax에서 빌드한다.

처음 ajax 통신을 할 때 먼저 빌드해도 좋지만 순서를 더 잘 공부하기 위해 응답하는 순서대로 빌드해본다.

$.ajax({
  type : "post",
  url : "/comment/new",
  data : JSON.stringify(cmtData),
  contentType:"application/json; charset=utf-8"
}).done(function(result){
  alert(result);
});

9. 리스트 호출하기

위까지의 내용이 처음 댓글이 입력되면 ajax 통신을 타고 가서 db에 저장되는 것이다.

이제 우리가 저장된 댓글을 브라우저에서 볼 수있도록 db에서 가져와서 화면에 뿌려주는 일을 해야한다.

심어주고 캐서 뿌리고.. 데이터농사꾼..

 

script내용이 길어질것을 대비해

resources안에 js안에 comment.js 파일만든다.

 

새로만든 js파일에 listComment함수를 만들어 준다.

이 메소드는 detail에 ajax통신이 끝난 후 결과값을 done으로 받고 나서 실행이 되는데

당연한게 댓글이 db에 성공적으로 들어가야 그걸 가지고 나올 수 있으니까 그러니까

function listComment(param_pno){
  
}

만든후 이게 ajax통신이 성공적으로 끝난후 호출되도록 detail.jsp에서 호출한다.

이때 어떤 상품에 대한 댓글인지를 알기 위해서 pno를 던져줘야한다.

$.ajax({
  type:"post",
  url:"/comment/new",
  data:JSON.stringify(cmtData),
  contentType:"application/json; charset=utf-8"
}).done(function(result){
    alert(result);
    listComment(cmt_pno);
  });

 

ajax 응답함수 안에 listComment(cmt_pno) 실행하도록 넣어준다.

 

js파일에

이걸 바탕으로 CommentCtrl에

 

Jackson이용

 

Rest활용

 

list의 pno를 받는데 이걸 path변수라고 한다.

주소를 변수로 받겠다는 얘기다.

이걸 사용하려면 몇가지 등록이 필요하다.

 

내가 json데이터 타입 형태로 요구했기때문에

어떻게 자바 객체자 자바스크립트 객체로 나올 수 있나..? - spring이 자동으로 해줌(파싱안함.)

 

data-cno : 버튼의 아이디값을 쉽게

 

10. 댓글 수정

일단 버튼이쁘게 셋팅 후 printListComment()에 클래스 delBtn 지정하기

ulTag += '<button class="btn btn-outline-danger delBtn" data-cno="'+cvo.cno+'">삭제</button></li></ul>';

detail.jsp에서 delBtn 클릭이벤트 넣어주는데 pno를 같이 던져주기

 

$(document).on("click",".delBtn", function(){ 
  let cno = $(this).data("cno");
  removeComment(cno,cmt_pno);
});

comment.js에서 removeComment메소드 생성하기

 

11. 댓글 Paging

처음 리스트 띄울때 1번 페이지 보여달라고하기

listComment(cmt_pno, 1);

comment.js에서 listComment에 pageNo파라미터로 던져주기

그리고 내가 누른 페이지 번호가 1보다 크면 그페이지 그대로 보여주고 아니면(음수이거나하면) 무조건 1페이지로 셋팅하기.

function listComment(param_pno, pageNo){
  let pageNo = page > 1 ? page : 1;
  $.getJSON("/comment/list/"+param_pno+"/"+pageNo+".json",function(cList){
})

CommentCtrl에서 value에 {page}를 던져주고 파라미터로도 page를 던져준다.

여기서 page는 내가 보는 페이지.

@GetMapping(value = "/list/{pno}/{page}", produces = {MediaType.APPLICATION_XML_VALUE, 
MediaType.APPLICATION_JSON_UTF8_VALUE })
public ResponseEntity<List<CommentVO>> list(@PathVariable("pno") int pno,@PathVariable("page")int page) {
  Criterion cri = new Criterion(page,10);
  return new ResponseEntity<List<CommentVO>>(csv.getList(cri,pno), HttpStatus.OK);
}

 

 

DTO(Data Transfer Object??) : 객체지향

list는 VO객체로 이뤄져있는데

Paging을 위해서 VO+totalCount인 CommentDTO클래스를 만들어준다.

 

 

그다음 Ctrㅣ에서 고쳐준다.

Cri를 던지므로 또 다 던져준다.

 

Map사용.(객체와 변수를 한번에 던지기 위해)

//DAOImpl
@Override
	public List<CommentVO> selectList(Criterion cri, int pno) {
		Map<String, Object> map = new HashMap<>();
		map.put("cri", cri);
		map.put("pno", pno);			
		return sql.selectList(ns+"list", map);
	}

 

Mapper

<select id="list" parameterType="java.util.HashMap" resultType="CommentVO">
    <![CDATA[
    select cno,pno,content,writer,modd8 
    from (select /*+INDEX(tbl_comment pk_comment)*/
     rownum rn, cno, pno, content, writer, modd8
     from tbl_comment where pno = #{pno} 
     and rownum <= #{cri.pageNum} * #{cri.amount})
     where rn > (#{cri.pageNum}-1) * #{cri.amount}
     ]]>
  </select>

 

다시 거꾸로 돌아가서

Ctrl에서 commentDTO의 형티로 받기로 했으므로 또 다 commentDTO로 변경.

그리고 list받아오고

cmtCnt받기 위해 또 다오에서 selectTotal(int pno)메소드 생성.

//CommentSeriveceImpl
@Override
	public CommentDTO getList(Criterion cri, int pno) {
		List<CommentVO> list = cdao.selectList(cri, pno);
		int cmtCnt = cdao.selectTotal(int pno);
		return cdao.selectList(cri,pno);
	}

 

여기서 Service의 역할분담을 알 수 있다.

Ctrl에서는 부르기만하고 Service는 갖다 바친다.

 

 

 

 

href는 페이지 이동이 일어나므로

ajax를 쓰는 댓글리스트에는 맞지 않는다

따라서 href를 막고 리스트를 띄우는 용도로만 사용한다.

 

 

 

 

-------------------------------------------------------------------------------------------------------------------------

여기까지가 새로운 Comment 빌드 방식이었다.

 

한줄반성

오타와의 전쟁에서 살아남았...쿨럭쿨럭......(전사)

제발 javascript에서 id불러올때 #좀 붙이자!!!!!!

 

MediaType은 org.springframework.http로 사용한다.

 

 

.json을 해독하기 위해서 MediaType의 APPLICATION_XML_VALUE를 사용해야하고

또 APPLICATION_JSON_UTF8_VALUE도 사용해야한다.

왜냐하면 돌려줘야 하는 파일이 단순 String이 아니라 JSON이기 때문에 알려줘야한다.

스트링화 시키는데 이 타입이 UTF-8로 인코딩된 JSON이라는 것을 알려줘야한다.

 

 

 

 

'_Programming > Spring' 카테고리의 다른 글

Spring.Security_basic  (0) 2020.07.07
Spring.AOP(Aspect-Oriented Programming)  (0) 2020.07.06
Spring.Search(검색기능)  (0) 2020.07.02
Spring.페이징(Paging)  (0) 2020.07.01
Spring.파일첨부(file)  (0) 2020.06.30