다연이네

[days09] MVC패턴으로 방명록 만들기 본문

JSP

[days09] MVC패턴으로 방명록 만들기

 다연  2021. 1. 6. 23:14
반응형

방명록 리스트 불러오기, 글 작성하기, 삭제하기, 수정하기

 

삭제 완료 후 리스트로 복귀

리스트에서 가장 우측의 [수정하기]를 클릭하면

수정 가능

[방명록 MVC패턴]
1.  days09 폴더
     ㄴ guestbook 폴더
           ㄴ images 폴더
           
2.  MV[C]     컨트롤러(서블릿)
    days09. guestbook.controller.ControllerUsingURI
    
3.  [M]VC     모델(로직 처리)   ~~Handler
     days09.guestbook.CommandHandler.java 인터페이스 String process()
                            .NullHandler.java         모델  (둘 다 지난번거 복사)
                                
4.  M[V]C  JSP 페이지 (마지막에 만들자)

5-1.  테이블 
        1) 방명록 테이블 생성
   create table guestbook_message
   (
      message_id number not null primary key
      , guest_name varchar2(50) not null
      , password varchar2(10) not null
      , message clob not  null
   ) ;
   
  2) create sequence seq_guestbook_message;
 
5-2.  DTO
 days09.guestbook.model.Message.java
 
 6-1.  DAO
 days09.guestbook.dao.MessageDao.java
 
 6-2.  DAO 클래스 안에서 Connection 객체에 접근하는 방법
  1) DAO 클래스의 메소드 안에서 직접 Connection 객체 생성
      insert()  방명록 쓰는  Connection con = ConnectionProvider.getConnection();
      selectList() 방명록 가져오는  Connection con = ConnectionProvider.getConnection();
      delete() 방명록 삭제하는  Connection con = ConnectionProvider.getConnection();
      => 매번 그 안에서 con 생성, 소멸
  2) DAO 객체를 생성할 때 [생성자]로 Connection 객체를 주입받기 (DI)
  3) DAO 클래스의 메소드의 파라미터로 Connection 객체를 주입받기(DI) (이번엔 이거 쓰겠다)
 
 
 7.  Service : 사용자의 요청 -> 처리하는 객체(DAO) ==  Model
  days09.guestbook.service 패키지
  1) GetMessageListService.java : 방명록 목록 반환 서비스 객체
  
  8.  DBCP (커넥션 풀) 사용  
  com.util.ConntectionProvider 안의 getConnection(){} 호출
  
  9. 
   finally{
     try{
       rs.close(), pstmt.close(), con.close()
     catch{ 
     반복적으로 들어가져 있기 때문에 아예
     rs, pstmt, con을 닫아주는 라이브러리를 생성해서 간단히 사용하도록 하자
     com. util. JdbcUtil 클래스
     
 10.  web. xml
 컨트롤러 서블릿 등록 days09.guestbook.controller.ControllerUsingURI

 

 

 

 

 

1. ex03.jsp

<%@ page 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>Insert title here</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="">
<style>
</style>
<script>
   $(document).ready(function (){     
   });
</script>
</head>
<body>

 <a href="/jspPro/days09/guestbook/list.do">방명록 목록</a>

</body>
</html>

 

2. 컨트롤러 days09.guestbook.controller.ControllerUsingURI

package days09.guestbook.controller;

import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

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

import days09.guestbook.controller.CommandHandler;

public class ControllerUsingURI  extends HttpServlet{
	
	
	@Override
	public void destroy() {
		// 웹 컨테이너 안에서 생성된 서블릿이 제거될때 호출되는 메소드
	}
	
	private Map<String, CommandHandler> commandHandlerMap =
			new HashMap<String, CommandHandler>();

	@Override
	public void init() throws ServletException {

		String path = getInitParameter("path");
		String configFilePath = getServletContext().getRealPath(path); 

		
		Properties prop = new Properties();
		try(FileReader fr = new FileReader(configFilePath)) {
			prop.load(fr); //자동으로 프로퍼티에 로딩
		} catch (Exception e) {
			throw new ServletException(e);
		}
		
		//prop -> commandHandlerMap 맵 채워넣는 작업
		Iterator<Object> ir = prop.keySet().iterator(); 
		while (ir.hasNext()) {
			String url = (String) ir.next(); //요청한 url :  "/hello.do" key값
			String handlerClassFullName = prop.getProperty(url); // "days08.mvc.hello.HelloHandler"
			
			try {
				Class<?> handlerClass = Class.forName(handlerClassFullName); //물리적인 클래스 파일명을 인자로 넣어주면 해당하는 클래스를 반환
				CommandHandler handlerInstance = (CommandHandler)handlerClass.newInstance();
				
				this.commandHandlerMap.put(url, handlerInstance);
				//요청이 들어오면 맵을 뒤져 어떤 요청인지 확인 가능하게 됐다
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
				throw new ServletException(e);
			} 
		}
	}//init

	@Override
	protected void doGet(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		String requestURI = request.getRequestURI();
		//System.out.println(requestURI); //     "/jspPro/hello.do"
		String contextPath = request.getContextPath(); // "/jspPro"
		if( requestURI.indexOf(contextPath)==0) { //요청 url이 그 contextPath로 시작하더라
			int beginIndex = contextPath.length();
			requestURI = requestURI.substring(beginIndex); //앞에 /jspPro 잘림 (endIndex 안주면 끝까지)
		}
		
		CommandHandler handler = this.commandHandlerMap.get(requestURI);
		
		//일 시켜야 함
		//M[View]C == JSP 페이지를 돌려줌
		String viewPage = null;
		try {
			viewPage = handler.process(request, response);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		if(viewPage!=null) {
			
			RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
			dispatcher.forward(request, response);
		}
		
	}//doGet

	@Override
	protected void doPost(HttpServletRequest request
			, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}//doPost
}//class

 

2 + web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>jspPro</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  
  <servlet>
  	<servlet-name>guestListController</servlet-name>
  	<servlet-class>days09.guestbook.controller.ControllerUsingURI</servlet-class>
  	<init-param><!-- 경로가 바뀌어도 수정하기 쉽게 -->
   <param-name>path</param-name>
   <param-value>/days09/guestbook/commandHandler.properties</param-value>
   </init-param> 
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>guestListController</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <session-config>
  <session-timeout>30</session-timeout>
  </session-config>

   <filter> <!-- 이 필터를 거친 후 밑 필터를 거친다 (필터체인 이렇게 일어남) -->
  <filter-name>EncodingFilter</filter-name>
  <filter-class>com.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value> <!--나중에 이 값만 바꾸면 됨  -->
  </init-param>
  </filter>
  <filter-mapping>
  <filter-name>EncodingFilter</filter-name>
  <url-pattern>/*</url-pattern> <!-- 모든 -->
  </filter-mapping>

  <resource-ref>
	 <description>Oracle Datasource example</description>
	 <res-ref-name>jdbc/myoracle</res-ref-name>
	 <res-type>javax.sql.DataSource</res-type>
	 <res-auth>Container</res-auth>
  </resource-ref>
  
</web-app>

2 + commandHandler.properties

# Request = Model 
/days09/guestbook/list.do=days09.guestbook.command.GetMessageListHandler
/days09/guestbook/write.do=days09.guestbook.command.WriteMessageHandler
/days09/guestbook/deleteMessage.do=days09.guestbook.command.DeleteMessageHandler
/days09/guestbook/updateMessage.do=days09.guestbook.command.UpdateMessageHandler

 

 

3. 모델 - 리스트 출력 days09.guestbook.command.GetMessageListHandler 

package days09.guestbook.command;

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

import days09.guestbook.controller.CommandHandler;
import days09.guestbook.service.GetMessageListService;
import days09.guestbook.service.MessageListView;

public class GetMessageListHandler implements CommandHandler { //days09 commandHandler (실수하면 안됨)

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println(">GetMessageListHandler.process() 호출..");
		
		String pCurrentPage = request.getParameter("page");
		int currentPage = 1;
		if(pCurrentPage!=null) {
			currentPage = Integer.parseInt(pCurrentPage);
		}
		
		//싱글톤
		GetMessageListService messageListService 
				= GetMessageListService.getInstance();
		
		MessageListView viewData =  messageListService.getMessageList(currentPage);
		
		request.setAttribute("viewData", viewData);
		//원래는 2개 담았는데 전부 다를 하나에 담는 것이 이 클래스의 목적
		
		return "/days09/guestbook/list.jsp";
	}

}

3. 모델 - 글쓰기 days09.guestbook.command.WriteMessageHandler

package days09.guestbook.command;

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

import days09.guestbook.controller.CommandHandler;
import days09.guestbook.model.Message;
import days09.guestbook.service.WriteMessageService;

public class WriteMessageHandler implements CommandHandler {

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println(">write.do 요청 :WriteMessageHandler.process()호출됨.. ");
		//결과물이 있다면 request에 담고
		//redirect /forward 시키는 것이 주 목적
		
		String guest_name = request.getParameter("guest_name");
		String password = request.getParameter("password");
		String message = request.getParameter("message");
		
		//서비스 DTO
		Message msg = new Message();
		msg.setGuest_name(guest_name);
		msg.setPassword(password);
		msg.setMessage(message); 
		//dto.setMessage_id(message_id);
		//message_id는 자동으로 시퀀스로 증가
		
		
		//WriteMessageService 클래스 만들기 (만들고 와서 마지막에 여기와서 추가)
		WriteMessageService messageService = WriteMessageService.getInstance();
		messageService.write(msg);
		
		//글을 쓰고 난 후 어디로 가야할까?
		//list.do 요청을 해야 한다.
		
		//주의) list.jsp주면 단순히 list.jsp만 뿌려질 것이다.(컨트롤러 안거치기 때문에 게시글 없음)
		//그게 아니라 list.do 로 요청이 되어져야지 해당 요청에 의해 컨트롤러 응답받고 거쳐 
		//list.jsp 요청 못하도록 처리해야한다. WEB_INF > views 폴더 생성 이동
		//list.do를 요청할건데 포워딩?리다이렉트?
		//보여지긴 보여지는데 write.do(포워딩)가 있는게 낫겠냐 list.do(리다이렉트)가 있는게 낫겠냐?
		// list.do가 낫겠다. => [리다이렉트] 하자
		String location = "list.do";
		response.sendRedirect(location); 
		return null;
		//무조건 포워딩 할게 아니라 리다이렉트할 때도 있다는 것 기억, 클래스 타입으로 return하는 것
		//리턴타입이 String이 아니라 객체여야함
	}
}

3. 모델 - 글삭제 days09.guestbook.command.DeleteMessageHandler

package days09.guestbook.command;

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

import days09.guestbook.controller.CommandHandler;
import days09.guestbook.model.Message;
import days09.guestbook.service.DeleteMessageService;

//파라미터 deleteMessage.do?message_id=1&password=1234;
public class DeleteMessageHandler implements CommandHandler {

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println(">deleteMessage.do 요청 : DeleteMessageHandler.process() 호출..");
		
		
		int messageId = Integer.parseInt(request.getParameter("messageId"));
		String password = request.getParameter("password");
		//누군가가 악의적으로 url 뒷부분 입력하면 삭제된다 => post로 가든
		// 어딘가에 표시가 되어있지 않으면 아무 브라우저 보지도 않는 페이지 입력하면 삭제 일어남
		//웹보안
		
		//System.out.println(message_id+"/"+password);

		
		//만들고오기 
		DeleteMessageService deleteMessageService = DeleteMessageService.getInstance();
		deleteMessageService.delete(messageId, password);
		
		response.sendRedirect("list.do?delete=success"); 
		//delete.do 요청은 안보이고 list.do가 보여야하니 redirect
		return null;
	}

}

3. 모델 - 글수정 days09.guestbook.command.UpdateMessageHandler

package days09.guestbook.command;

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

import days09.guestbook.controller.CommandHandler;
import days09.guestbook.model.Message;
import days09.guestbook.service.GetMessageListService;
import days09.guestbook.service.UpdateMessageService;

public class UpdateMessageHandler implements CommandHandler {

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		//수정할 수 있도록 입력받을 준비 해야하며, 입력버튼 누르면 db에 저장
		//수정할 페이지로 넘겨줘야함 수정할 글번호의 내용을 담아서 페이지에 뿌려서 원래값은 보여지고
		//원래값에서 수정받은 값을 저장
		
		//GET
		//POST 다르다 
		//GET - 수정 페이지 (a태그로 요청하면 입력폼을 뿌려줘야함 )
		//POST - 수정 페이지에서 입력한 값들을 db에 update한 후 list.do로 이동
		
		Message msg = new Message();
		if(request.getMethod().equalsIgnoreCase("GET")) {
			//updateMessage.do?messageId=2
			//수정 페이지
			System.out.println(">updateMessage.do 요청 : UpdateMessageHandler.process() GET 호출..");
			
			int messageId = Integer.parseInt(request.getParameter("messageId"));
			
			GetMessageListService messageService = GetMessageListService.getInstance();
			Message message = messageService.getMessage(messageId); //클릭해서 메소드 만들기
			//null이 넘어오면 존재하지 않는 글 => 목록으로 뿌려라 (일단 제외)
			
			request.setAttribute("message", message);
			
			//getMessage메소드 다 만든 후 update.jsp 파일 추가
			return "/days09/guestbook/update.jsp";
			
		}else if(request.getMethod().equalsIgnoreCase("POST")) { 
			// 수정 페이지에서 입력한 값들을 db에 update한 후 list.do로 이동
			System.out.println(">updateMessage.do 요청 : UpdateMessageHandler.process() POST 호출..");
			
			//int message_id = Integer.parseInt(request.getParameter("message_id"));
			//disabled는 파라미터로 안넘어옴 
			//2째 방법 : update.jsp의 form action에 ?message_id= 달고가기
			int message_id = Integer.parseInt(request.getParameter("h_id"));
			String guest_name = request.getParameter("guest_name");
			String password = request.getParameter("password");
			String message = request.getParameter("message");
			
			//서비스 DTO
			
			msg.setMessage_id(message_id);
			msg.setGuest_name(guest_name);
			msg.setPassword(password);
			msg.setMessage(message); 
		}//if
		
		
		UpdateMessageService messageService = UpdateMessageService.getInstance();
		messageService.update(msg);
		
		String location = "list.do";
		response.sendRedirect(location+"?update=success");
		
		return null;
	}

}

 

 

3 + 모델이 implement할 인터페이스

days09.guestbook.controller.CommandHandler

package days09.guestbook.controller;

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

//로직을 처리할 모델(Model)객체가 공통적으로 구현할 인터페이스
public interface CommandHandler {

	public String process( HttpServletRequest request
			, HttpServletResponse response ) throws Exception ;

	
}

3 + Null을 처리할 모델

days09.guestbook.controller.NullHandler

package days09.guestbook.controller;

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

public class NullHandler implements CommandHandler{

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
		return null;
	}

}

 

 

4. JSP (마지막에 생성)

5. DB 테이블, 시퀀스 생성

 

 

6. DTO  days09.guestbook.model.Message

package days09.guestbook.model;

//방명록 DTO
public class Message {
	
	//fileds
	private int message_id;
	private String guest_name;
	private String password;
	private String message;
	
	//getter-setter
	public int getMessage_id() {
		return message_id;
	}
	public void setMessage_id(int message_id) {
		this.message_id = message_id;
	}
	public String getGuest_name() {
		return guest_name;
	}
	public void setGuest_name(String guest_name) {
		this.guest_name = guest_name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	
	//추가
	public boolean hasPassword() {
		return password != null && !password.isEmpty();
	}
	public boolean matchPassword(String pwd) {
		return password != null && !password.equals(pwd);
	}
	
	
}

 

 

7.  DAO days09.guestbook.dao.MessageDao

package days09.guestbook.dao;

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

import com.util.ConnectionProvider;
import com.util.JdbcUtil;

import days09.guestbook.model.Message;

//싱글톤 (매번 dao만들 필요 없이 한번만 만들면 됨)
public class MessageDao {
	
	private static MessageDao messageDao = new MessageDao();
	
	private MessageDao() {} //생성자의 접근지정자가 private라 바로 객체 생성 못하게 막기
	
	/* 2. 생성자를 통한 Connection 객체 주입 (전달)
	          - 트랜잭션 처리 가능
	          - 매번 서비스 클래스 -> DAO 객체 새로 생성한다는 단점 (지금은 싱글톤이기때문에 매번 생성하지 않아도 됨)
	private Connection con = null;
	private MessageDao(Connection con) {
		this.con = con;
	}
	*/
	public static MessageDao getInstance() {
		return messageDao;
	}//getInstance 
	
	/* 1. 일일히 생성~소멸
	public int insert() {
		Connection con = ConnectionProvider.getConnection();
	}
	public List<Message> selectList(){
		Connection con = ConnectionProvider.getConnection();
	}
	*/
	
	//3. 파라미터로 Connection 객체를 주입받기
	//방명록 쓰기 
	public int insert(Connection con, Message message) throws SQLException{
		PreparedStatement pstmt = null;
		String sql = "insert  into  guestbook_message " +
							" (message_id, guest_name, password, message) "+
							" values (seq_guestbook_message.nextval, ?, ?, ? ) ";
		
		try {
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, message.getGuest_name());
			pstmt.setString(2, message.getPassword());
			pstmt.setString(3, message.getMessage());
				
			return pstmt.executeUpdate();
		} finally { //예외는  throws를 통해 넘어갈거라 필요x
			JdbcUtil.close(pstmt);
		}
	}
	
	

	//총 방명록 게시글 수 반환하는 메소드
	public int selectCount(Connection conn) throws SQLException { //예외 발생시 떠넘겨 => GetMessageListServiced 의 catch에 잡혀 예외메시지 출력
		Statement stmt = null;
	      ResultSet rs = null;
	      try {
	         stmt = conn.createStatement();
	         rs = stmt.executeQuery(
	               "select count(*) from guestbook_message");
	         rs.next();
	         return rs.getInt(1);
	      } finally {
	         JdbcUtil.close(rs);
	         JdbcUtil.close(stmt);
	      }
	}

	//방명록 목록 반환하는 메소드
	public List<Message> selectList(Connection conn
			, int firstRow, int endRow) throws SQLException {
		
		PreparedStatement pstmt = null;
	      ResultSet rs = null;
	      try {
	         String sql = " select *                                                     ";
	         sql+=     " from (                                                       ";
	         sql+=   "     select rownum no, t.*                                    ";
	         sql+=   "     from (                                                   ";
	         sql+=   "         select  *      ";
	         sql+=   "         from guestbook_message                                     ";
	         sql+=   "         order by message_id desc                                    ";
	         sql+=   "     ) t                                                      ";
	         sql+=   " ) b                                                          ";
	         sql+=   " where b.no between ? and ?                  ";   

	         //검색은 안되는 쿼리
	         
	         pstmt = conn.prepareStatement(sql);
	         pstmt.setInt(1, firstRow );
	         pstmt.setInt(2, endRow );
	         rs = pstmt.executeQuery();
	         if (rs.next()) {
	            List<Message> messageList = new ArrayList<>();
	            do {
	               messageList.add(makeMessageFromResultSet(rs)); //do~while dto 채워넣기 함수로 만듦(반복때문)
	            } while (rs.next());
	            return messageList;
	         } else {
	            return Collections.emptyList(); //null 돌리는 것과 같음
	         }
	      } finally {
	         JdbcUtil.close(rs);
	         JdbcUtil.close(pstmt);
	      }
	}

	private Message makeMessageFromResultSet(ResultSet rs) throws SQLException {
		Message message = new Message();
	      message.setMessage_id(rs.getInt("message_id"));
	      message.setGuest_name( rs.getString("guest_name"));      
	      message.setPassword(rs.getString("password"));
	      message.setMessage(rs.getString("message"));
	       return message;
	}

	public Message select(Connection con, int message_id) throws SQLException {
	      PreparedStatement pstmt = null;
	      ResultSet rs = null;
	      try {
	         pstmt = con.prepareStatement(
	               "select * from guestbook_message "
	               + " where message_id = ?");
	         pstmt.setInt(1, message_id);
	         rs = pstmt.executeQuery();
	         
	         if (rs.next()) {
	            // makeMessageFromResultSet() 프로젝트 선언해서 사용.
	            return makeMessageFromResultSet(rs);
	         } else {
	            return null;
	         }
	      } finally {
	         JdbcUtil.close(rs);
	         JdbcUtil.close(pstmt);
	      }
	   }

	public int delete(Connection con, int message_id) throws SQLException {
	      PreparedStatement pstmt = null;
	      try {
	         pstmt = con.prepareStatement(
	               "delete from guestbook_message "
	               + " where message_id = ?");
	         pstmt.setInt(1, message_id);
	         return pstmt.executeUpdate();
	      } finally {
	         JdbcUtil.close(pstmt);
	      }
	   }

	public int update(Connection con, Message message) throws SQLException {
		String sql = "update guestbook_message "+
							 " set message=?, guest_name=? "+
							 " where message_id=? and password=? ";
		
		PreparedStatement pstmt = null;
		int rowCount = 0;
		try {
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, message.getMessage());
			pstmt.setString(2, message.getGuest_name());
			pstmt.setInt(3, message.getMessage_id());
			pstmt.setString(4, message.getPassword());
			
			rowCount = pstmt.executeUpdate();
		} finally {
			JdbcUtil.close(pstmt);
			// JdbcUtil.close(con); service에서 close하고있으니 여기서 필요x
		}
		return rowCount;
	}
	
}//class

 

 

8. Service -  리스트 출력 서비스 days09.guestbook.service.GetMessageListService

package days09.guestbook.service;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

import javax.naming.NamingException;

import com.util.ConnectionProvider;
import com.util.JdbcUtil;

import days09.guestbook.dao.MessageDao;
import days09.guestbook.model.Message;

//싱글톤
public class GetMessageListService {

	private GetMessageListService() {}
	private static GetMessageListService instance = new GetMessageListService();
	public static GetMessageListService getInstance() {
		return instance;
	}
	
	//한 페이지에 방명록 출력 개수
	private static final int MESSAGE_COUNT_PER_PAGE = 3;
	
	//매개변수: 현재 페이지 pageNumber
	//리턴타입: MessageListView
	public MessageListView getMessageList(int pageNumber) {
		
	   	 Connection conn = null;
		
	      int currentPageNumber = pageNumber;
	      
	      try {
	         //         DBConn.getConnectionn();
	         conn = ConnectionProvider.getConnection();
	         // dao 객체 생성
	         MessageDao messageDao = MessageDao.getInstance();
	         // 총 방명록 수
	         int messageTotalCount = messageDao.selectCount(conn);

	         List<Message> messageList = null;
	         int firstRow = 0;
	         int endRow = 0;
	         if (messageTotalCount > 0) {
	            firstRow =
	                  (pageNumber - 1) * MESSAGE_COUNT_PER_PAGE + 1;
	            endRow = firstRow + MESSAGE_COUNT_PER_PAGE - 1;
	            //      dao.selectList()     해당 페이지의 방명록을 select
	            messageList =
	                  messageDao.selectList(conn, firstRow, endRow);
	         } else {
	            currentPageNumber = 0;
	            messageList = Collections.emptyList(); //null주는거랑 똑같다
	         }
	         
	         
	         return new MessageListView(
					               messageList,
					               messageTotalCount
					               , currentPageNumber,
					               MESSAGE_COUNT_PER_PAGE
					               , firstRow, endRow);
	      } catch (SQLException | NamingException e) {
	         throw new ServiceException("목록 구하기 실패: " + e.getMessage(), e);
	      } finally {
	         JdbcUtil.close(conn);
	      }
	}

	public Message getMessage(int messageId) {
		
		 Connection con = null;
 
		 Message message = null;
	      try {
	        
	         con = ConnectionProvider.getConnection();
	         MessageDao messageDao = MessageDao.getInstance();
	  
	         message = messageDao.select(con, messageId);      
	      }catch (Exception e) {
	    	  throw new ServiceException("메시지 구하기 실패: " + e.getMessage(), e);
	      } finally {
	         JdbcUtil.close(con);
	      }
	      return message;  
	     
	}//
	
	
	
}//class

8 + days09.guestbook.service.MessageListView (리스트 출력시 필요)

package days09.guestbook.service;

import java.util.List;
import days09.guestbook.model.Message;

//days05.pageBlock + ArrayList<board> 모두를 담을 수 있는 클래스를 만들겠다
//list따로 pageBlock 따로 setAttribute 하지 않고 한번에 하도록
public class MessageListView {
	//이 클래스를 왜 만드는지 ? 
	//누군가가 방명록 목록을 보겠다 요청 (ex02 링크태그 클릭)
	//-> 모든 *.do 요청 -> 컨트롤러가 요청 받아 (web.xml에 컨트롤러 등록되어있는지 확인 필수)
	//

	private List<Message> messageList; //방명록 목록을 저장할 ArrayList
	
	private int messageTotalCount; //총 방명록 글 수
	private int pageTotalCount; //총 페이지 수
	private int currentPageNumber; //현재 페이지 번호
	private int messageCountPerPage; //한 페이지에 출력할 방명록 글 수 
	private int firstRow; //시작
	private int endRow; //끝
	
	//생성자
	public MessageListView(
			List<Message> messageList, 
			int messageTotalCount, 
			int currentPageNumber,
			int messageCountPerPage, 
			int firstRow, int endRow) {
		
		this.messageList = messageList;
		this.messageTotalCount = messageTotalCount;
		this.currentPageNumber = currentPageNumber;
		this.messageCountPerPage = messageCountPerPage;
		this.firstRow = firstRow;
		this.endRow = endRow;
		
		//총 페이지 수를 계산하는 메소드
		calculatePageTotalCount();
	}
	
	// 총 페이지 수를 계산해서 반환하는 메소드
	private void calculatePageTotalCount() {
		if(messageTotalCount==0) {
			pageTotalCount = 0;
		}else {
			pageTotalCount =(int) Math.ceil( (double)messageTotalCount/messageCountPerPage);
		}
	}

	
	
	//getter만 만들기 
	public List<Message> getMessageList() {
		return messageList;
	}

	public int getMessageTotalCount() {
		return messageTotalCount;
	}

	public int getPageTotalCount() {
		return pageTotalCount;
	}

	public int getCurrentPageNumber() {
		return currentPageNumber;
	}

	public int getMessageCountPerPage() {
		return messageCountPerPage;
	}

	public int getFirstRow() {
		return firstRow;
	}

	public int getEndRow() {
		return endRow;
	}

	
	//추가
	public boolean isEmpty() {
		return this.messageTotalCount==0;
	}
	
	
}//class

8. Service -  글쓰기 서비스 days09.guestbook.service.GetMessageListService

package days09.guestbook.service;

import java.sql.Connection;

import com.util.ConnectionProvider;
import com.util.JdbcUtil;

import days09.guestbook.dao.MessageDao;
import days09.guestbook.model.Message;

//싱글톤
public class WriteMessageService {
	
	private static WriteMessageService instance = new WriteMessageService();
	public static WriteMessageService getInstance() {
		return instance;
	}
	private WriteMessageService() {}
	//위에 3개가 싱글톤 
	
	
	public int write(Message message) { //dto == message
		Connection con = null;
		int rowCount = 0; //영향받은 레코드 수
		try {
			con = ConnectionProvider.getConnection();
			MessageDao dao = MessageDao.getInstance();
			rowCount = dao.insert(con, message);
		} catch (Exception e) {
			throw new ServiceException(">메시지 등록 실패 : "+e.getMessage(), e);
		}finally {
			JdbcUtil.close(con);
		}
		return rowCount;
	}
}

8. Service -  글 삭제서비스 days09.guestbook.service.GetMessageListService 

package days09.guestbook.service;

import java.sql.Connection;

import com.util.ConnectionProvider;
import com.util.JdbcUtil;

import days09.guestbook.dao.MessageDao;
import days09.guestbook.model.Message;

//싱글톤
public class DeleteMessageService {
	private static DeleteMessageService instance = new DeleteMessageService();
	public static DeleteMessageService getInstance() {
		return instance;
	}
	private DeleteMessageService() {}
	//윗줄 싱글톤
	
	//많은 데이터가 넘어온다면 Message message 매개변수로 주면 된다. (하나 담아서 dto로 넘겨주면 되니까)
	public int delete(int message_id, String password) {
		Connection con = null;
		int rowCount = 0; //영향받은 레코드 수
		try {
			con = ConnectionProvider.getConnection();
			MessageDao dao = MessageDao.getInstance();
			
			//내가 삭제하러갔는데 관리자가 이미 삭제했을수도 있다 (게시글이 없을수도 있다)
			// dao.select 만들기 - 마우스 올려
			Message message = dao.select(con, message_id); //게시글 정보를 가져오는 메소드
			if (message==null) {
				throw new MessageNotFoundException("해당 메시지가 없습니다");
			}
			if(!message.matchPassword(password)){
				throw new InvalidPasswordException("비밀번호가 일치하지 않습니다");
			}
			//dao.delete메소드 만들기 - 마우스 올려
			rowCount = dao.delete(con, message_id);
			
		}catch(InvalidPasswordException | MessageNotFoundException e) {
			JdbcUtil.rollback(con);
			throw e; //예외 재발생시켜 떠넘기기
		}
		catch (Exception e) {
			throw new ServiceException(">메시지 삭제 실패 : "+e.getMessage(), e);
		}finally {
			JdbcUtil.close(con);
		}
		return rowCount;
	}
	
}

8. Service -  글 수정 서비스 days09.guestbook.service.GetMessageListService

package days09.guestbook.service;

import java.sql.Connection;

import com.util.ConnectionProvider;
import com.util.JdbcUtil;

import days09.guestbook.dao.MessageDao;
import days09.guestbook.model.Message;

//싱글톤
public class UpdateMessageService {

	

	private static UpdateMessageService instance = new UpdateMessageService();
	public static UpdateMessageService getInstance() {
		return instance;
	}
	private UpdateMessageService() {}
	//싱글톤
	
	public int update(Message message) {
		Connection con = null;
		int rowCount = 0;
		try {
			con = ConnectionProvider.getConnection();
			MessageDao dao = MessageDao.getInstance();
			//update클릭해서 만들기
			rowCount  = dao.update(con, message);
		} catch (Exception e) {
			throw new ServiceException("메시지 수정 실패 : "+e.getLocalizedMessage(), e);
		}finally {
			JdbcUtil.close(con);
		}
		return rowCount;
	}
}

 

8 + 사용자 정의 예외 클래스 days09.guestbook.service 패키지 속

 

ServiceException

package days09.guestbook.service;

//사용자 정의 예외 클래스
public class ServiceException extends RuntimeException{

	public ServiceException(String message) {
		super(message);
	}
	
	public ServiceException(String message, Exception e) {
		super(message, e);
	}
}

MessageNotFoundException

package days09.guestbook.service;

//사용자 정의 예외 클래스
public class MessageNotFoundException extends ServiceException{

	public MessageNotFoundException(String message) {
		super(message);
	}
	
}

InvalidPasswordException

package days09.guestbook.service;

//사용자 정의 예외 클래스
public class InvalidPasswordException extends ServiceException{

	public InvalidPasswordException(String message) {
		super(message);
	}
}

 

9.  list.jsp

<%@ page import="days09.guestbook.model.Message"%>
<%@ page import="days09.guestbook.service.MessageListView"%>
<%@ page import="days09.guestbook.service.GetMessageListService"%>

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

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
   <title>방명록 메시지 목록</title>
<style>
/* 
데이터는 있으나 list페이지가 원하는 결과가 안나온다면 
컨트롤러를 열어 요청이 들어왔을때 요청을 체크해서 맵 안에서 체크해서 핸들러에 담긴 것 보고
핸들러로 가 프로세스 확인 -> viewData를 request.setAttribute를 담으니 이 것 확인
 viewPage에 담겨 포워딩 
viewData가 담겨짐 -> 
결론 : MessageListView viewData 확인 -> 서비스의 겟메세지로 가서 메세지리스트 확인
-> 안넘어온다면 dao 잘 돌려주고 있는지 담아서 돌려주는지 확인 
 */
body, * {
   font-size: 12px;
}

input[type=text], textarea {
   border: solid 1px gray;
}

.in {
   /* color: green; */
}

a {
   text-decoration: none;
}

a:hover {
   color: black;
}

</style>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 모달창(삭제하기) -->
<link rel="stylesheet"
   href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script
   src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script
   src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<script>
    // window.onload = function (){}
   $(function() {
      // 방명록 열기 닫기 버튼을 클릭
      $("#btnWrite").click(function (){
         $("#write_box").slideToggle("slow"
               , function (){    // 열기, 닫기 작업 된 후에 function() 호출 
             $("#btnWrite").val(
                   $("#btnWrite").val().indexOf("열기") != -1
                   ?"방명록 쓰기 닫기":"방명록 쓰기 열기");
         });
      });
      
      $('tr:even').css({
         'background' : 'gray',
         'color' : 'white'
      });
      
      $(':input').first().focus();

      // 클래스명이 btnDelete 을 클릭할 때.....
      $(".btnDelete").click(function(){
         // 삭제 게시글의 실제 비밀번호를 js 변수 
         del_password = $(this).attr('alt');  // 비밀번호
         del_messageId = $(this).attr('name'); // PK
         
         // 모달창 띄우는 함수 : modal()
           $("#myModal").modal({backdrop: "static"});
           $("#myModal").on('shown.bs.modal', function () {
              $('#password').focus();
           });
       });
      
      $('#myModal > div').css({
         "width":"400px"
         ,"top":'150px'
         ,"margin":'0 auto'
      });
      
      // 
      var del_messageId, del_password ;
      
      $('#btnDeleteGo').click(function (){
         // 삭제하기 위해 입력한 비밀번호  $('#password').val() 
         if( $('#password').val() != del_password){
            alert(' 비밀번호 틀림!!!');
            return;
         }
         //비밀번호가 맞으면 
         location.href=
            "deleteMessage.do?messageId="+
                  del_messageId+"&password="+del_password;
      });
      
      // 수정하기
      $('.btnEdit').click(function (){         
         //alert($(this).parents('tr').prev().css({"display":"none"}));
         alert( $(this).parents('tr').prev().find(".m_message").html());
         
      });
   });
</script>
</head>
<body>
<div style="text-align: center">
<input type="button" id="btnWrite" value="방명록 쓰기 열기"   />
</div>
<hr>
   <!-- list.jsp    write_box -->
   <div id="write_box" style="display: none;">   
   <form action="/jspPro/days09/guestbook/write.do" method="post">
      <div
         style="text-align:center;border: solid 1px gray; width: 270px; margin: 0 auto; padding: 10px">
         <h3>방명록<span class="glyphicon glyphicon-heart-empty"></span></h3>
         이름 : <input type="text" name="guest_name" class="in"><br>
         암호 : <input type="text" name="password" class="in"><br>
         <!-- 위 name속성값들은 항상 dto의 필드명 (==db의 컬럼명)과 맞추기(가능하면)  -->
         메시지 :<br>
         <textarea rows="3" style="width:75%" name="message" class="in"></textarea>
         <br> <input type="submit" value="메시지 남기기">
      </div>
   </form>
</div>   
   <hr> 
   <div id="gm_box"
      style="border: solid 1px gray; width: 270px; margin: 0 auto; padding: 10px">
      <c:if test="${ empty viewData.messageList   }">
        등록된 메시지가 없습니다. 
      </c:if>
      <c:if test="${ not viewData.isEmpty() }">
         <table border="1" width="250px">
            <c:forEach items="${ viewData.messageList }" var="message">
               <tr>
                  <td>
                     <div style="position: relative;">
                       메시지ID : <span class="m_id">${message.message_id }</span> <br>
                      손님 이름 : <span class="m_name">${message.guest_name }</span>
                     <img src="./images/${message.guest_name }.gif" alt="" style="height:100%;position:absolute;right:0;top:0;" />
                      <br>
                     메시지 : <span class="m_message">${ message.message }</span><br> 
                     </div>                     
                  </td>
               </tr>
               <tr>
                  <td align="center">
                  <!-- alt 속성에 실제 그 글의 비밀번호 숨겨... -->
   <input type="button" class="btnDelete"
      name="${ message.message_id }" value="[삭제하기]" 
      alt="${ message.password }">
                  <input type="button" class="btnEdit"
                     name="${ message.message_id }" value="[수정하기]" 
                     alt="${ message.password }">
                        
                  <a href="updateMessage.do?messageId=${ message.message_id }">
                  [수정하기]
                  </a>   
                 </td>
               </tr>

            </c:forEach>
         </table>
         <%-- 현재페이지 : ${ viewData.currentPageNumber }<br> --%>
         <div style="text-align: center">
            <c:forEach var="pageNum" begin="1"   end="${ viewData.pageTotalCount }">
               <c:if test="${ pageNum eq viewData.currentPageNumber }">
                  <span style='color: red'>${ pageNum }</span>
               </c:if>
               <c:if test="${ not (pageNum eq viewData.currentPageNumber) }">
                  <a href="list.do?page=${ pageNum }">${ pageNum }</a>
               </c:if>
            </c:forEach>
         </div>
      </c:if>
   </div> 
   
   <!-- Modal -->
   <div class="modal fade" id="myModal" role="dialog">
      <div class="modal-dialog">

         <!-- Modal content-->
         <div class="modal-content">
            <div class="modal-header">
               <button type="button" class="close" data-dismiss="modal">&times;</button>
               <h4 class="modal-title">방명록 삭제</h4>
            </div>
            <form action="deleteMessage.do" method="post">
               <div class="modal-body">               
                  <%-- <input type="hidden" name="messageId" value='${ param.messageId }'> --%>
<input type="hidden" name="message_id" value='${ param.message_id }'>
                  메시지를 삭제하시려면 암호를 입력하세요 : <br> 
                  암호 : <input type="password" id="password" name='password'><br>
               </div>
               <div class="modal-footer">
                  <!-- <input type="submit" class="btn btn-default" value="메시지 삭제하기" alt=""> -->
                  <input type="button" id="btnDeleteGo" class="btn btn-default" value="메시지 삭제하기" alt="" style="font-size:10px">
                  <input type="button" class="btn btn-default" data-dismiss="modal" value="닫기" style="font-size:10px">
               </div>
            </form>
         </div>
      </div>
   </div>

   <!-- list.jsp -->
   <script>
     var error = <%= request.getParameter("error") %>;
     if( error != null && error == 0){
        alert("비밀번호가 잘못되어서 수정 못했습니다. ");
     }
   </script>
</body>
</html>

9. update.jsp

<%@ page 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>Insert title here</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="">
<style>
</style>
<script>
   $(document).ready(function (){     
   });
</script>
</head>
<body>
   <form action="/jspPro/days09/guestbook/updateMessage.do" method="post">
      <div
         style="text-align: center; border: solid 1px gray; width: 270px; margin: 0 auto; padding: 10px">
         <h3>
            방명록 수정<span class="glyphicon glyphicon-heart-empty"></span>
         </h3>
         ID : <input type="text" name="message_id" value="${ message.message_id}"
            disabled="disabled" class="in"><br> <!-- disabled 속성을 주면 데이터 못넘긴다 (submit될 때 제출 안함) -->
         이름 : <input type="text" name="guest_name" value="${ message.guest_name }" class="in"><br>
         암호 : <input type="text" name="password" class="in"><br>
         메시지 :<br>
         <textarea rows="3" style="width: 75%" name="message" class="in">${ message.message }</textarea>
         <br> <input type="submit" value="메시지 수정하기">
      </div> 

      <input type="hidden" name="h_id" value="${ message.message_id }" /><br>
      <input type="hidden" name="h_guestName" value="${ message.guest_name }" /><br>
      <!-- hidden 태그는 왜 남겼을까 ? 얘네 값까지 가지고 넘어가야 하니까 
       원래이름, 원래 id를 숨겨서 가져가는구나 생각 -->
   </form>
</body>
</html>
반응형
Comments