Controller (Servlet) 클래스

- WebServlet 클래스 등은 Apache Tomcat 의 servlet-api.jar 파일에 있다

 

흐름 정리

- 클라이언트가 요청하면 무조건 Controller 클래스로 가고, 적절한 Service 클래스로 넘겨준다 거기서 DAO 의 메소드를 호출하여 DB와 연동

- DAO 에서 검색한 결과를 Service 클래스에서 공유설정하고 Controller 클래스에서 포워딩해서 View 에서 공유된 값을 가져와서 출력


문제점 1

- 등록 버튼을 눌러서 글을 등록한 뒤 목록페이지에서 F5(새로고침) 를 누르면 똑같은 데이터가 추가된다

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 새로고침을 누를때마다 하나씩 추가됨

 

문제점 1 해결

- BoardAddAction.java (원문 글 작성 Service 클래스) 에서 setRedirect(false) 를 true 로 설정해서 Redirect 방식으로 포워딩하면 이 문제가 해결된다

 

문제점 2

- 글 작성 성공 후 글 목록 가져오기를 "/BoardListAction.do" 로 요청할때 get이 나와야하는데 post 방식이 나온다

- 포워딩을 하면 get 방식으로 전달되지만, 콘솔창에는 post 방식으로 뜬다

+ 폼으로 /BoardListAction.do 로 오는게 아니므로 post 가 아닌 get 방식이다

 

문제점 2 이유

- 위는 글 작성 요청 아래는 글 작성이 끝난 후 글 목록을 가져오는 요청

- dispatcher 방식으로 포워딩되면 URL 주소가 바뀌지 않으므로 이전에 글 작성할때 사용했던 post 방식으로 나옴

 

문제점 1 & 문제점 2 해결 방법

- setRedirect 를 true 로 바꿈

- setPath 에서 혹시 경로를 못찾아가면 . 를 앞에 붙여줌

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 새로고침해도 같은 데이터가 들어가지 않는다

 

문제점 정리

- Get 방식처럼 단순히 조회하는 요청은 Dispatcher 방식을 사용

- Post, Put 방식처럼 무언가를 변화시키는 요청에는 Redirect 방식을 사용

- Dispatcher 방식이 Redirect 방식보다 성능상 좋다. 

- Post, Put 같은 무언가를 변화시키는 요청에는 Redirect 방식을 사용

- 클라이언트가 Post 요청으로 무언가를 변화시킨 후 새로고침을 눌러버리면 웹 브라우저에서 새로고침은 마지막에 서버에 전송한 데이터를 다시 전송하기 때문에 다시 URL에 접속되면서 Post 요청이 한번 더 전송되기 때문

ex) 상품 등록 요청을 하였는데 재요청을 보내버리면 2개의 상품이 중복 등록가 돼버리는 상황이 발생하게 된다.

 

다른 페이지로 이동

- 아직 페이지를 선택할 수 있는 부분을 만들지 않았으므로 URL 에 입력해서 찾아가보자

- 이 페이지에 맞는 10 개의 글(데이터) 을 뿌려줌

 

파생변수 number

	<c:set var="num" value="${listcount - (page - 1) * 10}"/>

- 각 페이지에 출력될 시작 번호를 저장하고 있다

- 마지막의 10 은 한 페이지에 출력할 데이터 개수 limit 를 의미한다

- 그 번호에서 반복문으로 1씩 감소하여서 출력

 

더미 데이터 만들기

- 페이지 처리 구현시 보기 위해 더미 데이터 여러개를 만든다

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료


게시판 프로그램 : 목록 가져오기 (이어서)

- 목록가져오는 것은 구현했고, 공유된 리스트, 기본변수, 파생변수들을 View 에서 가져와서 출력하는 과정에 있다

- 목록은 뿌렸고 페이지 처리, 페이지 선택할 수 있는 것 만들고 있다

- qna_board_list.jsp (최종)

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

<a href="./BoardForm.do">글작성</a> <br>
글갯수 : ${listcount } 개 <br>

<%
	int count = ((Integer)request.getAttribute("listcount")).intValue();
%>
글갯수 : <%=count %> 개 <br>

<table border=1 width=700 align=center>
	<caption>게시판 목록</caption>
	<tr>
		<td>번호</td>
		<td>제목</td>
		<td>작성자</td>
		<td>날짜</td>
		<td>조회수</td>
	</tr>
	
	<c:set var="num" value="${listcount - (page - 1) * 10}"/>
	<c:forEach var="b" items="${boardlist}">
	<tr>
		<td> ${num}
			<c:set var="num" value="${num-1}"/>
		</td>
		<td>
			<!-- 댓글 제목 앞에 여백 처리 -->
			<c:if test="${b.board_re_lev > 0}">
				<c:forEach var="i" begin="0" end="${b.board_re_lev}">
					&nbsp;
				</c:forEach>
			</c:if>
			<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">${b.board_subject}</a>
		</td>
		<td>
			${b.board_name}
		</td>
		<td>
			<fmt:formatDate value="${b.board_date}"
							pattern="yyyy-MM-dd HH:mm:ss EEEE"/>
		</td>
		<td>${b.board_readcount}</td>
	</tr>
	</c:forEach>
</table> <br><br>

<!-- 페이지 처리 -->
<center>
<c:if test="${listcount > 0}">

<!-- 1page로 이동 -->
<a href="./BoardListAction.do?page=1" style="text-decoration:none"> << </a>

<!-- 이전 블럭으로 이동 -->
<c:if test="${startPage > 10}">
	<a href="./BoardListAction.do?page=${startPage-10}">[이전]</a>
</c:if>

<!-- 각 블럭에 10개의 페이지 출력 -->
<c:forEach var="i" begin="${startPage}" end="${endPage}">
	<c:if test="${i == page}"> <!-- 현재 페이지 -->
		[${i}]
	</c:if>
	<c:if test="${i != page}"> <!-- 현재 페이지가 아닐때 -->
		<a href="./BoardListAction.do?page=${i}">[${i}]</a>
	</c:if>
</c:forEach>

<!-- 다음 블럭으로 이동 -->
<c:if test="${endPage < pageCount}">
	<a href="./BoardListAction.do?page=${startPage+10}">[다음]</a>
</c:if>

<!-- 마지막 페이지로 이동 -->
<a href="./BoardListAction.do?page=${pageCount}" style="text-decoration:none"> >> </a>

</c:if>
</center>

 

페이지 처리 / 페이지 메뉴 바 설계

각 블럭에 10개의 페이지 출력 (qna_board_list.jsp 부분)

<!-- 각 블럭에 10개의 페이지 출력 -->
<c:forEach var="i" begin="${startPage}" end="${endPage}">
	<c:if test="${i == page}"> <!-- 현재 페이지 -->
		[${i}]
	</c:if>
	<c:if test="${i != page}"> <!-- 현재 페이지가 아닐때 -->
		<a href="./BoardListAction.do?page=${i}">[${i}]</a>
	</c:if>
</c:forEach>

 

- forEach 문으로 startPage 와 endPage 까지 반복문을 돌려서 1 2 3 ... 10 로 페이지 선택하는 걸 만듬

- 현재 페이지가 아닌 경우 클릭한 페이지 번호를 넘기면서 "/BoardListAction.do" 로 요청한다

- 이러면 Controller 클래스를 다시 찾아가서 BoardListAction.java Service 클래스로 가서 다시 목록을 가져온다

- 이때 BoardListAction.java 의 아래 코드에서

		if(request.getParameter("page") != null) {
			page = Integer.parseInt(request.getParameter("page"));
		}

- 사용자가 선택한 페이지가 page 변수에 저장되고 그걸 이용해서 데이터를 가져옴

- 그리고 page 변수로부터 유도된 변수 및 리스트를 BoardListAction.java 아래에서 공유설정함

		// 공유 설정
		request.setAttribute("page", page);

- 그걸 다시 View qna_board_list.jsp 에서 받는다

 

첫 페이지로 이동, 마지막 페이지로 이동 (qna_board_list.jsp 부분)

 

<!-- 1page로 이동 -->
<a href="./BoardListAction.do?page=1" style="text-decoration:none"> << </a>

- 1 page 로 이동

<!-- 마지막 페이지로 이동 -->
<a href="./BoardListAction.do?page=${pageCount}" style="text-decoration:none"> >> </a>

- 총 페이지 수 == 마지막 페이지

- pageCount 는 총 페이지 수이고, 공유되어 받아온 값이다

- >> 클릭시 마지막 페이지로 이동

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

 

이전 블럭 (qna_board_list.jsp)

<!-- 이전 블럭으로 이동 -->
<c:if test="${startPage > 10}">
	<a href="./BoardListAction.do?page=${startPage-10}">[이전]</a>
</c:if>

- 블럭단위로 이동

- startPage 는 본인이 속한 블럭의 첫 페이지 의미

- startPage > 10 은 '1 페이지가 아니면' 이라는 의미, ${startPage - 10} 은 현재 블럭의 첫 페이지에서 -10 을 하면 이전 블럭의 첫 페이지로 이동

ex) 13 페이지에 있다가 [이전] 클릭시 현재 블럭 startPage 인 11 에서 -10 을 하여 1 페이지로 이동, 즉 1 블럭의 startPage 로 이동

 

다음 블럭 이동 (qna_board_list.jsp)

<!-- 다음 블럭으로 이동 -->
<c:if test="${endPage < pageCount}">
	<a href="./BoardListAction.do?page=${startPage+10}">[다음]</a>
</c:if>

- endPage 는 본인이 속한 블럭의 끝 페이지를 의미

- endPage < pageCount 은 '마지막 페이지가 아니면' 이라는 의미, ${startPage + 10} 은 현재 블럭의 첫 페이지에서 +10 을 하면 다음 블럭의 첫 페이지로 이동

 

 

블럭과 파생변수 startPage, endPage

- 두번째 블럭의 startPage 는 11, endPage 는 20

 

- 페이지를 클릭할때마다

- 페이지 번호를 누를때마다 계속 DB와 연동해서 목록을 가져오는 작업, request 로 공유하는 작업, View 에서 받아서 뿌리는 작업이 수행된다

 


+ 같은 페이지 처리를 JSTL 쓰지 않고 했을때

- Model 1 게시판 페이지 메뉴바 만드는 코드이다 : https://laker99.tistory.com/117

<!-- 페이지 링크 설정 -->
<center>
<%
if(count > 0) {
	// pageCount : 총 페이지 수
	int pageCount = count/page_size + ((count%page_size==0) ? 0 : 1);
	System.out.println("pageCount : " + pageCount);
	
	// startPage : 각 블럭의 시작 페이지 번호 : 1, 11, 21 ...
	// endPage : 각 블럭의 끝 페이지 번호 :    10, 20, 30 ...
	int startPage = ((currentPage-1)/10) * 10 + 1;
	int block = 10;	// 1개의 블럭의 크기 : 10개의 페이지로 구성
	int endPage = startPage + block - 1;
	
	// 가장 마지막 블럭에 endPage값을 pageCount로 수정
	if(endPage > pageCount) {	//
		endPage = pageCount;
	}
%>	
	<!-- 1page로 이동 -->
	<a href="list.jsp?page=1" style="text-decoration:none"> << </a>
<%
	// 이전 블럭으로 이동
	if(startPage > 10){	%>
		<a href="list.jsp?page=<%=startPage-10 %>">[이전]</a>
<%	}

	// 각 블럭당 10개의 페이지 출력
	for(int i = startPage; i <= endPage; i++) {
		if(i == currentPage) { %>
			[<%=i %>]
<%		} else {%>
			<a href="list.jsp?page=<%=i %>">[<%=i %>]</a>
<%		}
	}// for end
	
	// 다음 블럭으로 이동
	if(endPage < pageCount) {
%>	
		<a href="list.jsp?page=<%=startPage+10 %>">[다음]</a>	
<%	} %>
	<!-- 마지막 페이지로 이동 -->
	<a href="list.jsp?page=<%=pageCount %>" style="text-decoration:none"> >> </a>	
<%}%>
</center>


게시판 프로그램 : 상세 페이지

- 제목 클릭시 상세 페이지로 넘어간다

- 글번호 num 과 페이지 번호를 전달해야한다 ( 총 3번 전달 중 첫번째)

- 목록페이지 -> 상세페이지 -> 수정/삭제 폼 -> 수정/삭제

- 처음 2 번은 get 방식으로 전달, 마지막은 post 로 전달

- qna_board_list.jsp 부분

<td>
	<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">${b.board_name}</a>
</td>

- 상세 페이지로 가기 위해 /BoardDetailAction.do 로 요청한다

 

 게시판 프로그램 : 상세 페이지 Service 클래스

- service 패키지 안에 상세 페이지 Service 클래스인 BoardDetailAction.java 를 생성 및 작성하자

- BoardDetailAction.java (수정 전)

package service;

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

import dao.BoardDAO;
import model.BoardBean;

public class BoardDetailAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardDetailAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		dao.readcountUpdate(board_num);	// 조회수 1 증가
		BoardBean board = dao.getDetail(board_num);	// 상세정보 구하기
		
		// 글내용 줄바꿈 기능
		String content = board.getBoard_content().replace("\n", "<br>");
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("content", content);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_view.jsp");
		
		return forward;
	}

}

- 목록 페이지에서 제목을 클릭해서 넘어왔고, 글번호와 페이지번호가 넘어왔다, 그 값을 변수 board_num 과 page 로 받음

- 글번호는 int 형으로 형변환한다

<조회수 1 증가>

- 조회수 값을 1 증가시키는 update SQL 을 수행하는 메소드 readcountUpdate() 가 필요하다

- 그 글의 조회수 컬럼을 1 증가시켜야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 따로 리턴을 받을 필요는 없다, 아무것도 리턴 받지 않고 있음

* DAO 클래스 readcountUpdate() 메소드 작성은 아래에

<상세 정보 구하기>

- 상세 정보를 구해오는 메소드 select SQL 을 수행하는 메소드 getDetail() 이 필요하다

- 그 글의 상세 정보를 구해와야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 글 1개의 상세정보를 리턴하므로 리턴 자료형은 DTO

* DAO 클래스 getDetail() 메소드 작성은 아래에

<DAO -> Service 로 돌아왔을때> (BoardDetailAction.java 부분)

		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("content", content);
		request.setAttribute("page", page);

- View 페이지로 돌아간다, 돌아갈때 2가지 변수 글번호, 페이지번호 뿐 아니라 검색된 결과를 돌려받은 객체 board 도 가져가야한다

- 이때, 객체 board, 줄바꿈 처리를 한 content , 페이지번호 page 3개를 공유설정한다

- 글번호 num 은 board 객체 안의 컬럼 board_num 에 있음

- content 는 board 에서 가져와서 줄바꿈 처리를 한 것이다

- 상세페이지 출력 View 페이지인 qna_board_view.jsp 파일로 포워딩한다.

- 이 Service 클래스를 호출했던 Controller 클래스로 돌아감


Model 1 vs Model 2

- Model 1 에서는 두 기능을 하나의 메소드에 처리했다, Model 2 에서는 따로 메소드를 만듬

+ MyBatis 로 바꿔서 처리하면 1개의 메소드 내에서 2개의 sql 문 사용하기 힘들어지므로 지금부터 다른 메소드로 처리

 

Controller 클래스에서 상세 페이지 Service 클래스 BoardDetailAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardDetailAction.do 로 요청이 올때의 경우를 처리

		// 상세 페이지
		} else if(command.equals("/BoardDetailAction.do")) {
			try {
				action = new BoardDetailAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}
		}

- 연결 이후 Service 클래스까지 잘 넘어간다

 

DAO 클래스 조회수 1 증가 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 readcountUpdate() 부분 코드만

	// 조회수 1 증가
	public void readcountUpdate(int board_num) {
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
			String sql = "update model2 set board_readcount = board_readcount + 1 ";
			sql += "where board_num = ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, board_num);
			pstmt.executeUpdate();	// SQL문 실행
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}		
		}
	}

- 데이터를 돌려줄 필요없으므로 리턴 자료형 void

- 그 글의 조회수 컬럼을 1 증가시켜야하므로 그 글을 특정할 수 있는 글번호 board_num 이 전달되고, 그걸로 SQL문 where 절 작성

 

DAO 클래스 상세정보 구하기 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 getDetail() 부분 코드만

	// 상세 페이지
	public BoardBean getDetail(int board_num) {
		BoardBean board = new BoardBean();
		
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			con = getConnection();
			
			String sql = "select * from model2 where board_num = ?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, board_num);
			rs = pstmt.executeQuery();	// SQL문 실행
			
			if(rs.next()) {
				board.setBoard_num(rs.getInt("board_num"));
				board.setBoard_name(rs.getString("board_name"));
				board.setBoard_pass(rs.getString("board_pass"));
				board.setBoard_subject(rs.getString("board_subject"));
				board.setBoard_content(rs.getString("board_content"));
				board.setBoard_file(rs.getString("board_file"));
				board.setBoard_re_ref(rs.getInt("board_re_ref"));
				board.setBoard_re_lev(rs.getInt("board_re_lev"));
				board.setBoard_re_seq(rs.getInt("board_re_seq"));
				board.setBoard_readcount(rs.getInt("board_readcount"));
				board.setBoard_date(rs.getTimestamp("board_date"));
			}
		} catch (Exception e) {
			e.printStackTrace();			
		} finally {
			if(rs != null) try { rs.close(); } catch(Exception e) {}
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}
		}
		return board;
	}

- 그 글의 상세 정보를 구해와야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 글 1개의 상세정보를 리턴하므로 리턴 자료형은 DTO

- select 문을 작성하므로 ResultSet rs 객체를 생성하고 그 객체로 검색된 데이터들을 받는다
- DTO 객체 board 를 생성하고, 그 board 에 검색된 데이터를 setter 메소드로 세팅한 후 리턴한다

 

- 다음은 board 폴더 하위에 상세 페이지를 출력하는 View 페이지인 qna_board_view.jsp 파일을 생성

- qna_board_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">
</head>
<body>
<table border=1 width=400 align=center>
	<caption>상세 페이지</caption>
	<tr>
		<td>제목</td>
		<td>${board.board_subject}</td>
	</tr>
	<tr>
		<td>내용</td>
		<td>
			<pre>${board.board_content}</pre>
			${content}
		</td>
	</tr>
	<tr>
		<td>첨부파일</td>
		<td>
			<!-- 첨부파일이 있을때 첨부파일 출력 -->
			<c:if test="${board.board_file != null}">
				<a href="./board/file_down.jsp?file_name=${board.board_file}">
					${board.board_file}
				</a>
			</c:if>
		</td>
	</tr>
	<tr>
		<td colspan=2 align=center>
			<input type="button" value="댓글">
			<input type="button" value="수정">
			<input type="button" value="삭제">
			<input type="button" value="목록"
			onClick="location.href='./BoardListAction.do?page=${page}'">
		</td>
	</tr>
</table>
</body>
</html>

<EL>

- Service 클래스인 BoardDetailAction.java 에서 board 객체를 공유설정했으므로 ${board.board_subject} 처럼 네임값.필드명 으로 간단히 출력 가능

<내용 출력>

- 내용 content 을 출력하는 방법 2가지를 모두 썻다

1. 공유된 content 를 바로 EL 로 출력

2. 공유된 board 객체에서 board.board_content 로 내용을 가져와서 <pre></pre> 로 감싸기

<첨부파일> (qna_board_view.jsp 부분)

			<!-- 첨부파일이 있을때 첨부파일 출력 -->
			<c:if test="${board.board_file != null}">
				<a href="./board/file_down.jsp?file_name=${board.board_file}">
					${board.board_file}
				</a>
			</c:if>

- if 태그로 첨부파일이 null 이 아니면, 즉 첨부파일이 있으면 첨부파일명을 출력

- 첨부파일명에 링크를 걸어 클릭시 첨부파일을 다운받도록 한다 첨부파일명을 클릭시 file_down.jsp 로 이동

- file_down.jsp 로 이동하며 다운로드 받을 파일명 ${board.board_file} 을 file_name 변수에 담아 전달한다

<목록 버튼>

- '목록' 버튼 클릭시 "/BoardListAction.do" 요청을 하면서 현제 페이지 번호를 목록 구하는 곳으로 전달한다

- page 변수에 값이 저장되었으므로 원래 페이지로 돌아갈 수 있다

<댓글 버튼>

- '댓글' 버튼 클릭시 "/BoardReplyAction.do" 요청을 하면서 글번호 board_num 과 원래 페이지번호 page 를 전달

- 특정 글 아래에 댓글을 달게되므로 글번호 board_num 이 필요하다

- 댓글 작성 완료 후 원래의 페이지로 돌아가기 위해서 page 번호가 필요하다

- 나머지 버튼들은 나중에 링크 연결하기

 

Q. 왜 파일 다운하기 위해 file_down.jsp 로 갈떄는 원문 글 작성폼으로 갈때와 달리 Controller 안거치고 jsp 로 가는지 모르겠다

A. 원칙은 Controller 클래스 거쳐서 가야하지만 이 file_down.jsp 파일은 예외적으로 바로 JSP 파일로 가기


게시판 프로그램 : 댓글 작성

- 상세페이지에서 '댓글' 버튼 클릭시 "/BoardReplyAction.do" 요청을 get 방식으로 한다

- Controller 클래스에서 이 요청을 처리하고 댓글 작성폼 Service 클래스인 BoardReplyAction.java 로 가야함

 

게시판 프로그램 : 댓글 작성폼 Service 클래스

- 댓글 작성폼으로 가기 전, 부모글의 정보가 필요하고, 부모글의 정보를 가져올떄 DB 연동을 해야하므로 필요한 Service 클래스이다

- 댓글 작성폼으로 이동 전 필요하므로 이름을 "댓글 작성폼 Service 클래스" 로 한다

- service 패키지 안에 BoardReplyAction.java 클래스 생성 및 작성

- BoardReplyAction.java

package service;

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

import dao.BoardDAO;
import model.BoardBean;

public class BoardReplyAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardReplyAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		
		// 부모글의 상세 정보 구하기
		BoardBean board = dao.getDetail(board_num);
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_reply.jsp");
		return forward;
	}

}

- 상세 페이지 View 인 qna_board_view.jsp 에서 넘어온 글번호 board_name 와 페이지번호를 받는다

- 댓글 처리 관련 변수 3개를 처리하기 위해서는 부모글의 상세 정보 (변수 3개)를 먼저 구해야하므로 getDetail() 사용

- getDetail() 의 매개변수로 부모 글의 번호가 될 board_name 를 전달한다

ex) 부모의 board_re_lev 에서 + 1 을 하는 등 부모의 정보 필요

- DB에서 받아온 부모 글의 정보를 저장한 객체 board 와 페이지번호 page 를 공유설정한다.

- 댓글 작성폼 View 페이지 qna_board_reply.jsp 로 포워딩한다, request 로 공유설정했으므로 dispatcher 방식으로 포워딩

 

Controller 클래스에서 댓글 작성폼 Service 클래스 BoardReplyAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardReplyAction.do 로 요청이 올때의 경우를 처리

		// 댓글 폼
		} else if(command.equals("/BoardReplyAction.do")) {
			try {
				action = new BoardReplyAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

 

- 댓글 작성폼 qna_board_reply.jsp 를 생성 및 작성하자

- qna_board_reply.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>댓글 게시판</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardReply.do" method="post">
<input type="hidden" name="board_num" value="${board.board_num}">
<input type="hidden" name="page" value="${page}">
<input type="hidden" name="board_re_ref" value="${board.board_re_ref}">
<input type="hidden" name="board_re_lev" value="${board.board_re_lev}">
<input type="hidden" name="board_re_seq" value="${board.board_re_seq}">
<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">댓글 게시판</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value="re."/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15"></textarea>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="댓글">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_write.jsp 의 내용 복붙 후 수정

- 첨부파일 부분을 삭제, enctype 도 삭제하고, action 값을 "/BoardReply.do" 로 수정

- 부모글의 댓글 관련 컬럼 3개와, 글번호, 페이지번호 총 5개를 hidden 으로 값을 전달해야한다

- 그리고 댓글 작성 폼에 입력된 값들도 전달

- 글번호는 부모글의 글 번호를 의미, 그래서 공유된 부모글 정보 객체인 board 에서 부모글의 글번호를 가져옴

 

게시판 프로그램 : 댓글 작성 Service 클래스

- 실제로 댓글을 작성하는 역할을 한다

- service 패키지 안에 BoardReply.java 클래스 생성 및 작성

- BoardReply.java 

package service;

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

import dao.BoardDAO;
import model.BoardBean;

public class BoardReply implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardReply");
		
		request.setCharacterEncoding("utf-8");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		int board_re_ref = Integer.parseInt(request.getParameter("board_re_ref"));
		int board_re_lev = Integer.parseInt(request.getParameter("board_re_lev"));
		int board_re_seq = Integer.parseInt(request.getParameter("board_re_seq"));
		String page = request.getParameter("page");
		
		BoardBean board = new BoardBean();
		board.setBoard_num(board_num);
		board.setBoard_re_ref(board_re_ref);
		board.setBoard_re_lev(board_re_lev);
		board.setBoard_re_seq(board_re_seq);
		board.setBoard_name(request.getParameter("board_name"));
		board.setBoard_pass(request.getParameter("board_pass"));
		board.setBoard_subject(request.getParameter("board_subject"));
		board.setBoard_content(request.getParameter("board_content"));
		
		BoardDAO dao = BoardDAO.getInstance();
		int result = dao.boardReply(board); // 댓글 작성
		if(result == 1) System.out.println("댓글 작성 성공");
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(true);
		forward.setPath("./BoardListAction.do?page="+page);
		return forward;
	}

}

- 댓글 작성 폼에서 한글값이 넘어올 수 있으므로 한글값 인코딩

- hidden 으로 넘어온 5개 값과 댓글 작성 입력 양식에서 넘어온 값들도 request.getParameter() 로 받는다

- 생성한 DTO 객체 board에 받아온 값들( 부모 글 정보 + 댓글 작성 입력양식에 사용자가 작성한 값들) 을 setter 메소드로 세팅한다

- 부모 글에 관한 정보를 저장한 변수들은 SQL문 안에서 사용될것이므로 int 형으로 변환한다

- 댓글 작성 메소드인 boardReply() 메소드를 호출하면서 부모 글 정보와 입력한 데이터가 담긴 board 객체를 매개변수로 전달해줌

- 원래 페이지로 돌아가기 위해 목록을 가져오는 요청인 "/BoardListAction.do" 로 포워딩 하면서 get 방식으로 현재 페이지 번호를 넘겨준다, 이로 인해 원래 페이지로 돌아갈 수 있음

* DAO 클래스의 boardReply() 작성은 아래에

 

Controller 클래스에서 댓글 작성 Service 클래스 BoardReply로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardReply.do 로 요청이 올때의 경우를 처리

		// 댓글 작성
		} else if(command.equals("/BoardReply.do")) {
			try {
				action = new BoardReply();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

 

DAO 클래스 댓글 작성 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 boardReply() 부분 코드만

	// 댓글 작성
	public int boardReply(BoardBean board) {
		int result = 0;
		Connection con = null;
		PreparedStatement pstmt = null;
		
		// 부모글에 대한 정보
		int re_ref = board.getBoard_re_ref(); // 글 그룹 번호
		int re_lev = board.getBoard_re_lev(); // 댓글의 깊이
		int re_seq = board.getBoard_re_seq(); // 댓글의 출력순서
		
		try {
			con = getConnection();
			
			String sql = "update model2 set board_re_seq = board_re_seq + 1";
			sql += " where board_re_ref = ? and board_re_seq > ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, re_ref);
			pstmt.setInt(2, re_seq);
			pstmt.executeUpdate(); // SQL문 실행
			
			sql = "insert into model2 values(model2_seq.nextval,";
			sql += "?,?,?,?,?,?,?,?,?,sysdate)";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, board.getBoard_name());
			pstmt.setString(2, board.getBoard_pass());
			pstmt.setString(3, board.getBoard_subject());
			pstmt.setString(4, board.getBoard_content());
			pstmt.setString(5, ""); // board_file
			pstmt.setInt(6, re_ref); // board_re_ref
			pstmt.setInt(7, re_lev+1); // board_re_lev
			pstmt.setInt(8, re_seq+1); // board_re_seq
			pstmt.setInt(9, 0); // board_readcount
			result = pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();		
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}			
		}
		return result;
	}

- 매개변수로는 부모글의 정보와 댓글 작성 폼에 적힌 데이터가 저장된 DTO 객체 board 가 넘어온다

- 부모글에 대한 3가지 정보를 변수에 저장

- 2가지 SQL 문을 사용해야 한다

<update 문>

- 댓글의 board_re_seq 는 자신의 부모글 보다 seq 값이 큰 글들만 seq 값을 1 씩 증가시킴

1) 부모가 원문이면 그 원문의 모든 댓글의 seq 값을 1 씩 증가

2) 부모가 댓글이면 그 부모보다 먼저 달린 형제 댓글들과 그 형제 댓글들의 대댓글들의 seq 값을 1씩 증가

<insert 문>

- 댓글 번호는 마찬가지로 sequence 로 넣는다

- 댓글의 board_re_ref 는 부모글의 board_re_ref 와 일치해야함

- 댓글의 board_re_lev 는 부모글의 board_re_lev 보다 1 커야함

- board_re_lev 와 board_re_seq 는 부모 글의 board_re_lev 와 board_re_seq 에서 1 증가한 값을 넣음

 

- 이제 상세 페이지에서 '댓글' 을 클릭해서 댓글 폼으로 간 후 댓글을 작성하자

 

문제점

- 안나온다

- 또 get 으로 전송하는데도 post 가 나옴

 

문제 해결

- BoardReply.java 의 setRedirect() 를 true 로 바꾸기

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 댓글 작성 후 목록페이지가 잘 나타난다

 

- 이제 댓글과 대댓글을 더 많이 써보자


게시판 프로그램 : 글 수정

- 수정 폼으로 넘어가기 전 DAO 에서 글의 상세정보를 불러와야한다

- 그러므로 "/BoardModifyAction.do" 로 요청함

 

- 상세 페이지 하단에서 '수정' 눌렀을때 수정폼 Service 클래스로 넘어가기 위해 "/BoardModifyAction" 로 요청

- qna_board_view.jsp 부분

<input type="button" value="수정"
onClick="location.href='./BoardModifyAction.do?board_num=${board.board_num}&page=${page}'">

 

- 상세페이지에서 글번호 board_num 과 페이지번호 page 가 넘어온다

 

 게시판 프로그램 : 글 수정폼 Service 클래스

- service 패키지 안에 글 수정폼 Service 클래스인 BoardModifyAction.java 를 생성 및 작성하자

- BoardModifyAction.java

package service;

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

import dao.BoardDAO;
import model.BoardBean;

public class BoardModifyAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardModifyAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		
		// 상세 정보 구하기
		BoardBean board = dao.getDetail(board_num);
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_modify.jsp");
		return forward;
	}

}

- 수정폼으로 가기 전에 상세정보를 먼저 구해야 한다, 그 후에 수정폼으로 포워딩해야함

- 가장 먼저 상세페이지에서 get 방식으로 넘어온 글번호 board_num 과 페이지번호 page 를 가져온다

- DAO 의 getDetail() 메소드로 board_num 을 사용해서 글의 상세정보를 가져온다

- 가져온 상세정보를 받은 객체 board 를 공유설정하고, 페이지 번호 page 도 공유설정한다

+ 글 번호는 객체 board 안에도 있으므로 따로 공유설정 하지 않음

 

Controller 클래스에서 글 수정폼 Service 클래스 BoardModifyAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardModifyAction.do 로 요청이 올때의 경우를 처리

		// 수정 폼
		} else if(command.equals("/BoardModifyAction.do")) {
			try {
				action = new BoardModifyAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}
		}

 

- 다음은 글 수정폼 View 페이지인 qna_board_modify.jsp 파일을 생성하자

- qna_board_modify.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>게시판 수정</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardModify.do" method="post">
<input type="hidden" name="board_num" value="${board.board_num}">
<input type="hidden" name="page" value="${page}">

<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">게시판 수정</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value="${board.board_name}"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value="${board.board_subject}"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15">${board.board_content}</textarea>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="수정">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_reply.jsp 파일의 내용을 복붙 후 수정

- 수정폼에 수정할 정보를 입력하고 '수정' 버튼 클릭시 글 수정 요청인 "/BoardModify.do" 로 요청이 간다 

- hidden 으로는 글 번호인 board_num 과 페이지번호 page 가 넘어간다

- 글 수정 요청인 "/BoardModify.do" 로 가면서 hidden 으로 2개, 입력양식으로 4개의 값들, 총 6개의 값이 글 수정 Service 클래스 BoardModify.java 로 전달됨

- Service 클래스 BoardModifyAction.java 에서 공유했던 board 객체에서 이름을 "${board.board_name}" 로 가져와서 입력양식에 value 속성으로 넣기

- 이름 뿐 아니라 제목, 내용도 마찬가지로 처리한다

 

- 구현 후 상세페이지에서 '수정' 클릭시 나타나는 수정폼에 정보들이 나타남

 

- 비밀번호를 입력하고 '수정' 버튼 클릭시 /BoardModify.do 로 요청함

 

- 실제 수정을 처리해주는 Service 클래스인 BoardModifyjava 클래스를 생성하자

 

 게시판 프로그램 : 글 수정 Service 클래스

- service 패키지 안에 글 수정 Service 클래스인 BoardModifyjava.java 를 생성 및 작성하자

- BoardModify.java (수정 전 1)

package service;

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

public class BoardModify implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardModify");
		
		ActionForward forward = new ActionForward();
		return forward;
	}
}

- 아직 작성 안했고 여기까지 도착했는지 출력만 하고 있다, 내일 작성

 

Controller 클래스에서 글 수정 Service 클래스 BoardModify로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardModify.do 로 요청이 올때의 경우를 처리

		// 글 수정
		} else if(command.equals("/BoardModify.do")) {
			try {
				action = new BoardModify();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}	
		}

+ Recent posts