2020/07/14 - [JSP] - [JSP] 모델2 기반의 MVC(MODEL-VIEW-CONTROLLER)
계층형(=답변형) 게시판 : 게시글(원글)과 그에 대한 답글이 계층관계(종속관계)를 가지는 게시판
MVC 패턴의 요소
1) 모델(Model) : 로직을 가지는 부분
- DB와의 연동을 통해서 데이터를 가져와 작업 처리
- 처리한 작업의 결과를 데이터로서 DB에 저장하는 일을 처리
- JavaBean, Java class(처리 로직)가 이에 해당
- DAO와 DTO를 합친 것과 같음
DAO(Data Access Object) : DB를 연동하는 클래스
DTO(Data Transfer Object) : 해당 테이블의 구조를 그대로 설계한 클래스-JavaBean
2) 뷰(View) : 요청에 대한 응답 결과를 화면에 표시 -> 사용자 인터페이스 제공
- JSP페이지의 request는 컨트롤러인 서블릿과 같은 객체로 공유
${requestScope.result} 또는 request.getAttribute("result")와 같이 사용해서 결과를 화면에 표시
- 정보를 보여주는 역할'만'을 담당
- JSP페이지가 이에 해당
3) 컨트롤러(Controller) : 어플리케이션의 흐름 제어 -> 서블릿(Servlet) 이 이에 해당
- 사용자의 요청을 받아서 요구 사항을 분석 후 로직 처리를 모델에게 넘겨줌
- 모델이 처리한 로직 처리결과를 사용자에게 응답하기 위해 뷰로 보냄
- 웹브라우저의 요청을 받는 진입점
0. 프로젝트 준비
1) DB 테이블 생성
2) 프로젝트 생성
3) 필요한 라이브러리들 폴더에 넣기
4) DBCP 설정에 필요한 코드 작성(server.xml, web.xml)
5) 서버테스트(usePool.jsp)
1. java resources 패키지, 클래스 생성 ( 우선 필수 코드들만 작성 )
1) com.doccomsa.web.persistence 패키지 - 모델 DAO / ★커넥션풀 설정 / DB연동
- BoardDAO.class
package com.doccomsa.web.persistence;
import java.sql.Connection;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class BoardDAO {
// 커넥션풀 설정, 가장 중요한 class
// 싱글톤 패턴디자인-자기 자신의 객체 생성 static
private static BoardDAO instance = new BoardDAO();
// 자기 자신의 객체 리턴 static
public static BoardDAO getInstance() {
return instance;
}
// 생성자 접근 막아주기
private BoardDAO() {}
// 커넥션 풀에서 커넥션 객체를 얻어오는 메소드-예외처리 필수
private Connection getConnection() throws Exception{
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/jsptesto");
return ds.getConnection();
}
/*
DB를 연동하고 필요한 로직을 수행하는 메소드를 작성하는 곳
*/
}
2) com.doccomsa.web.controller 패키지 - 서블릿 / 클라이언트의 요청을 받고 처리 결과를 뷰로 보냄
- FrontController.class
package com.doccomsa.web.controller;
import java.io.IOException;
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;
@WebServlet("*.do")
public class FrontController extends HttpServlet {
private static final long serialVersionUID = 1L;
public FrontController() {
super();
}
// doGet() : 클라이언트가 서버에 요청을 할 때 주소 오픈
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//response.getWriter().append("Served at: ").append(request.getContextPath());
//System.out.println("doGet");
doAction(request,response); // 메소드를 먼저 호출하는 작업
}
// doPost : 클라이언트가 서버에 요청을 할 때 주소 오픈하지않음
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//doGet(request, response);
//System.out.println("doPost");
doAction(request,response); // 메소드를 먼저 호출하는 작업
}
// get, post 어떤 방식으로 호출되어도 하나의 메소드로 해결
private void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 한글깨짐방지
String viewPage=null;
// request : 클라이언트의 요청
String uri = request.getRequestURI();
String conPath = request.getContextPath();
String com = uri.substring(conPath.length());
// url 구조
// <스킴>://<사용자이름>:<비밀번호>@<호스트>:<포트>/<경로>?<질의>#<프레그먼트>
// url : 주소전체 -> http://localhost:8080/coding02/list.do
// uri : 인터넷 기본 주소 제외 -> /coding02/list.do
// conPath = ContextPath : 프로젝트명 -> coding02
// com = uri.substring(conPath.length()) : 프로젝트명 뒤의 주소 -> list.do
// console에서 출력해보기
System.out.println(uri); // /coding02/*.do
System.out.println(conPath); // /coding02
System.out.println(com); // /*.do
/*
사용자의 입력 정보를 DB에 저장하고 서블릿 객체 생성,
뷰 페이지로 이동 시켜주는 코드 작성하는 곳
viewPage의 각 기능에 따라!
1) *.jsp : 실행이되어 결과 전송 후 페이지 이동
2) *.do : FrontController로 다시 호출되어 돌아옴
-> 생각하지않고 코드를 작성한다면
객체가 생성되어있지 않은 상태를 호출하는것과 마찬가지
*/
// RequestDispatcher : 사용자의 요청을 받아서 페이지를 이동시키는 기능을 가진 클래스
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
// 뷰페이지에 들어있던 요청과 응답을 해당 뷰로 forwarding -> 예외 처리or전가 필수 메소드
// throws ServletException, IOException
dispatcher.forward(request, response);
}
}
3) com.doccomsa.web.dto 패키지 - 모델 DTO / 테이블 구조 반영
- BoardVO.class
package com.doccomsa.web.dto;
import java.sql.Timestamp;
public class BoardVO {
/*
CREATE TABLE BBS(
bIdx NUMBER PRIMARY KEY, --글번호
bWriter VARCHAR2(50 BYTE), --작성자
bSubject VARCHAR2(50 BYTE), --글제목
bContent VARCHAR2(1000 BYTE), --글내용
bDate DATE default sysdate, --작성일
bReadCount NUMBER default 0, --조회수
bRef NUMBER default 0, --계층형게시판, 글의 그룹
bRestep NUMBER default 0, --계층형게시판, 글의 순서(order)
bRelevel NUMBER default 0 --계층형게시판, 글의 레벨
);
*/
private int bIdx;
private String bWriter;
private String bSubject;
private String bContent;
private Timestamp bDate;
private int bReadCount;
private int bRef;
private int bRestep;
private int bRelevel;
public BoardVO(int bIdx, String bWriter, String bSubject, String bContent, Timestamp bDate, int bReadCount, int bRef, int bRestep, int bRelevel) {
super();
this.bIdx = bIdx;
this.bWriter = bWriter;
this.bSubject = bSubject;
this.bContent = bContent;
this.bDate = bDate;
this.bReadCount = bReadCount;
this.bRef = bRef;
this.bRestep = bRestep;
this.bRelevel = bRelevel;
}
public int getbIdx() {
return bIdx;
}
public void setbIdx(int bIdx) {
this.bIdx = bIdx;
}
// ★중략..모든 필드 getter/setter 생성 해야함
public int getbRelevel() {
return bRelevel;
}
public void setbRelevel(int bRelevel) {
this.bRelevel = bRelevel;
}
// 객체에 데이터가 존재하는 체크 목적
// 어떤 에러인지? 확인 용도 : DB / DB를 가져오는 메소드 / jsp페이지
@Override
public String toString() {
return "BoardVO [bIdx=" + bIdx + ", bWriter=" + bWriter + ", bSubject=" + bSubject + ", bContent=" + bContent
+ ", bDate=" + bDate + ", bReadCount=" + bReadCount + ", bRef=" + bRef + ", bRestep=" + bRestep
+ ", bRelevel=" + bRelevel + "]";
}
}
4) com.doccomsa.web.command 패키지 - DB로 보내기전 비즈니스 로직을 처리하는 기능 -> DAO호출
(* 비즈니스 로직(Business Logic) : 유저의 요청에 따른 결과물을 만들어내기 위한 일련의 작업들)
- ICommand.interface를 상속받아 각각의 기능을 담당하는 Command.class
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface ICommand {
// public abstract 생략
void action(HttpServletRequest request, HttpServletResponse response);
}
2. View(.jsp 페이지)
클라이언트의 요청에 대해서 FrontController에서 작업 분기
-> 해당 기능의 Command class가 작동하여 DAO를 이용한 데이터 베이스 작업
-> DAO 클래스의 결과물을 DTO객체가 View(.jsp 페이지)로 전달
-> View페이지에서 클라이언트의 request에 대한 response를 화면(UI)을 구성하여 출력
- 기능별 코드 작업 - (코드 실행 순서대로 각각의 코드 작성)
1. 글쓰기 페이지
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
- 글쓰기 폼 write_view.jsp 파일만 먼저 생성
- 글쓰기 저장 후 글목록을 보여주는 list.do 요청 코드 작성
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 WriteCommand 클래스 파일만 먼저 생성
// FrontController
if(com.equals("/write_view.do")) { // 주소가 ~라면 실행
// 1. 주소 요청이 들어오면 글쓰기폼 전송 -> 사용자에게 view.jsp 페이지 보여주기
viewPage="write_view.jsp";
// 2. 사용자가 작성한 글쓰기 저장 후 write.do 실행
}else if(com.equals("/write.do")) {
// 3. 글쓰기 command 객체 생성 후 요청
ICommand command = new WriteCommand();
command.action(request, response);
// 4. 글쓰기 저장 후 목록을 보여주는 요청 뷰 페이지에 넣기
viewPage="list.do";
}
2) 글쓰기 폼 write_view.jsp 코드 작성
- <form action="write.do" method="post">
<%-- write_view.jsp --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>write</title>
</head>
<body>
<form action="write.do" method="post">
<table style="width:500px;">
<tr>
<td>이름</td>
<td><input type="text" name="bWriter" size="50"></td>
</tr>
<tr>
<td>제목</td>
<td><input type="text" name="bSubject" size="50"></td>
</tr>
<tr>
<td>내용</td>
<td><textarea name="bContent" row="10"></textarea></td>
</tr>
<tr>
<td><input type="submit" value="입력"> <a href="list.do">목록보기</a></td>
</tr>
</table>
</form>
</body>
</html>
3) WriteCommand -> DB연동 전 비지니스 로직을 처리해주는 코드 작성
- 사용자가 글쓰기 폼에서 정보 입력 후 write.do를 요청
- 다시 FrontController로 돌아와서 write.do 요청이 들어왔을 때 실행되는 코드 작성
- 사용자가 입력한 정보 파라미터로 받아주기
- DB를 연동해주는 BoardDAO write() 호출 코드 작성 (아직 작성되지않은 메소드)
// WriteCommand
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.persistence.BoardDAO;
public class WriteCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
// 사용자가 글쓰기 폼에서 입력한 정보 받기
String bWriter = request.getParameter("bWriter");
String bSubject = request.getParameter("bSubject");
String bContent = request.getParameter("bContent");
// DB연동해주는 BoardDAO write() 메소드 호출
BoardDAO dao = BoardDAO.getInstance();
dao.write(bWriter, bSubject, bContent);
}
}
4) BoardDAO -> DB연동 코드 작성
- WriteCommand에서 호출한 write(String bWriter, String bSubject, String bContent) 코드 작성
// BoardDAO
// 글쓰기 저장1 : 소수 컬럼의 데이터 입력시 사용(파라미터로 받기)
// 사용자 입력 정보를 DB에 저장해주는 메소드
public void write(String bWriter, String bSubject, String bContent) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
String query="insert into bbs(bidx, bwriter, bsubject, bcontent, breadcount, bref, brestep, brelevel)"
+ "values(bbs_seq.NEXTVAL, ?, ?, ?, 0, bbs_seq.CURRVAL, 0, 0 )";
pstmt=conn.prepareStatement(query);
pstmt.setString(1, bWriter);
pstmt.setString(2, bSubject);
pstmt.setString(3, bContent);
// executeUpdate : insert문에 씀 / 테이블에 작업한 데이터의 수 반환(int)
int rn=pstmt.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(pstmt!=null) {pstmt.close();}
if(conn!=null) {conn.close();}
}catch(Exception e2) {
e2.printStackTrace();
}
}
}
// 글쓰기 저장2 : 테이블의 모든 컬럼 및 다수 컬럼의 데이터를 입력시 사용(DTO 객체로 받기)
// public void write(Board vo) {}
2. 글목록 페이지
-> 사용자 글 작성 후 사용자가 입력한 정보 DB연동
-> viewPage에 글목록을 보여주는 list.do 요청 코드 넣기
-> list.do가 요청되었을 때 실행되는 코드 작성 -> list.jsp
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
- 글목록 폼 list.jsp 파일만 먼저 생성
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 ListCommand 클래스 파일만 먼저 생성
// FrontController
else if(com.equals("/list.do")) {
// list.do 요청 받았을 때
ICommand command = new ListCommand();
command.action(request, response);
// viewPage에 리스트페이지 넣어주기
viewPage="list.jsp";
}
2) 글 목록 폼 list.jsp 코드 작성
- jstl / EL 사용
- 글 목록을 클릭하면 content_view.do 요청 쿼리스트링 작성
- 계층형 게시판 bRelevel(글의 레벨) 주의해서 코드 작성
- 글쓰기 페이지 요청 코드 작성 write_view.do
<%-- list.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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>list.jsp</title>
<style>
table{border-collapse: collapse;}
table, td, th{border:1px solid black;}
span.brelevel{font-size:7px;color:red;}
</style>
</head>
<body>
<table style="width:500px;">
<tr>
<td>번호</td>
<td>이름</td>
<td>제목</td>
<td>날짜</td>
<td>조회수(히트)</td>
</tr>
<%-- request.setAttribute("listAll",list); --%>
<%-- forEach 반복문/글목록 --%>
<c:forEach var="brd" items="${listAll}">
<tr>
<%-- 변수가 아닌 getter method 호출 --%>
<td>${brd.bIdx}</td>
<td>${brd.bWriter}</td>
<td>
<%-- 제목이 출력되는 위치 --%>
<%-- 1부터 글의 레벨까지 --%>
<c:forEach begin="1" end="${brd.bRelevel}"><span class="brelevel">[re]</span></c:forEach>
<a href="content_view.do?bIdx=${brd.bIdx}">${brd.bSubject}</a>
</td>
<td><fmt:formatDate value="${brd.bDate}" pattern="yyyy-MM-dd" /></td>
<td>${brd.bReadCount}</td>
</tr>
</c:forEach>
<tr>
<td colspan="5"><a href="write_view.do">글작성</a></td>
</tr>
</table>
</body>
</html>
3) ListCommand -> 글 목록을 가져오는 로직(DB요청)을 처리해주는 코드 작성
- list.do 요청이 들어왔을 때 테이블에 저장된 DB를 요청하는 메소드 호출 코드 작성 (아직 작성되지않은 메소드)
- List<Board> list = dao.list();
- list.jsp에 받아온 데이터를 "listAll"이라는 이름으로 세팅 후 데이터 테이블 정보 제공 (키,값) -> items="${listAll}"
// ListCommand
package com.doccomsa.web.command;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.dto.BoardVO;
import com.doccomsa.web.persistence.BoardDAO;
// 글 목록을 읽어오는 작업
public class ListCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
BoardDAO dao = BoardDAO.getInstance();
// BoardDAO list() 메소드 호출
List<BoardVO> list = dao.list();
// 다음에 진행되는 jsp페이지에 "listAll"이라는 이름으로 데이터 테이블 정보 제공(키,값)
request.setAttribute("listAll", list);
}
}
4) BoardDAO -> DB연동 코드 작성
- ListCommand에서 호출한 List<BoardVO> list() 메소드 코드 작성
// BoardDAO
// 리스트 : 테이블에서 모든 데이터를 읽어와서 list 컬렉션에 저장
// 리스트의 개수가 정확하지않아서 배열X -> 컬렉션 이용
// 패턴이 항상 같은 공식!!!!
public List<BoardVO> list(){
// Board DTO list 객체
List<BoardVO> list = new ArrayList<BoardVO>();
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
conn=getConnection();
String query="SELECT bidx, BWRITER, BSUBJECT, BCONTENT, BDATE, BREADCOUNT, BREF, BRESTEP, BRELEVEL "
+ "FROM bbs order by bref desc, brestep asc";
pstmt=conn.prepareStatement(query);
rs=pstmt.executeQuery();
while(rs.next()) {
int bIdx = rs.getInt("bIdx");
String bWriter = rs.getString("bWriter");
String bSubject = rs.getString("bSubject");
String bContent = rs.getString("bContent");
Timestamp bDate = rs.getTimestamp("bDate");
int bReadCount = rs.getInt("bReadCount");
int bRef = rs.getInt("bRef");
int bRestep = rs.getInt("bRestep");
int bRelevel = rs.getInt("bRelevel");
BoardVO vo = new BoardVO(bIdx, bWriter, bSubject, bContent, bDate, bReadCount, bRef, bRestep, bRelevel);
list.add(vo);
}
}catch(Exception e) {
e.printStackTrace();
}finally{
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return list;
}
3. 글 내용 보기 페이지
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
- 글내용보기 폼 content_view.jsp 파일만 먼저 생성
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 ContentCommand 클래스 파일만 먼저 생성
// FrontController
else if(com.equals("/content_view.do")) {
// content_view.do요청
// 글 내용 보기 페이지
ICommand command = new ContentCommand();
command.action(request, response);
// request.setAttribute("content",board);
viewPage="content_view.jsp";
}
2) 글 내용 보기 폼 content_view.jsp 코드 작성
- 글 내용 보기 폼에서 글 수정 요청 코드 작성 -> form action="modify.do" method="post" -> submit 버튼 클릭했을때 동작
- 글 내용 보기 폼에서 input type="submit" 수정 버튼 누르면 modify.do 요청됨 (따로 수정 페이지는 없음)
- 목록보기 a태그 버튼 -> list.do 요청
- 삭제하기 a태그 버튼 -> delete.do?bIdx=${content.bIdx} 쿼리스트링 코드 작성
- 답변하기 a태그 버튼 -> reply_view.do?bIdx=${content.bIdx} 쿼리스트링 코드 작성 -> reply_view.jsp 파일만 미리 생성
<%-- content_view --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>content_view</title>
</head>
<body>
<form action="modify.do" method="post">
<table style="width:500px;">
<%-- bIdx hidden --%>
<input type="hidden" name="bIdx" value="${content.bIdx}" />
<tr>
<td>번호</td>
<td>${content.bIdx}</td>
</tr>
<tr>
<td>조회수(히트)</td>
<td>${content.bReadCount}</td>
</tr>
<tr>
<td>이름</td>
<td><input type="text" name="bWriter" value="${content.bWriter}" readonly></td>
</tr>
<tr>
<td>제목</td>
<td><input type="text" name="bSubject" value="${content.bSubject}"></td>
</tr>
<tr>
<td>내용</td>
<td><textarea rows="10" name="bContent">${content.bContent}</textarea></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="수정">
<a href="list.do">목록보기</a>
<a href="delete.do?bIdx=${content.bIdx}">삭제</a>
<a href="reply_view.do?bIdx=${content.bIdx}">답변</a>
</td>
</tr>
</table>
</form>
</body>
</html>
3) ContentCommand -> 글 내용을 가져오는 로직(DB요청)을 처리해주는 코드 작성
- content_view.do 요청이 들어왔을 때 테이블에 저장된 DB를 요청하는 메소드 호출 코드 작성
- contentView(bIdx) : 사용자 bIdx로 글 내용을 가져오는 DAO 메소드 (아직 작성되지않은 메소드)
// ContentCommand
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.dto.BoardVO;
import com.doccomsa.web.persistence.BoardDAO;
public class ContentCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
// DB에 글 내용을 요청
// 사용자가 입력한 bIdx
String bIdx=request.getParameter("bIdx");
BoardDAO dao = BoardDAO.getInstance();
// contentView() : 글 내용을 가져오는 DAO 메소드
BoardVO board = dao.contentView(bIdx);
request.setAttribute("content", board);
}
}
4) BoardDAO -> DB연동 코드 작성
- 조회수 작업 메소드 private void readCount(String bIdx) 작성 후 호출해주기
//BoardDAO
// 조회수 작업 메소드
private void readCount(String bIdx) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
// bReadCount = bReadCount + 1 --> 기본 값 0
String query="UPDATE bbs SET bReadCount=bReadCount+1 WHERE bIdx=?";
pstmt=conn.prepareStatement(query);
pstmt.setString(1,bIdx);
//int rn=pstmt.executeUpdate();
pstmt.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(pstmt!=null) pstmt.close();
if(conn!=null) conn.close();
}catch(Exception e2) {
e2.printStackTrace();
}
}
}
- ContentCommand에서 호출한 Board contentView(String bIdx) 메소드 코드 작성
bRef : 글을 그룹화하기 위한 필드, 제목글과 그에 딸린 답변글이 그룹화
bRestep : 제목글과 답변글의 순서를 정리하기 위한 필드 / 부모와자식이순차적으로보이게할수있음
bRelevel : 글의 레벨을 저장하는 필드 (질문글 레벨0 -> 답변글 레벨1)
// BoardDAO
// 선택한 게시물의 내용보기 -> 선택한 게시물 '번호'가 나와야함
// 리턴값 Board -> 전체 정보 넘겨야함, 사용자로부터 아이디값 받기
public BoardVO contentView(String bIdx) {
// 조회수 작업 메소드 호 -> 반드시 함수 맨위에 해주기
readCount(bIdx);
BoardVO board=null;
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null; // DB에서 정보를 읽어오기 때문에 필요
try {
conn=getConnection();
String query="select * from bbs where bIdx=?";
pstmt=conn.prepareStatement(query);
// BIDX : NUMBER이기때문에 사용자로부터 받은 String bIdx 변환해주기
pstmt.setInt(1, Integer.parseInt(bIdx));
rs=pstmt.executeQuery();
if(rs.next()) {
// 정보를 받아오는 것이기때문에 지역변수 선언해서 변수 안에 받아주기
int bidx=rs.getInt("bIdx");
String bwriter=rs.getString("bWriter");
String bsubject=rs.getString("bSubject");
String bcontent=rs.getString("bContent");
Timestamp bdate=rs.getTimestamp("bDate");
int breadCount=rs.getInt("bReadCount");
// bRef : 글을 그룹화하기 위한 필드, 제목글과 그에 딸린 답변글이 그룹화
// bRestep : 제목글과 답변글의 순서를 정리하기 위한 필드 / 부모와자식이순차적으로보이게할수있음
// bRelevel : 글의 레벨을 저장하는 필드 (질문글 레벨0 -> 답변글 레벨1)
int bref=rs.getInt("bref");
int brestep=rs.getInt("bRestep");
int brelevel=rs.getInt("bRelevel");
// BoardVO DTO클래스의 객체에 받아온 데이터 저장
board = new BoardVO(bidx, bwriter, bsubject, bcontent, bdate, breadCount, bref, brestep, brelevel);
}
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
}catch(Exception e2) {
e2.printStackTrace();
}
}
// System.out.println(board.toString());
// --> 어디서? 문제가 일어났는지 확인 용도 : 1) DB / 2) db가져오는 method / 3) 페이지를 보여주는 jsp
return board;
}
4. 글 내용 수정 페이지
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
- 글 내용 보기 폼에서 글 수정 요청 -> form action="modify.do" method="post" -> submit 버튼 클릭했을때 동작
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 ModifyCommand클래스 파일만 먼저 생성
- 수정 후 바로 list.jsp 페이지로 요청하기때문에 따로 jsp 페이지 작성은 하지않아도 됨
// FrontController
else if(com.equals("/modify.do")) {
// 글 내용 수정 요청
ICommand command = new ModifyCommand();
command.action(request, response);
// 수정 후 리스트페이지로 가는 요청
viewPage="list.do";
}
2) ModifyCommand -> 글 수정 요청 로직(DB요청)을 처리해주는 코드 작성
- modify.do 요청이 들어왔을 때 사용자가 수정한 데이터를 테이블에 저장된 DB에 저장하는 요청 메소드 호출 코드 작성
- modify(bIdx,bSubject,bContent) : 사용자의 ID정보(확인용)와 수정하는 제목, 내용을 저장해 수정하는 DAO 메소드(아직 작성되지않은 메소드)
// ModifyCommand
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.persistence.BoardDAO;
public class ModifyCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
// 글 수정 요청 DB요청
// 글 수정 요청을 하는 사용자로부터 Id정보와 수정하는 제목, 내용 받기
String bIdx = request.getParameter("bIdx");
String bSubject = request.getParameter("bSubject");
String bContent = request.getParameter("bContent");
// BoardDAO 객체를 얻어온 후 modify() 메소드 호출
BoardDAO dao = BoardDAO.getInstance();
dao.modify(bIdx,bSubject,bContent);
}
}
3) BoardDAO -> DB연동 코드 작성
- ModifyCommand에서 호출한 modify(String bIdx, String bSubject, String bContent) 메소드 코드 작성
// BoardDAO
// 수정-업데이트 메소드
public void modify(String bIdx, String bSubject, String bContent) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
String query="update bbs set bSubject=?, bContent=? where bIdx=?";
pstmt=conn.prepareStatement(query);
pstmt.setString(1, bSubject);
pstmt.setString(2, bContent);
pstmt.setInt(3, Integer.parseInt(bIdx));
int rn = pstmt.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(pstmt!=null)pstmt.close();
if(conn!=null)conn.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
5. 글 삭제 페이지
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
- 글 내용 보기 폼에서 글 삭제 요청 / <a href="delete.do?bIdx=${content.bIdx}">삭제</a> 버튼 클릭했을때 동작
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 DeleteCommand클래스 파일만 먼저 생성
- 삭제 후 바로 list.jsp 페이지로 요청하기때문에 따로 jsp 페이지 작성은 하지않아도 됨
// FrontController
else if(com.equals("/delete.do")) {
// 글 삭제 요청
ICommand command = new DeleteCommand();
command.action(request, response);
// 삭제 후 리스트페이지로 가는 요청
viewPage="list.do";
}
2) DeleteCommand -> 글 삭제 요청 로직(DB요청)을 처리해주는 코드 작성
- delete.do 요청이 들어왔을 때 사용자가 입력한 아이디가 파라미터인 글 삭제 요청 메소드 호출 코드 작성
- delete(bIdx) : 사용자의 ID정보(확인용)와 글 삭제 요청 DAO 메소드(아직 작성되지않은 메소드)
// DeleteCommand
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.persistence.BoardDAO;
public class DeleteCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
// 삭제하고자하는 게시물 번호
String bIdx = request.getParameter("bIdx");
// 삭제 작업 메소드 준비
BoardDAO dao = BoardDAO.getInstance();
dao.delete(bIdx);
}
}
3) BoardDAO -> DB연동 코드 작성
- DeleteCommand에서 호출한 delete(String bIdx) 메소드 코드 작성
// BoardDAO
// 게시물 삭제 메소드
public void delete(String bIdx) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
String query="delete from bbs where bIdx=?";
pstmt=conn.prepareStatement(query);
pstmt.setInt(1, Integer.parseInt(bIdx)); // bIdx->NUMBER
int rn = pstmt.executeUpdate();
}catch(Exception ex) {
ex.printStackTrace();
}finally {
try {
if(pstmt!=null)pstmt.close();
if(conn!=null)conn.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
★★ 6. 글 답변 페이지 ★★ 계층형 게시판
★ bRef : 글을 그룹화하기 위한 필드, 제목글과 그에 딸린 답변글이 그룹화
★ bRestep : 제목글과 답변글의 순서를 정리하기 위한 필드 / 부모와자식이순차적으로보이게할수있음
★ bRelevel : 글의 레벨을 저장하는 필드 (질문글 레벨0 -> 답변글 레벨1)
1) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
reply_view.do : 답변쓰기 페이지 요청
- 글 내용 보기 폼에서 글 답변쓰기 요청 / <a href="reply_view.do?bIdx=${content.bIdx}">답변</a> 버튼 클릭했을때 동작
- reply_view.do 요청이 왔을때 실행되는 답변쓰기 폼 reply_view.jsp 파일 content_view.jsp 에서 미리 생성했음
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 ReplyViewCommand 클래스 파일만 먼저 생성
// FrontController
else if(com.equals("/reply_view.do")) {
// 글 답변 버튼 클릭 -> 답변 페이지로 요청
ICommand command = new ReplyViewCommand();
command.action(request, response);
viewPage="reply_view.jsp";
}
2) 글 답변 쓰기 reply_view.jsp 코드 작성
- 글 답변 쓰기 폼 작성 -> form action="reply.do" method="post" -> submit 버튼 클릭했을때 동작 -> 답변 쓰기 완료
<%-- reply_view.jsp --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>reply_view</title>
</head>
<body>
<form action="reply.do" method="post">
<table style="width:500px;">
<%-- ReplyViewCommand --%>
<input type="hidden" name="bIdx" value="${reply.bIdx}">
<input type="hidden" name="bRef" value="${reply.bRef}">
<input type="hidden" name="bRestep" value="${reply.bRestep}">
<input type="hidden" name="bRelevel" value="${reply.bRelevel}">
<tr>
<%-- 고유번호/수정불가 --%>
<td>번호</td>
<td>${reply.bIdx}</td>
</tr>
<tr>
<td>조회수(히트)</td>
<td>${reply.bReadCount}</td>
</tr>
<tr>
<td>이름</td>
<td><input type="text" name="bWriter" value="${reply.bWriter}"></td>
</tr>
<tr>
<td>제목</td>
<td><input type="text" name="bSubject" value="${reply.bSubject}"></td>
</tr>
<tr>
<td>내용</td>
<td><textarea row="10" name="bContent">${reply.bContent}</textarea></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="답변"> <a href="list.do">목록</a></td>
</tr>
</table>
</form>
</body>
</html>
3) ReplyViewCommand -> DB연동 전 비지니스 로직을 처리해주는 코드 작성 -> 답변
- 답변을 쓰려는 글의 정보 파라미터로 받아주기
- DB를 연동해주는 replyView(bIdx)호출 코드 작성 (아직 작성되지않은 메소드)
// ReplyViewCommand
package com.doccomsa.web.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.dto.BoardVO;
import com.doccomsa.web.persistence.BoardDAO;
public class ReplyViewCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
String bIdx=request.getParameter("bIdx");
BoardDAO dao = BoardDAO.getInstance();
BoardVO board=dao.replyView(bIdx);
request.setAttribute("reply", board);
}
}
4) BoardDAO -> DB연동 코드 작성
- ReplyViewCommand에서 호출한 BoardVO replyView(String bIdx) 메소드 코드 작성
- 답변쓰기 전, 원래 글의 데이터 가져오기
- Duplicate local variable 변수명 // 중복 변수명 주의
// BoardDAO
// 답변 쓰기 전, 원글 데이터를 가져오는 메소드
public BoardVO replyView(String bIdx) {
BoardVO board = null;
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null; // 데이터 받아와서 출력
try {
conn=getConnection();
String query="select * from bbs where bIdx=?";
pstmt=conn.prepareStatement(query);
pstmt.setInt(1, Integer.parseInt(bIdx));
rs=pstmt.executeQuery();
if(rs.next()) {
//Duplicate local variable 변수명 , 중복 변수명 주의
int bidx=rs.getInt("bIdx");
String bwriter=rs.getString("bWriter");
String bsubject=rs.getString("bSubject");
String bcontent=rs.getString("bContent");
Timestamp bdate=rs.getTimestamp("bDate");
int breadCount=rs.getInt("bReadCount");
int bref=rs.getInt("bRef");
int brestep=rs.getInt("bRestep");
int brelevel=rs.getInt("bRelevel");
board=new BoardVO(bidx, bwriter, bsubject, bcontent, bdate, breadCount, bref, brestep, brelevel);
}
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if (rs != null)
rs.close();
if (pstmt!= null)
pstmt.close();
if (conn!= null)
conn.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
return board;
}
5) FrontController.java 코드 작성 / 클라이언트의 요청을 받고 로직 실행 요청
reply.do : 답변쓰기 완료 요청
- 글 답변쓰기 완료 후 reply.do 요청이 왔을때 list.do 요청 실행
- ICommand를 상속받아 DB연동전 비즈니스 로직을 처리하는 ReplyCommand 클래스 파일만 먼저 생성
// FrontController
else if(com.equals("/reply.do")) {
// 글 답변쓰기 완료 요청
ICommand command = new ReplyCommand();
command.action(request, response);
viewPage="list.do";
}
// viewPage : 1) *.jsp -실행이 되어 결과가 전송 / 2) *.do -FrontController 다시 호출됨
// list.jsp -> 그대로 호출이 된다면 객체가 생성되어있지 않은 상태를 호출하는거나 마찬가지
// RequestDispatcher : 사용자의 요청을 받아서 페이지를 이동시키는 기능을 가진 클래스
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
// 뷰페이지에 들어있던 요청과 응답을 해당 뷰로 forwarding -> 예외 처리or전가 필수 메소드
// throws ServletException, IOException
dispatcher.forward(request, response);
6) ReplyCommand -> DB연동 전 비지니스 로직을 처리해주는 코드 작성 -> 답변완료 후 버튼 누름
- reply_view.jsp에서 답변 작성 후 submit 버튼 누르면 reply.do 요청 실행
- request.getParameter를 통해 사용자가 입력한 정보를 변수에 받아주기
- DB를 연동(저장)해주는 replyView(bIdx, bWriter, bSubject, bContent, bRef, bRestep, bRelevel)호출 코드 작성 (아직 작성되지않은 메소드)
// ReplyCommand
package com.doccomsa.web.command;
import java.sql.Timestamp;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.doccomsa.web.persistence.BoardDAO;
public class ReplyCommand implements ICommand {
@Override
public void action(HttpServletRequest request, HttpServletResponse response) {
// 게시물번호
String bIdx=request.getParameter("bIdx");
// 답변 입력내용
String bWriter=request.getParameter("bWriter");
String bSubject=request.getParameter("bSubject");
String bContent=request.getParameter("bContent");
// 부모글 정보
String bRef=request.getParameter("bRef");
String bRestep=request.getParameter("bRestep");
String bRelevel=request.getParameter("bRelevel");
BoardDAO dao=BoardDAO.getInstance();
dao.reply(bIdx, bWriter, bSubject, bContent, bRef, bRestep, bRelevel);
}
}
7) BoardDAO -> 계층형 게시판 메소드 작성 -> DB연동 코드 작성
- reply 메소드(코드 작성 전)에서 호출한 계층형 게시판 업데이트 메소드 작성
- private void replyUpdate(String bRef, String bRestep) / 매개변수는 reply메소드에서 받아오기
// BoardDAO
// 계층형 게시판 update method
private void replyUpdate(String bRef, String bRestep) {
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
String query="update bbs set bRestep=bRestep+1 where bRef=? and bRestep>?";
pstmt=conn.prepareStatement(query);
pstmt.setInt(1,Integer.parseInt(bRef));
pstmt.setInt(2,Integer.parseInt(bRestep));
int rn = pstmt.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(pstmt!=null) pstmt.close();
if(conn!=null) conn.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
8) BoardDAO -> DB연동 코드 작성 -> INSERT
- ReplyCommand에서 호출한 reply 메소드 코드 작성
- 메소드 가장 최상위에 계층형 게시판 함수 호출 해주기
- 사용자가 답변한 내용 DB update
// BoardDAO
// 사용자가 답변한 내용 연동 : update, insert 2가지 기능 넣기
public void reply(String bIdx, String bWriter, String bSubject, String bContent,String bRef, String bRestep, String bRelevel) {
// 업데이트 메소드 호출(계층형 게시판) -> update후에 insert진행
// 메소드 시작할 때, 가장 최상위에 메소드 호출 해주기
replyUpdate(bRef,bRestep);
Connection conn=null;
PreparedStatement pstmt=null;
try {
conn=getConnection();
String query="insert into bbs(bIdx, bWriter, bSubject, bContent, breadcount, bRef, bRestep, bRelevel) values(bbs_seq.NEXTVAL,?, ?, ?, 0, ?, ?, ? )";
pstmt=conn.prepareStatement(query);
// bIdx는 bbs_seq.NEXTVAL 시퀀스 사용
pstmt.setString(1, bWriter);
pstmt.setString(2, bSubject);
pstmt.setString(3, bContent);
pstmt.setString(4, bRef);
pstmt.setInt(5, Integer.parseInt(bRestep)+1);
pstmt.setInt(6, Integer.parseInt(bRelevel)+1);
// insert문에 씀 / 테이블에 작업한 데이터의 수 반환 (int형)
int rn = pstmt.executeUpdate();
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
오류가 있을 수 있어요ㅠ 발견하시면 댓글 남겨주세요!
'Programming > etc' 카테고리의 다른 글
원산지결정기준-일반/공통기준, 일반/특례기준, 품목별원산지기준 (0) | 2021.01.13 |
---|---|
[JAVA] iText API를 이용해 html을 PDF로 변환 후 다운로드하는 예제 (0) | 2020.12.29 |
[ advanced rest client ] Rest API 방식 기능을 테스트하는 도구 (0) | 2020.08.12 |
[Eclipse] Workspace 환경 설정 복사 (0) | 2020.08.10 |
DBCP(커넥션풀)를 이용한 회원가입 페이지 예제 (0) | 2020.07.09 |