본문 바로가기
JSP

Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(20) - ajax를 이용한 덧글 기능 구현하기

by 김마리님 2020. 6. 15.

이전 게시글은 여기

Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(19) - 파일 업로드를 이용해 프로필 사진 넣기

 

Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(19) - 파일 업로드를 이용해 프로필 사진 넣기

이전 게시글은 여기 >> Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(18) - 키워드를 이용한 검색 만들기 > Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(17) - 쿠키를 이용한 새로고침 시 조��

itstudy-mary.tistory.com

 

덧글은 무엇으로 이루어져 있을까?

유저의 프로필 사진, 유저의 이름, 내용, 삭제 수정 버튼이 있다.

 

이 포스트에서는 덧글을 쓰고 유저의 사진, 이름, 내용을 도출하는 코드만 쓴다.

먼저 덧글을 쓰는 jsp는 다음과 같다. 상세보기 페이지, 게시물의 내용을 띄우는 div 박스 아래에 띄운다.

 

-detail.jsp

	<hr />
	<!-- 댓글 박스 -->
	<div class="row bootstrap snippets">
		<div class="col-md-12">
			<div class="comment-wrapper">
				<div class="panel panel-info">
					<div class="panel-heading m-2"><b>Comment</b></div>
					
					<div class="panel-body">
					
						<textarea id="reply__write__form" class="form-control" placeholder="write a comment..." rows="3"></textarea>
						<br>
						<button onclick="replyWrite(${detailDto.boardDto.board.id}, ${sessionScope.principal.id })" class="btn btn-primary pull-right">댓글쓰기</button>
						<div class="clearfix"></div>
						<hr />
						
						<!-- 댓글 리스트 시작-->
						<ul id="reply__list" class="media-list">
						
							<c:forEach var="replyDto" items="${detailDto.replysDto }">
							<!-- 댓글 아이템 -->
							<li class="media">	
								<img onerror="this.src='/blog/image/userProfile.png'" src="${replyDto.userProfile }" class="img-circle">		
								<div class="media-body">
									<strong class="text-primary">${replyDto.username }</strong>
									<p>
										${replyDto.reply.content }
									</p>
								</div>
							</li>
							</c:forEach>
						</ul>
						<!-- 댓글 리스트 끝-->
					</div>
				</div>
			</div>

		</div>
	</div>
	<!-- 댓글 박스 끝 -->

천천히 봅시다.

textarea에 js를 적용할때 찾을 수 있도록 아이디를 걸어둡니다.

그리고 버튼에는 버튼을 눌렀을 때 작동할 function을 설정합니다. 우리가 덧글을 쓸 땐 뭐가 필요할까요? 이 글을 쓰는 사람과, 그 덧글이 쓰이는 내용이 들어가므로 변수에 아이디와 게시글 아이디를 받아서 자바스크립트 함수에 넘기게 된다.

(함수는 좀 있다 이야기 합시다)

 

댓글 리스트에는 받아온 값들을 list로 만들어 foreach문으로 출력한다.

 

이제 함수에 대해서 얘기해봅시다.

function replyWrite(boardId, userId){
		console.log(userId);
		if(userId===undefined){
			alert("로그인이 필요합니다.");
			return;
		}
		var data= {
			boardId : boardId,
			userId: userId,
			content : $("#reply__write__form").val()
		};

		$.ajax({
			type: "post",
			url : "/blog/reply?cmd=writeProc",
			data : JSON.stringify(data),
			contentType: "application/json; charset=utf-8",
			dataType: "json"
		}).done(function(result){
			//정상응답
			if(result==-1 || result==0){
				alert("덧글 등록 실패");
			}else{
			//1.reply__list를 찾아 비우기
			alert("덧글 등록 성공");
			$("#reply__list").empty();
			console.log(result);
			//2.ajax 재호출 findAll()
			renderReplyList(result);
			//3.reply__list를 찾아 내부에 채워주기
			$("#reply__write__form").val("");
			}
		}).fail(function(error){
			alert("덧글 등록 실패");
		});
	}

function renderReplyList(replyDtos){
	for (var replyDto of replyDtos) {
		$("#reply__list").append(makeReplyItem(replyDto));
		//reply-id 추가 시작
		var replyItem = `<li id="reply-${replyDto.reply.id }" class="media">`;
		//reply-id 추가 끝
	}
}

function makeReplyItem(replyDto) {
	var replyItem=`<li class = "media">`;
	
	if(replyDto.userProfile ==null){
		replyItem+=`<img src="/blog/image/userProfile.png" class="img-circle">`;
		
	}else{
		replyItem+=`<img src="${replyDto.userProfile}" class="img-circle">`;
		
	}
	replyItem += `<div class ="media-body">`;
	replyItem += `<strong class="text-primary">${replyDto.username}</strong>`;
	replyItem += `<p>${replyDto.reply.content}</p>`;
	replyItem += `</div>`;

	//휴지통 추가 시작
	replyItem += `<div class="m-3">`;
	replyItem += `<i onclick="replydelete(${replyDto.reply.id})" style="cursor : pointer;" class="Tiny material-icons">delete</i>`;
	replyItem += `</div>`;
	
	replyItem += `</li>`;
	
	return replyItem;
}

ajax로 받는 이유는 전체 DOM을 받아서 리로드하는 것은 시간이 오래걸리기 때문에 일부 dom(일부 <div>)만 리로드 하는 것이 목적이다. 여기서는 덧글을 담는 <div>만 리로드 한다.

 

일단 내가 쓸 덧글을 ajax 통신으로 액션으로 보낸다.

여기서 앞에서 매개변수를 지정한 이유와 id를 지정한 이유를 볼 수 있다. 

ajax로 내가 데이터를 보낼 url을 지정하고, 데이터를 설정한다.

데이터는 세 가지를 보내야 하니까, 이것을 Json 데이터로 묶어서 보낸다. 그래서 JSON.stringify함수를 통해 string을 json으로 변환한다.

json으로 보내니까, 보내는 컨텐츠 타입과 데이터 타입을 지정한다.

이제 정상응답을 보내줄 액션을 만들자.

 

-ReplyController.java

reply 역시 다른 controller과 같은 방식을 공유한다.

package com.cos.blog.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.cos.blog.action.Action;
import com.cos.blog.action.board.BoardDeleteProcAction;
import com.cos.blog.action.board.BoardDetailAction;
import com.cos.blog.action.board.BoardHomeAction;
import com.cos.blog.action.board.BoardSearchAction;
import com.cos.blog.action.board.BoardUpdateAction;
import com.cos.blog.action.board.BoardUpdateProcAction;
import com.cos.blog.action.board.BoardWriteAction;
import com.cos.blog.action.board.BoardWriteProcAction;
import com.cos.blog.action.reply.ReplyDeleteProcAction;
import com.cos.blog.action.reply.ReplyWriteProcAction;
import com.cos.blog.action.user.UsersLoginAction;

// http://localhost:8000/blog/board
@WebServlet("/reply")
public class ReplyController extends HttpServlet {
	private final static String TAG = "ReplyController : ";
	private static final long serialVersionUID = 1L;
       
    public ReplyController() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}
	
	protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// http://localhost:8000/blog/user?cmd=join
		String cmd = request.getParameter("cmd");
		System.out.println(TAG+"router : "+cmd);
		Action action = router(cmd);
		action.execute(request, response);
	}
	
	public Action router(String cmd) {
		if(cmd.equals("writeProc")) {
			// 홈페이지로 이동
			return new ReplyWriteProcAction(); //Board의 목록
		}
		return null;
		
	}
	

}

 

라우터를 통해 해당 액션과 연결하고.

 

-ReplyWriteProcAction.java

package com.cos.blog.action.reply;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.cos.blog.action.Action;
import com.cos.blog.dto.ReplyResponseDto;
import com.cos.blog.model.Reply;
import com.cos.blog.repository.ReplyRepository;
import com.cos.blog.util.Script;
import com.google.gson.Gson;

public class ReplyWriteProcAction implements Action {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	// TODO Auto-generated method stub
	
	//form 타입이 아니고 json이기 때문에 request.getParameter 못씀
	
		BufferedReader br=request.getReader();
		StringBuffer sb= new StringBuffer();
		String input=null;
		while((input=br.readLine())!=null) {
			sb.append(input);
		}
		System.out.println(sb.toString());
		
		
		Gson gson=new Gson();
		Reply reply=gson.fromJson(sb.toString(), Reply.class);
		
		//ReplyRepository 연결 - save(reply)
		//script.outText()응답
		
		ReplyRepository replyRepository=ReplyRepository.getInstance();
		int result=replyRepository.save(reply);
		//save 성공하면 1 실패하면 0,1
		if(result==1) {
			List<ReplyResponseDto> replyDtos =replyRepository.findAll(reply.getBoardId());
			String replyDtosJson=gson.toJson(replyDtos);
			Script.outJson(replyDtosJson, response);

		}else {
			Script.outText("error", response);
		}
		
	}
}

json타입은 key-value형태로 값을 받지 않기 때문에 json은 getParameter형태로 값을 불러 올 수 없다. 그렇기 때문에 getReader()로 불러와서 이것을 Buffer로 받는다.

받아서 append 해서 붙여준 StringBuffer의 형태를 String 형태로 변환하여 Reply 형태 객체에 담고 이 객체로 데이터베이스에 접근한다.

접근해서 받은 값이, update에 성공하여 1을 반환하면 등록된 값을 이용하여 게시판 매개변수를 where절에 넣고 이 값을 다시 json 데이터로 변환시켜 응답한다.

 

-ReplyResponsitory.java의 덧글 등록 쿼리와 덧글 불러오기 쿼리

	public int save(Reply reply) {
		final String SQL="insert into reply(id,userId,boardid,content,createdate) values (reply_SEQ.nextval,?,?,?,sysdate)";
		try {
			conn=DBConn.getConnection();
			pstmt = conn.prepareStatement(SQL);
			//물음표 완성하기
			
			pstmt.setInt(1, reply.getUserId());
			pstmt.setInt(2, reply.getBoardId());
			pstmt.setString(3, reply.getContent());
			
			return pstmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println(TAG+"save : "+e.getMessage());
		}finally {
			DBConn.close(conn, pstmt);
		}
		
		return -1;
	}
    
    	public List<ReplyResponseDto> findAll(int boardId) {
		StringBuffer sb= new StringBuffer();
		sb.append("select r.id, r.userId, r.boardId, r.content, r.createDate, ");
		sb.append(" u.username, u.userprofile ");
		sb.append("from reply r inner join users u ");
		sb.append("on r.userid=u.id ");
		sb.append("where boardid=? ");
		sb.append("order by r.id desc");
		
		final String SQL=sb.toString();
		List<ReplyResponseDto> replyDtos=null;
		
		try {
			conn=DBConn.getConnection();
			pstmt = conn.prepareStatement(SQL);
			//물음표 완성하기
			pstmt.setInt(1, boardId);
			rs=pstmt.executeQuery();
			
			replyDtos=new ArrayList<>();
			while(rs.next()){
				Reply reply=Reply.builder()
						.id(rs.getInt(1))
						.userId(rs.getInt(2))
						.boardId(rs.getInt(3))
						.content(rs.getString(4))
						.createDate(rs.getTimestamp(5))
						.build();
				
				ReplyResponseDto replydto=ReplyResponseDto.builder()
									.reply(reply)
									.username(rs.getString(6))
									.userProfile(rs.getString(7))
									.build();
				replyDtos.add(replydto);
				
			}

 

이렇게 불러온 값을 자바스크립트 for in 구문을 통해 받은 배열만큼 값을 순환시키고, 순환 될때마다 받아온 덧글창이 만들어지고(html 함수를 백틱을 통해 추가했다.) 이 만들어진 html 코드가 empty된 공간 안에 랜더링 된다.

다음처럼 굳이 새로고침 하지 않아도 ajax 통신을 통해 덧글창만 새로 쓰였다.

반응형