본문 바로가기
JSP

[게시판 Ver.2] 커스텀태그_응용

by amoomar 2022. 3. 15.
반응형

이전 포스팅의 내용을 활용하여 추가 작업을 진행하였다. 목차는 다음과 같다.

 

1. 커스텀 태그의 활용
   1) 타입 정의하기
   2) EL의 값 외부에서 받아오기

2. FrontController개념 적용

 

코드를 포인트 부분만 첨부할 예정이기에 이외 코드들은 깃허브 링크를 통해 확인 가능하다.

 

https://github.com/Hamjeonghui/m/tree/main

 

GitHub - Hamjeonghui/m

Contribute to Hamjeonghui/m development by creating an account on GitHub.

github.com

 


 

 

1. 커스텀 태그의 활용

어느 위치에 커스텀 태그를 어떻게 활용할 수 있을지를 먼저 생각한 후 태그파일을 생성, 내부에 로직을 작성하는 순서로 진행한다. 오늘 포스팅에서는 총 4개의 태그파일이 존재하게 되는데, 주요 내용은 3개의 태그파일에 해당한다. main.jsp의 코드는 아래와 같다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="kim" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
</head>
<body>
<script type="text/javascript">
   function newMember(){
      window.open(
         "signup.jsp","newMember","width=500,height=300"
      );
   }
</script>

<header>
   <h1>댓글과 대댓글 실습예제</h1>
   <nav>
      <ul>
         <li><a href="controller.jsp?action=main">로고</a></li>
         <li><kim:login /></li>
      </ul>
   </nav>
</header>

<div id="wrapper">
   <section id="main">
      <br><hr>
      <form action="insertBoard.do" method="post">
         <kim:board type="board" />
         <input type="hidden" name="mid" value="${member}">
         <input type="submit" value="글 작성하기">
      </form>
      <hr>
      <h2>게시글 목록</h2>
      <c:forEach var="bs" items="${datas}">
      <c:set var="vo" value="${bs.boardVO}" />
         <div class="board">
            <h3>[${vo.bid}] ${vo.mid} >> ${vo.content} [댓글수 ${vo.rpcnt} | 좋아요 ${vo.favcnt}] [${vo.bdate}] <kim:bmenu bid="${vo.bid}" mid="${vo.mid}"/></h3>
         </div>
         <div class="reply">
            <form action="insertReply.do" method="post">
               <kim:board type="reply" />
               <input type="hidden" name="mid" value="${member}">
               <input type="hidden" name="bid" value="${vo.bid}">
               <input type="submit" value="댓글 작성하기">
            </form>
         </div>
         <div class="replylist">
            <c:forEach var="r" items="${bs.replyList}">
               <h4>${r.mid} >> ${r.msg} [${r.rdate}] <kim:rmenu mid="${r.mid}" rid="${r.rid}"/> </h4>
            </c:forEach>
         </div>
      </c:forEach>
   </section>
</div>

<footer>
   <p>ⓒCOPYRIGHT</p>
</footer>

</body>
</html>

 

 

1) 타입 정의하기

메인화면에서 board.tag파일을 통해 로그인상태에만 게시글과 댓글을 남길 수 있도록 코드를 작성하려고 한다. 이때 위 코드에서 <kim:board>커스텀태그로 type속성을 통해 게시글에 대한 로직인지, 댓글에 대한 로직인지가 구분되어 사용되고 있음을 알 수 있다. 

 

주석을 통해 내용을 파악할 수 있다.

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

/* 타입이라는 속성명을 정의 */
<%@ attribute name="type" %>

<c:if test="${member!=null}">/*로그인상태라면*/
<c:choose>
/*type이 board일때, 아래 문장 수행*/
<c:when test="${type=='board'}"><input type="text" name="content"></c:when>
/*type이 reply일때, 아래 문장 수행*/
<c:when test="${type=='reply'}"><input type="text" name="msg"></c:when>
</c:choose>
</c:if>

<c:if test="${member==null}">/*비로그인상태라면*/
<c:choose>
/*type이 board일때, 아래 문장 수행*/
<c:when test="${type=='board'}"><input type="text" name="content" value="로그인후에 이용가능" disabled></c:when>
/*type이 reply일때, 아래 문장 수행*/
<c:when test="${type=='reply'}"><input type="text" name="msg" value="로그인후에 이용가능" disabled></c:when>
</c:choose>
</c:if>

 

 


 

2) EL의 값 외부에서 받아오기

마찬가지로 main.jsp의 코드를 확인해보았을때, bmenu.tag파일을 통해 로그인된 아이디로 작성된 게시글일때만 삭제가 가능하도록 하며, 좋아요 기능은 로그인 유무에 상관 없이 사용이 가능하도록 구현한다.

 

게시글하나와 댓글 여러개를 반환하는 내용(boardSet클래스)은 datas라는 이름으로 request단에 저장되어있기 때문에 각 게시글의 vo와 댓글의 vo에 접근하기 위해서는 datas.boardVO.어쩌고 혹은 datas.replyList.저쩌고로 일일이 작성을 했어야하는데, 이를 관리하기 위해 <c:set>태그를 통해 코드의 작성을 단축시켰다.

 

EL의 특성상 태그파일 내부로 들어가게 되면 "좋아요, 게시글 삭제"기능을 구현하기 위해 사용될 비즈니스 메서드의 필수 인풋인 bid와, 현재 로그인된 mid가 ${member}(mid의 값이 저장되어있음)와 동일한지 알아보기 위한 mid로 곧장 접근이 불가능하기 때문에 main.jsp에서 각각의 vo 값을 알려주어야한다. 이때, 해당 방식을 통해 값의 전달이 가능하다.

 

bmenu.tag

 

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ attribute name="bid" %>
<%@ attribute name="mid" %>

[ <a href="fav.do?bid=${bid}">♥</a> ]

<c:if test="${member==mid}">
	|[<a href="deleteBoard.do?bid=${bid}">삭제</a>]
</c:if>

 

 

마찬가지로, 댓글을 삭제하기 위해서는 mid와 rid가 각각 필요하게 되는데 위와 같은 이유로 외부에서 값을 받아 로직을 수행하는 방법을 통해 정상 작동이 가능하다.

rmenu.tag

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ attribute name="rid" %>
<%@ attribute name="mid" %>

<c:if test="${member==mid}">
	[<a href="deleteReply.do?rid=${rid}">삭제</a>]
</c:if>

 


 

2. FrontController개념 적용

actionDo메서드를 집중적으로 살펴보면 이전 포스팅의 로직과 약간 다르게 되어있다. 이전에는 잘못된 요청에 대해 forward에 null을 저장하여, forward==null일때 error페이지로 가도록 작성했다면, 이번 포스팅에서는 FC에서 스크립트를 사용하기 위해 로직이 약간 다르게 진행되었다.

 

스크립트코드는 일반 class에서 구현할 수 없으므로 FC 혹은 view에서 작업할 수 있도록 해야한다.

 

이외의 내용은 주석을 통해 확인이 가능하다.

package controller.common;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
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 controller.action.ActionForward;
import controller.action.DeleteBoardAction;
import controller.action.DeleteReplyAction;
import controller.action.FavAction;
import controller.action.InsertBoardAction;
import controller.action.InsertReplyAction;
import controller.action.LoginAction;
import controller.action.LogoutAction;
import controller.action.MainAction;
import controller.action.SignupAction;

/**
 * Servlet implementation class FrontController
 */
@WebServlet("*.do")
public class FrontController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public FrontController() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		actionDo(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		actionDo(request, response);
	}
	private void actionDo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		String uri=request.getRequestURI();
		String cp=request.getContextPath();
		String command=uri.substring(cp.length());

		ActionForward forward=null;
		if(command.equals("/main.do")) {

			System.out.println("FC:메인으로 이동");
			try {
				forward=new MainAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}else if(command.equals("/login.do")) {

			System.out.println("FC:로그인 시도");
			try {
				forward=new LoginAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}else if(command.equals("/logout.do")) {

			System.out.println("FC:로그아웃 시도");
			try {
				forward=new LogoutAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}else if(command.equals("/signup.do")) {

			System.out.println("FC:회원가입 요청");
			try {
				forward=new SignupAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}else if(command.equals("/fav.do")) {

			System.out.println("FC:좋아요 요청");
			try {
				forward=new FavAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}else if(command.equals("/insertReply.do")) {

			System.out.println("FC:댓글작성 요청");
			try {
				forward=new InsertReplyAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}else if(command.equals("/insertBoard.do")) {

			System.out.println("FC:게시글작성 요청");
			try {
				forward=new InsertBoardAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}else if(command.equals("/deleteReply.do")) {

			System.out.println("FC:댓글 삭제 요청");
			try {
				forward=new DeleteReplyAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}else if(command.equals("/deleteBoard.do")) {

			System.out.println("FC:게시글삭제 요청");
			try {
				forward=new DeleteBoardAction().execute(request, response);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}else {
			System.out.println("잘못된 요청");
		}

		//성공 했을때만 디스패처 실행
		if(forward!=null) {
			RequestDispatcher dispatcher=request.getRequestDispatcher(forward.getPath());
			dispatcher.forward(request, response);
		} // forward가 null이 아니면 페이지가 이동해버려서 else처리 해주지 않아도 정상작동 된다.
		
		//이전에는 forward가 null일때 에러페이지로 갈 수 있도록 new처리 해주었지만
		// 지금은 <script>처리
		PrintWriter out=response.getWriter();
		out.println("<script>alert('요청처리 실패');history.go(-1);</script>");
		
	}
}

 

try catch가 작성되지 않은 이유는 actionDo메서드 자체에서 발생될 예외에 대해 throws되도록 코드를 작성했기 때문이다.

 

 


 

반응형