Server

게시판 개발 STEP 2 - Board

IT의 큰손 2023. 5. 31. 18:02
728x90

★ Board : 게시글을 올리고, 해당 게시글에 댓글을 다는 곳

 

★ 소스코드 분석 - com.test.toy.board

 

■ Add.java

package com.test.toy.board;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;

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 javax.servlet.http.HttpSession;

import com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.BoardDTO;

@WebServlet("/board/add.do")
public class Add extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//Add.java
		String mode = req.getParameter("mode");
		
		String thread = req.getParameter("thread");
		String depth = req.getParameter("depth");
		
		req.setAttribute("mode", mode);
		
		req.setAttribute("thread", thread);
		req.setAttribute("depth", depth);
		
		RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/board/add.jsp");
		dispatcher.forward(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		//AddOk.java
		
		
		//1. 데이터 가져오기
		//2. DB 작업 > insert
		//3. 결과
		
		HttpSession session = req.getSession();
		
		String subject = req.getParameter("subject");
		String content = req.getParameter("content");
		
		String mode = req.getParameter("mode");
		
		
		BoardDTO dto = new BoardDTO();
		dto.setSubject(subject);
		dto.setContent(content);
		dto.setId((String)session.getAttribute("id"));
		
		BoardDAO dao = new BoardDAO();
		
		int thread = -1;
		int depth = -1;
		
		//계산
		if (mode.equals("new")) {
			//새글 쓰기
			//1. 현존하는 모든 게시물 중에서 가장 큰 thread값을 찾아서, 그 값에 +1000을 한 값을 새글의 thread값으로 넣는다.
			thread = dao.getMaxThread() + 1000;
			
			//2. 새글의 depth는 항상 0 을 넣는다.
			depth = 0;
			
		} else {
			int parentThread = Integer.parseInt(req.getParameter("thread"));
			int parentDepth = Integer.parseInt(req.getParameter("depth"));
			//답변글 쓰기
			//1. 현존하는 모든 게시물의 thread값을 대상으로, 현재 작성중인 답변글의 부모글 thread 값보다 작고, 이전 새글의 thread값보다 큰 thread를 모두 찾아서 -1을 한다.
			int previousThread = (int)Math.floor((parentThread - 1) / 1000) * 1000;
			
			HashMap<String,Integer> map = new HashMap<String,Integer>();
			
			map.put("praentThread", parentThread);
			map.put("previousThread", previousThread);
			
			dao.updateThread(map);
			
			//2. 답변글의 thread값은 부모글의 thread-0을 넣는다.
			thread = parentThread - 1;
			
			//3. 답변글의 depth값을 부모글의 depth+1을 넣는다.
			depth = parentDepth + 1;
			
		}
		
		dto.setThread(thread);
		dto.setDepth(depth);
		
		
		
		int result = dao.add(dto);
		
		if(result == 1) {
			resp.sendRedirect("/toy/board/board.do");
		} else {
			PrintWriter writer = resp.getWriter();
			writer.print("<script>alert('failed');history.back();</script>");
			writer.close();
		}
		
		
		
	}
}
  • 코드 분석
더보기
  1. 주어진 요청에 따라 게시판에 새로운 글을 추가하거나 답변글을 작성하는 기능을 수행합니다
  2. doGet() 메서드:
    • mode, thread, depth를 요청 매개변수에서 가져옵니다.
    • mode, thread, depth를 요청의 속성으로 설정합니다.
    • /WEB-INF/views/board/add.jsp로 포워딩하여 JSP 파일을 렌더링합니다.
  3. doPost() 메서드:
    • 세션을 가져옵니다.
    • 요청 매개변수에서 subject, content, mode를 가져옵니다.
    • BoardDTO 객체를 생성하고, 가져온 데이터를 설정합니다.
    • BoardDAO 객체를 생성합니다.
    • mode에 따라 새 글 작성 또는 답변글 작성을 수행합니다.
      • 새 글 작성 모드:
        • 현재 게시판에서 가장 큰 thread 값을 찾고, 그 값에 1000을 더한 값을 새 글의 thread 값으로 설정합니다.
        • 새 글의 depth는 0으로 설정합니다.
      • 답변글 작성 모드:
        • 부모 글의 thread 값보다 작고, 이전 글의 thread 값보다 큰 모든 thread 값을 찾아서 1을 빼줍니다.
        • 답변글의 thread 값은 부모 글의 thread 값에서 1을 뺀 값으로 설정합니다.
        • 답변글의 depth 값은 부모 글의 depth 값에 1을 더한 값으로 설정합니다.
    • BoardDTO에 설정한 값으로 게시글을 추가합니다.
    • 결과에 따라 리다이렉션하거나 실패 메시지를 출력합니다.

 

■ Auth.java

package com.test.toy.board;

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

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

import com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.BoardDTO;

public class Auth {

	public static boolean check(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		HttpSession session = req.getSession();
		
		String seq = req.getParameter("seq");
		
		//글쓴이 본인? or 관리자?
		BoardDAO dao = new BoardDAO();
		
		BoardDTO dto = dao.get(seq);
		
		if (!session.getAttribute("id").equals(dto.getId())
				&& !session.getAttribute("lv").toString().equals("3")) {
			
			PrintWriter writer = resp.getWriter();
			writer.write("<script>alert('failed'); location.href='/toy/index.do';</script>");
			writer.close();
			
			return true;
			
		}
		
		return false;
	}

	
	
}
  • 코드 분석
더보기

게시판의 권한을 확인하는 Auth 클래스입니다. 주어진 요청에 대해 글쓴이 본인 또는 관리자인지 확인하여 권한이 없는 경우 알림을 표시하고 리다이렉션합니다.

  • check() 메서드:
    • 요청 매개변수에서 seq 값을 가져옵니다.
    • 세션을 가져옵니다.
    • BoardDAO 객체를 생성합니다.
    • seq 값을 사용하여 해당 글의 정보인 BoardDTO를 가져옵니다.
    • 세션에 저장된 id와 lv를 사용하여 권한을 확인합니다.
      • 세션의 id와 BoardDTO의 id가 다르고, 세션의 lv가 3이 아닌 경우:
        • 알림을 표시하고 홈페이지로 리다이렉션합니다.
        • PrintWriter를 사용하여 알림을 출력합니다.
        • return true를 반환합니다.
    • return false를 반환하여 권한 확인이 성공적으로 이루어졌음을 나타냅니다.

이 코드는 주어진 요청에 대해 글쓴이 본인 또는 관리자인지 확인하여 권한이 없는 경우 알림을 표시하고 리다이렉션하는 기능을 제공합니다.

 

■ Board.java

package com.test.toy.board;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

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 javax.servlet.http.HttpSession;

import com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.BoardDTO;

@WebServlet("/board/board.do")
public class Board extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		test(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		test(req, resp);
	}

	private void test(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//Board.java
		
		//1. DB 작업 > select
		//2. 결과 반환
		
		HttpSession session = req.getSession();
		
		//읽음 제어
		session.setAttribute("read", "n");
		
		
		//board.do == page=1
		//board.do?page=1
		//board.do?page=2
		
		//?page=5
		//?page=5&column=&word= > ""
		//?page=5&column=subject&word=자바 > "subject"
		
		String page = req.getParameter("page");
		
		//페이징
		int nowPage = 0;		//현재 페이지 번호
		int totalCount = 0;		//총 게시물 수
		int pageSize = 10;		//한페이지에서 출력할 게시물 수
		int totalPage = 0;		//총 페이지 수
		int begin = 0;			//
		int end = 0;			//
		int n = 0;				//
		int loop = 0;			//
		int blockSize = 10;		//한번에 보여질 페이지 개수
		
		if (page == null || page == "") nowPage = 1;
		else nowPage = Integer.parseInt(page);
		
		//nowPage > 현재 보려는 페이지 번호
		//board.do?page=1 > where rnum between 1 and 10
		//board.do?page=1 > where rnum between 11 and 20
		//board.do?page=1 > where rnum between 21 and 30
		begin = ((nowPage - 1) * pageSize) + 1;
		end = begin + pageSize - 1;
		
		
		
		
		
		//2가지 용도로 호출
		//1. 일반 목록 보기
		//2. 검색 결과 보기 
		
		//1.
		String column = req.getParameter("column");
		String word = req.getParameter("word");
		String search = "n"; //검색중(O,X)
		
		HashMap<String, String> map = new HashMap<String, String>();
		
		
		if ((column == null && word == null)
	             || (column.endsWith("") && word.equals(""))) {
	         search = "n";
	      } else {
	         search = "y";
	      }
		
//		System.out.println("search" + search);
		
		map.put("column", column);
		map.put("word", word);
		map.put("search", search);
		
		map.put("begin", begin + "");
		map.put("end", end + "");
		
		
		
		BoardDAO dao = new BoardDAO();
		
		List<BoardDTO> list = dao.list(map);
		
		//데이터 조작
		for (BoardDTO dto : list) {
			
			//날짜 출력(기준: 당일)
			//- 년월일 
			//- 시분초
//			System.out.println(dto.getRegdate());
			//dto.setRegdate(dto.getRegdate().substring(0, 10));
			
			String subject = dto.getSubject();
			
			//태그 이스케이프
			subject = subject.replace("<", "&lt;")
					         .replace(">", "&gt;");
			
			//제목에서 검색 중.. 검색어를 강조
			if(search.equals("y") && column.equals("subject")) {
				//새글입니다.
				//<span style="background-color:yellow; color:red;">새글</span>입니다.
				subject = subject.replace(word, "<span style=\"background-color:yellow; color:red;\">" + word + "</span>");
			}
			
			dto.setSubject(subject);
		}
		
		//페이징 작업
		//총 게시물
		totalCount = dao.getTotalCount(map);
		totalPage = (int)Math.ceil((double)totalCount / pageSize);
		
		/*
		 
		  	<a href="/toy/board/board.do?page=1">[이전 10페이지]</a>
			<a href="/toy/board/board.do?page=1">1</a>
			<a href="/toy/board/board.do?page=2">2</a>
			<a href="/toy/board/board.do?page=3">3</a>
			<a href="/toy/board/board.do?page=4">4</a>
			<a href="/toy/board/board.do?page=5">5</a>
			<a href="/toy/board/board.do?page=6">6</a>
			<a href="/toy/board/board.do?page=7">7</a>
			<a href="/toy/board/board.do?page=8">8</a>
			<a href="/toy/board/board.do?page=9">9</a>
			<a href="/toy/board/board.do?page=10">10</a>
			<a href="/toy/board/board.do?page=1">[다음 10페이지]</a>
		 
		 */
		
		StringBuilder sb = new StringBuilder();
		
//		for (int i=1; i<=totalPage; i++) {
//			if (i == nowPage) {
//				sb.append(String.format(" <a href=\"#!\" style='color:tomato;'>%d</a> ", i));				
//			} else {
//				sb.append(String.format(" <a href=\"/toy/board/board.do?page=%d\">%d</a> ", i, i));			
//			}
//		}
		
		//board.do?page=1
		//[] 1 2 3 4 5 6 7 8 9 10 []
		
		//board.do?page=5
		//[] 1 2 3 4 5 6 7 8 9 10 []
		
		//board.do?page=10
		//[] 1 2 3 4 5 6 7 8 9 10 []
				
		//board.do?page=11
		//[] 11 12 13 14 15 16 17 18 19 20 []
		
		loop = 1;	//루프 변수
		n = ((nowPage -1) / blockSize) * blockSize + 1; //페이지 번호
		
		//이전 10페이지
		if(n == 1) {
			sb.append(String.format("<a href=\"#!\">[이전 %d페이지]</a>",blockSize ));			
		}else {
			sb.append(String.format("<a href=\"/toy/board/board.do?page=%d\">[이전 %d페이지]</a>", n-1 ,blockSize ));						
		}
		
		while (!(loop > blockSize || n > totalPage)) {
			
			if (n == nowPage) {
				sb.append(String.format(" <a href=\"#!\" style='color:tomato;'>%d</a> ", n));				
			} else {
				sb.append(String.format(" <a href=\"/toy/board/board.do?page=%d\">%d</a> ", n, n));			
			}
			
			loop++;
			n++;
		}
		
		//다음 10페이지
		if(n > totalPage) {
			sb.append(String.format("<a href=\"#!\">[다음 %d페이지]</a>", blockSize));
		}else {
			sb.append(String.format("<a href=\"/toy/board/board.do?page=%d\">[다음 %d페이지]</a>", n, blockSize));			
		}

		req.setAttribute("list", list);
		req.setAttribute("map", map);
		req.setAttribute("totalCount", totalCount);
		req.setAttribute("totalPage", totalPage);
		req.setAttribute("nowPage", nowPage);
		req.setAttribute("pagination", sb);
		
		//JSP 페이지 알 수 있는 정보
		//1. req를 통해서 전달된 정보
		//2. session/cookie
		//3. application 
		
		RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/board/board.jsp");
		dispatcher.forward(req, resp);
	}

}
  • 코드 분석
더보기

게시판 기능을 담당하는 Board 클래스입니다. 클라이언트로부터의 GET 또는 POST 요청에 대해 처리합니다.

  • doGet() 메서드와 doPost() 메서드:
    • test() 메서드를 호출하여 요청 처리를 수행합니다.
  • test() 메서드:
    • 세션을 가져옵니다.
    • 세션에 read 속성을 설정하여 읽음 제어를 처리합니다.
    • 요청 매개변수에서 page 값을 가져옵니다.
    • 페이징 관련 변수들을 초기화합니다.
    • page 값이 없는 경우 nowPage를 1로 설정하고, 그렇지 않은 경우 정수로 변환하여 nowPage에 저장합니다.
    • 현재 페이지에 따라 begin과 end 값을 계산합니다.
    • 검색 관련 변수들을 초기화합니다.
    • column과 word 매개변수를 확인하여 검색 상태인지 여부를 결정합니다. map에 관련 정보를 저장합니다.
    • BoardDAO 객체를 생성합니다.
    • list() 메서드를 호출하여 게시물 목록을 가져옵니다.
    • 가져온 게시물 목록을 조작하여 데이터를 가공합니다.
      • 날짜 출력 형식을 변경합니다.
      • 태그 이스케이프를 처리합니다.
      • 검색 중인 경우 제목에서 검색어를 강조합니다.
    • getTotalCount() 메서드를 호출하여 총 게시물 수를 가져옵니다.
    • 페이징 작업을 수행합니다.
    • 이전/다음 페이지 링크 및 페이지 번호 링크를 생성합니다.
    • 생성한 링크를 StringBuilder에 추가합니다.
    • JSP 페이지에 필요한 정보들을 속성으로 설정합니다.
    • RequestDispatcher를 사용하여 JSP 페이지로 포워딩합니다.

이 코드는 게시판의 목록 조회, 페이징 처리, 검색 기능을 구현하고, JSP 페이지로 데이터를 전달하여 출력합니다.

 

■ Comment.java

package com.test.toy.board;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;

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 javax.servlet.http.HttpSession;

import com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.CommentDTO;

@WebServlet("/board/comment.do")
public class Comment extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//Comment.java
		HttpSession session = req.getSession();
		
		String content = req.getParameter("content");
		String bseq = req.getParameter("bseq");
		
		String column = req.getParameter("column");
		String word = req.getParameter("word");
		String search = req.getParameter("search");
		
		BoardDAO dao = new BoardDAO();
		CommentDTO cdto = new CommentDTO();
		
		cdto.setContent(content);
		cdto.setBseq(bseq);
		cdto.setId((String)session.getAttribute("id"));
		
		int result = dao.addContent(cdto);
		
		if (result == 1) {
			resp.sendRedirect("/toy/board/view.do?seq=" + bseq + "&column=" + column + "&word=" + URLEncoder.encode(word, "UTF-8")  + "&search=" + search);
		} else {
			PrintWriter writer = resp.getWriter();
			writer.print("<script>alert('failed');history.back();</script>");
			writer.close();
		}
		
	}

}
  • 코드 분석
더보기

댓글 기능을 처리하는 Comment 클래스입니다. 클라이언트로부터의 POST 요청에 대해 처리합니다.

  • doPost() 메서드:
    • 세션을 가져옵니다.
    • 요청 매개변수에서 content, bseq, column, word, search 값을 가져옵니다.
    • BoardDAO 객체를 생성합니다.
    • CommentDTO 객체를 생성하고 매개변수로 전달받은 값을 설정합니다.
    • addContent() 메서드를 호출하여 댓글을 추가합니다.
    • 결과에 따라 처리합니다.
      • 성공한 경우: resp.sendRedirect()를 사용하여 해당 게시물의 상세 페이지로 리다이렉트합니다. 게시물 번호와 검색 관련 매개변수를 함께 전달합니다.
      • 실패한 경우: PrintWriter를 사용하여 실패 메시지를 출력하고 이전 페이지로 되돌아갑니다.

 

■ Del.java

package com.test.toy.board;

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 com.test.toy.board.repository.BoardDAO;

@WebServlet("/board/del.do")
public class Del extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//Del.java
		
		//del.do?seq=10
		
		if (Auth.check(req, resp)) {
			return;
		};
		
		String seq = req.getParameter("seq");
		
		req.setAttribute("seq", seq);

		RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/board/del.jsp");
		dispatcher.forward(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		String seq = req.getParameter("seq");
		
		BoardDAO dao = new BoardDAO();
		
		//삭제될 글에 달린 댓글이 달렸는지 확인 > 있으면 삭제 
		int result = 0;
		
		result = dao.delComment(seq); //댓글 삭제
		
		result *= dao.del(seq); //글 삭제
		
		if (result >= 1) {
			resp.sendRedirect("/toy/board/board.do");
		} else {
			PrintWriter writer = resp.getWriter();
			writer.print("<script>alert('failed');history.back();</script>");
			writer.close();
		}
		
	}

}
  • 코드 분석
더보기

게시물 삭제 기능을 처리하는 Del 클래스입니다. 클라이언트로부터의 GET 및 POST 요청에 대해 처리합니다.

  • doGet() 메서드:
    • Auth.check() 메서드를 호출하여 권한을 확인합니다.
    • seq 매개변수를 가져와서 요청 속성으로 설정합니다.
    • RequestDispatcher를 사용하여 삭제 페이지(del.jsp)로 포워딩합니다.
  • doPost() 메서드:
    • seq 매개변수를 가져옵니다.
    • BoardDAO 객체를 생성합니다.
    • 댓글을 삭제하기 위해 delComment() 메서드를 호출합니다.
    • 게시물을 삭제하기 위해 del() 메서드를 호출합니다.
    • 결과에 따라 처리합니다.
      • 성공한 경우: resp.sendRedirect()를 사용하여 게시판 페이지로 리다이렉트합니다.
      • 실패한 경우: PrintWriter를 사용하여 실패 메시지를 출력하고 이전 페이지로 되돌아갑니다.

 

■ DelComment.java

package com.test.toy.board;

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

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.test.toy.board.repository.BoardDAO;

@WebServlet("/board/delcomment.do")
public class DelComment extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//DelComment.java

		String seq = req.getParameter("seq");
		String cseq = req.getParameter("cseq");
		
		BoardDAO dao = new BoardDAO();
		
		int result = dao.delComment2(cseq);
		
	   if (result == 1) {
         resp.sendRedirect("/toy/board/view.do?seq=" + seq);
       } else {
         PrintWriter writer = resp.getWriter();
         writer.print("<script>alert('failed'); history.back();</script>");
         writer.close();
       }
	}

}
  • 코드 분석
더보기

댓글 삭제 기능을 처리하는 DelComment 클래스입니다. 클라이언트로부터의 GET 요청에 대해 처리합니다.

  • doGet() 메서드:
    • seq와 cseq 매개변수를 가져옵니다.
    • BoardDAO 객체를 생성합니다.
    • delComment2() 메서드를 호출하여 댓글을 삭제합니다.
    • 결과에 따라 처리합니다.
      • 성공한 경우: resp.sendRedirect()를 사용하여 게시물 페이지로 리다이렉트합니다.
      • 실패한 경우: PrintWriter를 사용하여 실패 메시지를 출력하고 이전 페이지로 되돌아갑니다.

 

■ Dummy.java

package com.test.toy.board;

import java.sql.Connection;
import java.sql.PreparedStatement;

import com.test.my.DBUtil;

public class Dummy {

	public static void main(String[] args) throws Exception{
		
		Connection conn = null;
		PreparedStatement stat = null;
		
		conn = DBUtil.open();
		
		String sql = "insert into tblBoard values (seqBoard.nextVal, ?, '내용', 'admin', default, default)";
		
		stat = conn.prepareStatement(sql);
		
		for (int i=0; i<250; i++) {
			
			stat.setString(1, "게시판 페이징 처리 테스트중입니다.." + i);
			stat.executeUpdate();
			System.out.println(i);
			
		}
		
		stat.close();
		conn.close();
		
		
	}
	
}
  • 코드 분석
더보기

주어진 SQL 문을 사용하여 데이터베이스에 더미 게시물을 추가합니다. 주로 게시판 페이징 처리 테스트를 위해 사용될 수 있습니다.

  • main() 메서드:
    • 데이터베이스 연결을 위해 DBUtil.open() 메서드를 호출하여 Connection 객체를 가져옵니다.
    • SQL 문을 준비하기 위해 PreparedStatement 객체를 생성합니다.
    • 반복문을 사용하여 250개의 더미 게시물을 생성합니다.
      • stat.setString()을 사용하여 게시물 제목을 설정합니다.
      • stat.executeUpdate()를 호출하여 SQL 문을 실행하여 게시물을 데이터베이스에 추가합니다.
      • 반복 횟수를 출력합니다.
    • 사용한 자원을 정리합니다. (stat와 conn을 닫습니다.)

 

■ Edit.java

package com.test.toy.board;

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 com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.BoardDTO;

@WebServlet("/board/edit.do")
public class Edit extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//Edit.java
		//1. 데이터 가져오기(글번호)
		//2. DB 작업 > select
		//3. 결과 > JSP 호출하기
		
		//1.
		String seq = req.getParameter("seq");
		
		BoardDAO dao = new BoardDAO();
		BoardDTO dto = dao.get(seq);
		
		req.setAttribute("dto", dto);

		RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/board/edit.jsp");
		dispatcher.forward(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		//EditOk.java
		//1. 데이터 가져오기(수정할 값)
		//2. DB작업 > update
		//3. 결과
		
		//권한 체크 -> 링크로 들어오는 것을 막음.
		if (Auth.check(req, resp)) {
			return;
		};
		
		//1.
		String seq = req.getParameter("seq");
		String subject = req.getParameter("subject");
		String content = req.getParameter("content");
		
		//2.
		BoardDAO dao = new BoardDAO();
		BoardDTO dto = new BoardDTO();
		dto.setSeq(seq);
		dto.setSubject(subject);
		dto.setContent(content);
		
		int result = dao.edit(dto);
		
		if (result == 1) {
			resp.sendRedirect("/toy/board/view.do?seq=" + seq);
		} else {
			PrintWriter writer = resp.getWriter();
			writer.print("<script>alert('failed');history.back();</script>");
			writer.close();
		}
		
	}

}
  • 코드 분석
더보기

게시물을 수정하는 기능을 제공하는 Edit 클래스입니다.

  • doGet() 메서드:
    • 클라이언트로부터 요청된 게시물 번호(seq)를 가져옵니다.
    • BoardDAO를 사용하여 해당 게시물을 조회합니다.
    • 조회된 게시물(BoardDTO)을 요청 속성에 설정합니다.
    • RequestDispatcher를 사용하여 수정 페이지(edit.jsp)로 포워딩합니다.
  • doPost() 메서드:
    • 권한 체크를 위해 Auth.check() 메서드를 호출하여 인증 상태를 확인합니다.
    • 요청 파라미터에서 수정할 게시물 번호(seq), 제목(subject), 내용(content)을 가져옵니다.
    • BoardDAO를 사용하여 게시물 객체(BoardDTO)를 생성하고 수정할 값들을 설정합니다.
    • dao.edit()를 호출하여 게시물을 수정하고, 결과 값을 반환받습니다.
    • 수정이 성공적으로 이루어지면 해당 게시물의 상세 페이지로 리다이렉트합니다.
    • 수정이 실패하면 실패 메시지를 출력하고 이전 페이지로 돌아갑니다.

 

■ EditComment.java

package com.test.toy.board;

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

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.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.CommentDTO;

@WebServlet("/board/editcomment.do")
public class EditComment extends HttpServlet {

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

      //EditComment.java
      String bseq = req.getParameter("bseq");
      String cseq = req.getParameter("cseq");
      String content = req.getParameter("content");
      
      
      BoardDAO dao = new BoardDAO();
      CommentDTO cdto = new CommentDTO();
      
      cdto.setSeq(cseq);
      //cdto.setBseq(bseq);
      cdto.setContent(content);
      
      int result = dao.editComment(cdto);
      
      if (result == 1) {
         resp.sendRedirect("/toy/board/view.do?seq=" + bseq);
      } else {
         PrintWriter writer = resp.getWriter();
         writer.print("<script>alert('failed'); history.back();</script>");
         writer.close();
      }
      
      
   }

}
  • 코드 분석
더보기

댓글을 수정하는 기능을 제공하는 EditComment 클래스입니다.

  • doPost() 메서드:
    • 요청 파라미터에서 수정할 댓글의 게시물 번호(bseq), 댓글 번호(cseq), 내용(content)을 가져옵니다.
    • BoardDAO를 사용하여 댓글 객체(CommentDTO)를 생성하고 수정할 값들을 설정합니다.
    • dao.editComment()를 호출하여 댓글을 수정하고, 결과 값을 반환받습니다.
    • 수정이 성공적으로 이루어지면 해당 게시물의 상세 페이지로 리다이렉트합니다.
    • 수정이 실패하면 실패 메시지를 출력하고 이전 페이지로 돌아갑니다.

 

■ View.java

package com.test.toy.board;

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

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 javax.servlet.http.HttpSession;

import com.test.toy.board.repository.BoardDAO;
import com.test.toy.board.repository.BoardDTO;
import com.test.toy.board.repository.CommentDTO;

@WebServlet("/board/view.do")
public class View extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		//View.java
		
		//1. 데이터 가져오기(seq)
		//2. DB 작업 > select	
		//3. 결과 > JSP 호출하기
		
		HttpSession session = req.getSession();
		
		//무분별한 조회 막기
		//- 무분별 새로 고침 > 조회수 증가X
		//- 다른 경로를 통해서 유입
		//	- list.do > view.do > 조회수 증가O
		//	- view.do 즐겨찾기 > 조회수 증가O
		//  - 링크 > 조회수 증가 O
		
		//요철 헤더 > referer
		//현재 페이지릴 보기전에 어디서 왔는지? 이전 페이지 주소반환
		//String referer = req.getHeader("referer");
		//System.out.println(referer);
		
		
		//1.
		String seq = req.getParameter("seq");
		
		String column = req.getParameter("column");
		String word = req.getParameter("word");
		String search = req.getParameter("search");
		
		//2.
		BoardDAO dao = new BoardDAO();
		
		//무분별한 조회수 막기 ( 새로고침 막기 )
		if (session.getAttribute("read") == null
					|| session.getAttribute("read").toString().equals("n")) {
			
			//조회수 증가
			dao.updateReadCount(seq);
			session.setAttribute("read", "y");
		}
		
		
		BoardDTO dto = dao.get(seq);
		
		
		String content = dto.getContent();
		
		//HTML 태그 이스케이프
		content = content.replace("<", "&lt;")
						 .replace(">", "&gt;");
		
		//글 내용 개행 문자 처리
		content = content.replace("\r\n", "<br>");
		
		//내용으로 검색중.. 검색어 강조!!
		if(search.equals("y") && column.equals("content")) {
			//새글입니다.
			//<span style="background-color:yellow; color:red;">새글</span>입니다.
			content = content.replace(word, "<span style=\"background-color:yellow; color:red;\">" + word + "</span>");
		}
		
		dto.setContent(content);
		
		String subject = dto.getSubject();
		
		//태그 이스케이프
		subject = subject.replace("<", "&lt;")
				         .replace(">", "&gt;");
		
		dto.setSubject(subject);
		
		
		//댓글 목록 가져오기 
		List<CommentDTO> clist = dao.clist(seq);
		
		
		
		
		//3
		req.setAttribute("dto", dto);
		req.setAttribute("clist", clist);
		req.setAttribute("column", column);
		req.setAttribute("word", word);
		req.setAttribute("search", search);

		RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/board/view.jsp");
		dispatcher.forward(req, resp);
	}

}
  • 코드 분석
더보기

게시물 상세 페이지를 보여주는 View 클래스입니다.

  • doGet() 메서드:
    • 요청 파라미터에서 게시물 번호(seq), 검색 관련 파라미터(column, word, search)를 가져옵니다.
    • 세션을 사용하여 조회수 증가 여부를 체크합니다.
    • BoardDAO를 사용하여 게시물 정보(BoardDTO)와 댓글 목록(CommentDTO)을 가져옵니다.
    • 게시물의 내용을 HTML 태그 이스케이프 처리하고 개행 문자를 <br> 태그로 변환합니다.
    • 만약 검색 관련 파라미터가 있고, 검색 대상이 "content"인 경우 검색어를 강조하여 표시합니다.
    • 게시물 제목도 HTML 태그 이스케이프 처리합니다.
    • 게시물 정보(dto), 댓글 목록(clist), 검색 관련 파라미터를 설정한 후, view.jsp로 포워딩합니다.

 

★ 소스코드 분석 - com.test.toy.board.repository

 

■ BoardDAO

package com.test.toy.board.repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.fasterxml.jackson.databind.ser.std.MapProperty;
import com.test.my.DBUtil;
import com.test.toy.board.Comment;

public class BoardDAO {

	private Connection conn;
	private Statement stat;
	private PreparedStatement pstat;
	private ResultSet rs;
	
	public BoardDAO() {
		this.conn = DBUtil.open();
	}

	public int add(BoardDTO dto) {
		
		try {

			String sql = "insert into tblBoard (seq, subject, content, id, regdate, readcount, thread, depth) values (seqBoard.nextval, ?, ?, ?, default, default, ?, ?)";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, dto.getSubject());
			pstat.setString(2, dto.getContent());
			pstat.setString(3, dto.getId());
			
			pstat.setInt(4, dto.getThread());
			pstat.setInt(5, dto.getDepth());
			

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	//Board 서블릿이 게시판 목록 출력 요청
	public List<BoardDTO> list(HashMap<String, String> map) {
		
		List<BoardDTO> list = new ArrayList<BoardDTO>();
		
		try {
			
			String where ="";
			
			if (map.get("search").equals("y")) {
	            where = String.format("where %s like '%%%s%%'"
	                              , map.get("column")
	                              , map.get("word"));
	         }

	         String sql = String.format("select * from (select a.*, rownum as rnum from vwBoard a %s) where rnum between %s and %s "
	                              , where
	                              , map.get("begin")
	                              , map.get("end")
	                              );

			stat = conn.createStatement();
			rs = stat.executeQuery(sql);

			

			while (rs.next()) {
				BoardDTO dto = new BoardDTO();

				dto.setSeq(rs.getString("seq"));
				dto.setSubject(rs.getString("subject"));
				dto.setId(rs.getString("id"));
				dto.setRegdate(rs.getString("regdate"));
				dto.setReadcount(rs.getString("readcount"));
				
				dto.setName(rs.getString("name"));
				
				dto.setIsnew(rs.getDouble("isnew"));
				
				dto.setCcnt(rs.getString("ccnt"));
				
				dto.setDepth(rs.getInt("depth"));
				
				list.add(dto);
			}
			return list;

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return list;
	}

	//View 서블릿이 번호를 줄테니 레코드 1줄(DTO)를 주세요~
	public BoardDTO get(String seq) {
		
		try {

			String sql = "select \r\n"
					+ "    tblBoard.*,\r\n"
					+ "    (select name from tblUser where id = tblBoard.id) as name\r\n"
					+ "from tblBoard\r\n"
					+ "    where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, seq);

			rs = pstat.executeQuery();

			if (rs.next()) {

				BoardDTO dto = new BoardDTO();

				dto.setSeq(rs.getString("seq"));
				dto.setSubject(rs.getString("subject"));
				dto.setContent(rs.getString("content"));
				dto.setId(rs.getString("id"));
				dto.setRegdate(rs.getString("regdate"));
				dto.setReadcount(rs.getString("readCount"));
				dto.setName(rs.getString("name"));
				
				dto.setThread(rs.getInt("thread"));
				dto.setDepth(rs.getInt("depth"));
				
				return dto;
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}

	//View 서블릿이 글번호를 건내주면, 조회수 +1 증가
	public void updateReadCount(String seq) {
		
		try {

			String sql = "update tblBoard set readcount = readcount + 1 where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, seq);

			pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	//Edit 서블릿이 DTO를 건내주면, 수정
	public int edit(BoardDTO dto) {
		
		try {

			String sql = "update tblBoard set subject = ?, content = ? where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, dto.getSubject());
			pstat.setString(2, dto.getContent());
			pstat.setString(3, dto.getSeq());

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public int del(String seq) {
		
		try {

			String sql = "delete from tblBoard where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, seq);

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public int addContent(CommentDTO cdto) {
		
		try {

			String sql = "insert into tblComment (seq, content, id, regdate, bseq) values (seqComment.nextVal, ?, ?, default, ?)";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, cdto.getContent());
			pstat.setString(2, cdto.getId());
			pstat.setString(3, cdto.getBseq());

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	//View 서블릿이 현재 보고 있는 글의 딸려있는 댓글 목록을 출력.
	public List<CommentDTO> clist(String bseq) {
		
		try {

			String sql = "select tblComment.*, (select name from tblUser where id = tblComment.id) as name from tblComment where bseq = ? order by seq desc";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, bseq);

			rs = pstat.executeQuery();

			List<CommentDTO> list = new ArrayList<CommentDTO>();

			while (rs.next()) {

				CommentDTO dto = new CommentDTO();

				dto.setSeq(rs.getString("seq"));
				dto.setContent(rs.getString("content"));
				dto.setId(rs.getString("id"));
				dto.setRegdate(rs.getString("regdate"));
				dto.setBseq(rs.getString("bseq"));
				dto.setName(rs.getString("name"));
				
				list.add(dto);
			}
			return list;

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	//Del 서블릿이 글번호를 줄테니, 글에 달려있는 댓글도 다 삭제해주세요.
	public int delComment(String bseq) {
	
		try {

			String sql = "delete from tblComment where bseq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, bseq);

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public int editComment(CommentDTO cdto) {
		
		try {

			String sql = "update tblComment set content = ? where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, cdto.getContent());
			pstat.setString(2, cdto.getSeq());

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public int delComment2(String cseq) {
		
		try {

			String sql = "delete from tblComment where seq = ?";

			pstat = conn.prepareStatement(sql);

			pstat.setString(1, cseq);

			return pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	//Board 서블릿이 게시물 총개수를 달라고 요청
	public int getTotalCount(HashMap<String, String> map) {
		
		try {

			String where ="";
			
			if (map.get("search").equals("y")) {
				where = String.format("where %s like '%%%s%%'", map.get("column"), map.get("word") );
			}

			String sql = String.format("select count(*) as cnt from vwBoard %s", where);

			pstat = conn.prepareStatement(sql);

			rs = pstat.executeQuery();

			if (rs.next()) {

				return rs.getInt("cnt");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public int getMaxThread() {
		
		try {

			String sql = "select nvl(max(thread), 0) as thread from tblBoard";

			stat = conn.createStatement();
			rs = stat.executeQuery(sql);

			if (rs.next()) {
				return rs.getInt("thread");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}

	public void updateThread(HashMap<String, Integer> map) {
		
		try {

			String sql = "update tblBoard set thread = thread -1 where thread > ? and thread < ?";

			pstat = conn.prepareStatement(sql);

			pstat.setInt(1, map.get("previousThread"));
			pstat.setInt(2, map.get("parentThread"));

			pstat.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	
}
  • 코드 분석
더보기

웹 애플리케이션의 게시판 기능과 관련된 데이터베이스 작업을 수행하는 역할을 합니다.

아래는 코드의 구성 요소에 대한 설명입니다:

  • 코드는 필요한 자바 클래스와 패키지를 import 문으로 가져옵니다.
  • BoardDAO 클래스는 데이터베이스에 대한 연결을 수립하는 생성자를 가지고 있습니다. 생성자에서는 DBUtil 클래스의 open 메서드를 호출하여 데이터베이스 연결을 설정합니다.
  • add 메서드는 게시글을 추가하는 기능을 수행합니다. SQL 쿼리를 사용하여 데이터를 입력하고, PreparedStatement를 통해 SQL 문의 매개변수를 설정합니다.
  • list 메서드는 게시판 목록을 가져오는 기능을 수행합니다. HashMap을 매개변수로 받아와서 검색 조건에 따라 SQL 쿼리를 동적으로 생성하고, 결과를 List<BoardDTO>로 반환합니다.
  • get 메서드는 게시글의 상세 내용을 가져오는 기능을 수행합니다. 게시글 번호를 매개변수로 받아와서 해당하는 게시글 정보를 조회하여 BoardDTO 객체에 저장하여 반환합니다.
  • updateReadCount 메서드는 게시글의 조회수를 증가시키는 기능을 수행합니다. 게시글 번호를 매개변수로 받아와서 해당하는 게시글의 조회수를 1 증가시킵니다.
  • edit 메서드는 게시글을 수정하는 기능을 수행합니다. BoardDTO 객체를 매개변수로 받아와서 해당하는 게시글의 제목과 내용을 수정합니다.
  • del 메서드는 게시글을 삭제하는 기능을 수행합니다. 게시글 번호를 매개변수로 받아와서 해당하는 게시글을 삭제합니다.
  • addContent 메서드는 댓글을 추가하는 기능을 수행합니다. CommentDTO 객체를 매개변수로 받아와서 해당하는 게시글에 댓글을 등록합니다.
  • clist 메서드는 게시글에 달린 댓글 목록을 가져오는 기능을 수행합니다. 게시글 번호를 매개변수로 받아와서 해당하는 게시글에 달린 댓글 목록을 조회하여 List<CommentDTO>로 반환합니다.
  • 다른 메서드들은 댓글 삭제, 수정 등과 관련된 기능을 수행합니다.
  • 마지막으로 getTotalCount와 getMaxThread 메서드는 게시글의 총 개수와 가장 큰 스레드 값을 가져오는 기능을 수행합니다.

 

■ BoardDTO.java

package com.test.toy.board.repository;

import lombok.Data;

@Data
public class BoardDTO {
	
	private String seq;
	private String subject;
	private String content;
	private String id;
	private String regdate;
	private String readcount;
	
	
	private String name; //작성자
	
	private double isnew; //최신글
	
	private String ccnt; //현재글에 달린 댓글 개수
	
	private int thread;
	private int depth;
}

 

■ CommentDTO.java

package com.test.toy.board.repository;

import lombok.Data;

@Data
public class CommentDTO {
	private String seq;
	private String content;
	private String id;
	private String regdate;
	private String bseq;
	private String name;
}

 

★ JSP 파일

 

■ add.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Toy Project</title>

<%@ include file="/WEB-INF/views/inc/asset.jsp" %>

<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	<main id="main">
		<h1 class="page">게시판 
		
		<c:if test="${mode == 'new'}">
			<small>글쓰기</small>
		</c:if>
		<c:if test="${mode == 'reply'}">
			<small>답변쓰기</small>
		</c:if>
		</h1>
		
		<form method="POST" action="/toy/board/add.do">
		<table class="vertical">
			<tr>
				<th>제목</th>
				<td><input type="text" name="subject" id="subject" required class="full"></td>
			</tr>
			<tr>
				<th>내용</th>
				<td><textarea name="content" id="content" required class="full"></textarea></td>
			</tr>
		</table>
		<div>
			<button type="button" class=back"
				onclick="location.href='/toy/board/board.do';">돌아가기</button>
			<button type="submit" class="add primary">글쓰기</button>
		</div>
		<input type="hidden" name="mode" value="${mode}">
		<input type="hidden" name="thread" value="${thread}">
		<input type="hidden" name="depth" value="${depth}">
		</form>
		
	</main>




<script>
</script>
</body>
</html>
  • 코드 분석
더보기

JSP(JavaServer Pages)를 사용하여 구현된 게시판의 글쓰기 기능을 담당하는 페이지입니다. 아래는 코드의 구성과 기능에 대한 설명입니다.

  • 첫 번째 줄에서는 페이지의 언어를 지정하고, 문자 인코딩을 UTF-8로 설정하고 있습니다.
  • JSTL(Core 태그 라이브러리)을 사용하기 위해 태그 라이브러리를 import하고 있습니다.
  • <head> 태그 내부에서는 페이지의 제목과 필요한 CSS 파일을 포함시키는 등의 작업을 수행합니다. 현재는 스타일이 지정되어 있지 않은 상태입니다.
  • <body> 태그 내부에서는 게시판 페이지의 레이아웃을 구성합니다.
  • <%@ include file="/WEB-INF/views/inc/header.jsp" %>를 사용하여 게시판 페이지의 상단에 헤더 부분을 포함시키고 있습니다.
  • <main> 태그 내부에서는 게시판의 제목과 글 작성 폼을 표시합니다. 제목은 "게시판"이며, 글 작성 모드(mode)에 따라 "글쓰기" 또는 "답변쓰기"로 표시됩니다.
  • <form> 태그를 사용하여 글 작성 폼을 구성하고 있습니다. 폼은 POST 메서드로 전송되며, /toy/board/add.do 주소로 전송됩니다.
  • 테이블을 사용하여 제목과 내용을 입력하는 필드를 구성하고 있습니다. 각 필드는 <input> 및 <textarea> 태그를 사용하여 표시되며, 필수 입력 사항(required)으로 지정되어 있습니다.
  • <button> 요소를 사용하여 "돌아가기" 및 "글쓰기" 버튼을 표시하고 있습니다. "돌아가기" 버튼은 이전 페이지로 돌아가는 역할을 하며, "글쓰기" 버튼은 폼을 제출하는 역할을 합니다.
  • <input> 요소를 사용하여 mode, thread, depth와 같은 숨겨진(hidden) 필드를 생성하여 폼 데이터와 함께 전송됩니다. 이러한 필드는 폼 데이터와 관련된 정보를 서버로 전달하는 데 사용됩니다.
  • <script> 태그 내부에서는 JavaScript 코드를 작성할 수 있으며, 현재는 비어있는 상태입니다.

 

■ board.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Toy Project</title>

<%@ include file="/WEB-INF/views/inc/asset.jsp" %>

<style>

	#list th:nth-child(1) {width : 50px;}
	#list th:nth-child(2) {width : auto;}
	#list th:nth-child(3) {width : 70px;}
	#list th:nth-child(4) {width : 120px;}
	#list th:nth-child(5) {width : 50px;}
	
	#list td { text-align: center; }
	#list td:nth-child(2) { text-align: left; }
	
	.isnew {
		font-size: 14px;
		color: red;
	}
	
	.comment-count {
		font-size: 12px;
		color: #777;
	}
	
	#searchForm {
		margin-bottom: 15px;
		text-align: center;
	}
	
	#searchForm > * {
		box-sizing: border-box;
		height: 34px;
	}

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	<main id="main">
		<h1 class="sub">
			게시판 
			<c:if test="${map.search == 'n'}">
			<small>목록</small>
			</c:if>
			<c:if test="${map.search == 'y'}">
			<small>검색</small>
			</c:if>
			
			<%--
			<span id="pagebar" style="float: right; margin-top: -5px;">
				<input type="number" id="page" class="short" min="1" max="${totalPage}" value="${nowPage}">
				<input type="button" value="이동" onclick="location.href='/toy/board/board.do?page=' + $('#page').val() + '&column=${map.column}&word=${map.word}';">
			</span>
			 --%>
			 
			 
			<span id="pagebar" style="float: right;">
				<select onchange="location.href='/toy/board/board.do?page=' + $(this).val() + '&column=${map.column}&word=${map.word}';">
					<c:forEach var="i" begin="1" end="${totalPage}">
					<option value="${i}" <c:if test="${i == nowPage}">selected</c:if>>${i}페이지</option>
					</c:forEach>
				</select>
			</span>
			
			
		</h1>
		<c:if test="${map.search == 'y'}">
		<div style="text-align: center;">
			'${map.word}'(으)로 검색한 결과 ${totalCount}건이 있습네다.
		</div>
		</c:if>
		
		<table id="list">
			<tr>
				<th>번호</th>
				<th>제목</th>
				<th>이름</th>
				<th>날짜</th>
				<th>읽음</th>
			</tr>
			<c:if test="${list.size() == 0}">
			<tr>
				<td colspan="5">게시물이 없습니다.</td>
			</tr>
			</c:if>
			<c:forEach items="${list}" var="dto">
			<tr>
				<td>
					<c:if test="${dto.depth == 0}">
						${dto.seq}
					</c:if>
					<c:if test="${dto.depth > 0}">
						답변
					</c:if>
				</td>
					
				<td>
					<c:if test="${dto.depth > 0}">
						<span class="material-symbols-outlined" style="font-size:15px; margin-left: ${dto.depth * 20}px;">
						subdirectory_arrow_right
						</span>
					</c:if>
					
					<!-- 제목(링크) -->
					<a href="/toy/board/view.do?seq=${dto.seq}&column=${map.column}&word=${map.word}&search=${map.search}">${dto.subject}</a>
					
					<!-- 댓글 개수 -->
					<c:if test="${dto.ccnt > 0}">
					<span class="comment-count">(${dto.ccnt})</span>
					</c:if>
					<!-- 새글 표시 -->
					<c:if test="${dto.isnew < 30 / 24 / 60}">
						<span class="isnew">new</span>
					</c:if>
				</td>
				<td>${dto.name}</td>
				<td>${dto.regdate}</td>
				<td>${dto.readcount}</td>
			</tr>
			</c:forEach>
		</table>
		
		<!-- <form method="GET"> 사용 사례 -->
		<form id="searchForm" action="/toy/board/board.do" method="GET">
			<select name="column">
				<option value="subject">제목</option>
				<option value="content">내용</option>
				<option value="name">이름</option>
			</select>
			<input type="text" name="word" class="long" required>
			<input type="submit" value="검색하기">
		</form>
		
		<div id="pagination" style="text-align : center; margin-bottom: 10px;">${pagination}</div>
		
		<div>
			<c:if test="${not empty id}">
			<button type="button" class="add Primary" onclick="location.href='/toy/board/add.do?mode=new';">글쓰기</button>
			</c:if>
			<button type="button" class="list Primary" onclick="location.href='/toy/board/board.do';">목록보기</button>
			
		</div>
		
	</main>




<script>

	<c:if test="${map.search == 'y'}">
	$('select[name=column]').val('${map.column}');
	$('input[name=word]').val('${map.word}');
	</c:if>
</script>
</body>
</html>
  • 코드 분석
더보기

JSP(JavaServer Pages)를 사용하여 구현된 게시판의 목록 페이지입니다. 아래는 코드의 구성과 기능에 대한 설명입니다.

  • 첫 번째 줄에서는 페이지의 언어를 지정하고, 문자 인코딩을 UTF-8로 설정하고 있습니다.
  • JSTL(Core 태그 라이브러리)을 사용하기 위해 태그 라이브러리를 import하고 있습니다.
  • <head> 태그 내부에서는 페이지의 제목과 필요한 CSS 파일을 포함시키는 등의 작업을 수행합니다. 현재는 스타일이 지정되어 있으며, 게시판 목록 테이블의 각 열의 너비(width) 등을 설정하고 있습니다.
  • <body> 태그 내부에서는 게시판 페이지의 레이아웃을 구성합니다.
  • <%@ include file="/WEB-INF/views/inc/header.jsp" %>를 사용하여 게시판 페이지의 상단에 헤더 부분을 포함시키고 있습니다.
  • <main> 태그 내부에서는 게시판의 목록을 표시합니다. 게시판의 제목과 현재 목록인지 검색 결과인지에 따라 다른 내용이 표시됩니다.
  • 검색 결과를 표시하는 경우, 검색된 결과 개수와 검색어를 표시하는 부분이 추가됩니다.
  • <table> 태그를 사용하여 게시판 목록을 표시하는 테이블을 구성하고 있습니다. 테이블의 각 열은 번호, 제목, 이름, 날짜, 읽음 수로 구성되어 있습니다.
  • ${list} 객체를 사용하여 게시판의 글 목록을 반복적으로 표시합니다. 각 글의 번호, 제목, 작성자, 작성일, 읽음 수를 표시합니다.
  • 검색 폼을 구성하여 사용자가 원하는 조건으로 게시판을 검색할 수 있도록 합니다. 검색어와 검색 대상을 선택하는 필드와 검색 버튼이 표시됩니다.
  • 페이지네이션을 표시하는 부분이 추가됩니다. 사용자는 페이지를 선택하여 다른 페이지로 이동할 수 있습니다.
  • 글쓰기 버튼과 목록보기 버튼이 표시되며, 사용자가 해당 버튼을 클릭하면 글쓰기 페이지로 이동하거나 목록 페이지로 돌아갈 수 있습니다.
  • <script> 태그 내부에서는 JavaScript 코드를 작성할 수 있으며, 현재는 ${map.search} 값에 따라 검색 폼의 값을 설정하는 부분이 추가되어 있습니다.

 

■ del.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Toy Project</title>

<%@ include file="/WEB-INF/views/inc/asset.jsp" %>

<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	<main id="main">
		<h1>게시판 <small>삭제하기</small></h1>
		
		<form method="POST" action="/toy/board/del.do">
		<div>
			<button type="button" class=back"
				onclick="history.back();">돌아가기</button>
			<button type="submit" class="del primary">삭제하기</button>
		</div>
		<input type="hidden" name="seq" value="${seq}">
		</form>
		
	</main>




<script>
</script>
</body>
</html>
  • 코드 분석
더보기

JSP(JavaServer Pages)를 사용하여 구현된 게시물 삭제 페이지입니다. 아래는 코드의 구성과 기능에 대한 설명입니다.

  • 첫 번째 줄에서는 페이지의 언어를 지정하고, 문자 인코딩을 UTF-8로 설정하고 있습니다.
  • JSTL(Core 태그 라이브러리)을 사용하기 위해 태그 라이브러리를 import하고 있습니다.
  • <head> 태그 내부에서는 페이지의 제목과 필요한 CSS 파일을 포함시키는 등의 작업을 수행합니다.
  • <body> 태그 내부에서는 게시물 삭제 페이지의 레이아웃을 구성합니다.
  • <%@ include file="/WEB-INF/views/inc/header.jsp" %>를 사용하여 게시물 삭제 페이지의 상단에 헤더 부분을 포함시키고 있습니다.
  • <main> 태그 내부에서는 게시물 삭제를 위한 폼을 표시합니다. 게시물 삭제 페이지의 제목과 함께 "삭제하기"라는 작은 문구가 표시됩니다.
  • <form> 태그를 사용하여 게시물 삭제를 위한 폼을 구성하고 있습니다. 폼은 POST 방식으로 데이터를 서버로 전송하며, /toy/board/del.do URL로 데이터를 전송합니다.
  • <button> 태그를 사용하여 "돌아가기"와 "삭제하기" 버튼을 표시하고 있습니다. "돌아가기" 버튼은 이전 페이지로 돌아가는 기능을 수행하며, "삭제하기" 버튼은 게시물을 삭제하는 기능을 수행합니다.
  • <input> 태그를 사용하여 삭제할 게시물의 식별자인 seq 값을 서버로 전송하고 있습니다. seq 값은 ${seq} 표현식을 통해 동적으로 설정됩니다.
  • <script> 태그 내부에서는 JavaScript 코드를 작성할 수 있으며, 현재는 비어있는 상태입니다.

 

■ edit.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Toy Project</title>

<%@ include file="/WEB-INF/views/inc/asset.jsp" %>

<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	<main id="main">
		<h1>게시판 <small>수정하기</small></h1>
		
		<form method="POST" action="/toy/board/edit.do">
		<table class="vertical">
			<tr>
				<th>제목</th>
				<td><input type="text" name="subject" id="subject" required class="full" value="${dto.subject}"></td>
			</tr>
			<tr>
				<th>내용</th>
				<td><textarea name="content" id="content" required class="full">${dto.content}</textarea></td>
			</tr>
		</table>
		<div>
			<button type="button" class=back"
				onclick="history.back();">돌아가기</button>
			<button type="submit" class="edit primary">수정하기</button>
		</div>
		
		<input type="hidden" name="seq" value="${dto.seq}">
		</form>
		
	</main>




<script>
</script>
</body>
</html>
  • 코드 분석
더보기

JSP(JavaServer Pages)를 사용하여 구현된 게시물 수정 페이지입니다. 아래는 코드의 구성과 기능에 대한 설명입니다.

  • 첫 번째 줄에서는 페이지의 언어를 지정하고, 문자 인코딩을 UTF-8로 설정하고 있습니다.
  • JSTL(Core 태그 라이브러리)을 사용하기 위해 태그 라이브러리를 import하고 있습니다.
  • <head> 태그 내부에서는 페이지의 제목과 필요한 CSS 파일을 포함시키는 등의 작업을 수행합니다.
  • <body> 태그 내부에서는 게시물 수정 페이지의 레이아웃을 구성합니다.
  • <%@ include file="/WEB-INF/views/inc/header.jsp" %>를 사용하여 게시물 수정 페이지의 상단에 헤더 부분을 포함시키고 있습니다.
  • <main> 태그 내부에서는 게시물 수정을 위한 폼을 표시합니다. 게시물 수정 페이지의 제목과 함께 "수정하기"라는 작은 문구가 표시됩니다.
  • <form> 태그를 사용하여 게시물 수정을 위한 폼을 구성하고 있습니다. 폼은 POST 방식으로 데이터를 서버로 전송하며, /toy/board/edit.do URL로 데이터를 전송합니다.
  • <table> 태그를 사용하여 제목과 내용을 입력할 수 있는 입력 필드를 구성하고 있습니다. 제목은 <input> 태그를 사용하고, 내용은 <textarea> 태그를 사용합니다. 값이 기존 게시물의 값으로 미리 설정되도록 ${dto.subject}와 ${dto.content} 표현식을 사용합니다.
  • <button> 태그를 사용하여 "돌아가기"와 "수정하기" 버튼을 표시하고 있습니다. "돌아가기" 버튼은 이전 페이지로 돌아가는 기능을 수행하며, "수정하기" 버튼은 게시물을 수정하는 기능을 수행합니다.
  • <input> 태그를 사용하여 수정할 게시물의 식별자인 seq 값을 서버로 전송하고 있습니다. seq 값은 ${dto.seq} 표현식을 통해 동적으로 설정됩니다.
  • <script> 태그 내부에서는 JavaScript 코드를 작성할 수 있으며, 현재는 비어있는 상태입니다.

 

■ view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Toy Project</title>

<%@ include file="/WEB-INF/views/inc/asset.jsp" %>

<style>

   #content {
      height:215px;
   }
   
   #comment td:nth-child(1) {width: auto;}
   #comment td:nth-child(2) {
            width: 170px;
            test-align: center;
   }
   
   #addcomment td:nth-child(1) {width: auto;}
   #addcomment td:nth-child(2) {
         width: 150px;
         text-align: center;
   }
   
   
   .comment-content {
      display: flex;
      display: table-cell;
      justify-content: space-between;
   }
   
   .comment-regdate {
      font-size: 12px;
      color: #777;
   }
   
   

</style>
</head>
<body>
   <!-- template.jsp > add.jsp > view.jsp -->
   
   <%@ include file="/WEB-INF/views/inc/header.jsp" %>
   <main id="main">
      <h1>게시판 <small>글보기</small></h1>
      
      
      <table class="vertical">
         <tr>
            <th>번호</th>
            <td>${dto.seq}</td>
         </tr>
         <tr>
            <th>이름</th>
            <td>${dto.name}(${dto.id})</td>
         </tr>
         <tr>
            <th>제목</th>
            <td>${dto.subject}</td>
         </tr>
         <tr>
            <th>내용</th>
            <td id="content">${dto.content}</td>
         </tr>
         <tr>
            <th>날짜</th>
            <td>${dto.regdate}</td>
         </tr>
         <tr>
            <th>조회수</th>
            <td>${dto.readcount}</td>
         </tr>
      </table>
      
      <table id="comment">
         <c:forEach items="${clist}" var="cdto">
         <tr>
            <td>
               <div class="comment-content">
                  <div><c:out value="${cdto.content}" /></div>
                  <div class="comment-regdate">${cdto.regdate}</div>
               </div>
            </td>
            <td>
               <div>
                  <div>${cdto.name}(${cdto.id})</div>
                  <c:if test="${not empty id && (id == cdto.id || lv == '3')}">
                  <div>
                     <button type="button" class="edit" onclick="editComment(${cdto.seq});">수정</button>
                     <button type="button" class="del" onclick="delComment(${cdto.seq});" >삭제</button>
                  </div>
                  </c:if>
               </div>
            </td>
         </tr>
         </c:forEach>
      </table>
      
      
      <!-- 로그인 안하면 댓글 못보도록  -->
      <c:if test="${not empty id}">
      <form method="POST" action="/toy/board/comment.do">
      <table id="addcomment">
         <tr>
            <td><input type="text" name="content" class="full" required></td>
            <td>
               <button type="submit" class="comment">댓글쓰기</button>
            </td>
         </tr>
      </table>
      <input type="hidden" name="bseq" value="${dto.seq}">
      <input type="hidden" name="column" value="${column}">
      <input type="hidden" name="word" value="${word}">
      <input type="hidden" name="search" value="${search}">
      </form>
      </c:if>
      
      <div>
         <!-- 페이지를 다시 불러오기함, db에서 다시 select 해옴 -->
            <button type="button" class="back"
               onclick="location.href='/toy/board/board.do?column=${column}&word=${word}';">돌아가기</button>
         
                              <!-- 세션 아이디 == 글쓴 사람 아이디 || -->
         <c:if test="${not empty id && (id == dto.id || lv == '3')}">
            <button type="button" class="edit"
               onclick="location.href='/toy/board/edit.do?seq=${dto.seq}';">수정하기</button>
            <button type="button" class="del"
               onclick="location.href='/toy/board/del.do?seq=${dto.seq}';">삭제하기</button>
         </c:if>
         
         <button type="button" class="reply"
               onclick="location.href='/toy/board/add.do?mode=reply&thread=${dto.thread}&depth=${dto.depth}';">답변하기</button>
         <!-- 브라우저가 읽었던 이전 상태를 복구 -->
<!--          <button type="button" class="back"
            onclick="history.back();">돌아가기</button> -->
      </div>
      
      
   </main>
   
   <form id="editCommentForm" method="POST" action="/toy/board/editcomment.do">
      <input type="hidden" name="bseq">
      <input type="hidden" name="cseq">
      <input type="hidden" name="content">
   </form>

<script>

   function editComment(cseq) {
      
      //이전 수정중인 댓글 폼 > 전부 삭제 
      $('.edit-comment').remove();
      
      const content = $(event.target).closest('tr').find('.comment-content').children().eq(0).text();
      
      $(event.target).closest('tr').after(
         `   
            <tr style="background-color: #EFEFEF;" class="edit-comment">
               <td><input type="text" class="full" value="\${content}" id="editcomment"></td>
               <td>
                  <input type="button" value="확인" onclick="editOkComment(\${cseq});">
                  <input type="button" value="취소" onclick="cancelComment();">
               </td>
            </tr>
         `
      );
      
      
   }
   
   function cancelComment() {
      $(event.target).closest('tr').remove();
   }
   
   function editOkComment(cseq) {
      
      //부모 글번호
      //댓글 번호
      //댓글 내용
      
      //console.log(${dto.seq}); //부모글번호
      //console.log(cseq);   //현재댓글번호
      //console.log($('#editcomment').val()); //수정내용
      
      $('#editCommentForm input[name=bseq]').val(${dto.seq});
      $('#editCommentForm input[name=cseq]').val(cseq);//수정할 댓글번호
      $('#editCommentForm input[name=content]').val($('#editcomment').val());//수정할 텍스트내용
      
      $('#editCommentForm').submit();
      
      
   }
   
   function delComment(cseq) {
	   
	   location.href = '/toy/board/delcomment.do?seq=${dto.seq}&cseq=' + cseq;
	   
   }
   

</script>
</body>
</html>
  • 코드 분석
더보기

JSP(JavaServer Pages)를 사용하여 게시물 상세보기 페이지를 구현한 것입니다. 아래는 코드의 구성과 주요 기능에 대한 설명입니다.

  • 첫 번째 줄에서는 페이지의 언어를 지정하고, 문자 인코딩을 UTF-8로 설정하고 있습니다.
  • JSTL(Core 태그 라이브러리)을 사용하기 위해 태그 라이브러리를 import하고 있습니다.
  • <head> 태그 내부에서는 페이지의 제목과 필요한 CSS 파일을 포함시키는 등의 작업을 수행합니다.
  • <body> 태그 내부에서는 게시물 상세보기 페이지의 레이아웃을 구성합니다.
  • <%@ include file="/WEB-INF/views/inc/header.jsp" %>를 사용하여 게시물 상세보기 페이지의 상단에 헤더 부분을 포함시키고 있습니다.
  • <main> 태그 내부에서는 게시물의 제목, 내용, 작성자 정보 등을 표시합니다.
  • <table> 태그를 사용하여 게시물의 정보를 테이블 형태로 표시하고 있습니다. ${dto} 표현식을 사용하여 게시물의 속성값을 동적으로 설정합니다.
  • 댓글 목록을 표시하기 위해 <table> 태그 내부에 <c:forEach> 태그를 사용하여 ${clist}(댓글 목록)의 각 댓글을 반복하여 표시합니다. ${cdto} 표현식을 사용하여 각 댓글의 속성값을 동적으로 설정합니다.
  • 댓글을 수정하거나 삭제하기 위한 버튼을 표시하고 있습니다. <button> 태그를 사용하며, JavaScript 함수를 호출하여 댓글 수정 및 삭제 동작을 처리합니다.
  • 댓글 작성을 위한 폼을 표시하고 있습니다. <form> 태그를 사용하며, 댓글 내용을 입력하는 <input> 태그와 "댓글쓰기" 버튼을 포함하고 있습니다.
  • 게시물 수정, 삭제, 답변하기 버튼을 표시하고 있습니다. 사용자의 권한과 현재 로그인한 사용자의 정보에 따라 버튼의 표시 여부를 동적으로 결정합니다.
  • <script> 태그 내부에서는 JavaScript 함수를 정의하고 있습니다. 이 함수들은 댓글 수정, 삭제 등의 동작을 처리하는 역할을 수행합니다.

 

★ 실행 결과

  • 글 작성

  • 글 리스트 Board

  • 글 상세보기

  • 댓글 작성

  • 수정하기

  • 삭제하기

728x90