이번 글에서는 JSP가 컨트롤러, 뷰 역할을 모두 처리하는 모델1 방식으로 방명록 애플리케이션을 만들어 보도록 하겠습니다.



1. 방명록 개요

방명록 애플리케이션 페이지 구성은 간단합니다.

  • 방명록을 작성하는 입력 form 페이지
  • 게시글 리스트 페이지
  • 삭제하기 위해 비밀번호를 입력하는 페이지


애플리케이션 디렉토리 구조와 guestbook 테이블은 다음과 같습니다.



참고로 DAO 코드가 try - catch - finally 때문에 길어져서, 스트림을 닫는 finally 코드를 제거했습니다.




2. GuestBookVO 클래스 생성

먼저 VO 클래스를 만들겠습니다.


GuestBookVo.java

package guestbook;

public class GuestBookVO {
private Integer no; // PK
private String name; // 이름
private String pwd; // 비밀번호
private String content; // 내용
private String regDate; // 등록일

public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRegDate() {
return regDate;
}
public void setRegDate(String regDate) {
this.regDate = regDate;
}
}





3. GuestBookDAO 클래스 생성

다음으로 DAO 클래스를 만들겠습니다.


GuestBookDAO.java

package guestbook;

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


public class GuestBookDAO {
private Connection getConnection() throws SQLException {
Connection conn = null;

try {
Class.forName("com.mysql.jdbc.Driver");

String url = "jdbc:mysql://localhost/test";
conn = DriverManager.getConnection(url, "test", "test");
}
catch (ClassNotFoundException e) {
System.out.println(" 드라이버 로딩 실패 ");
}

return conn;
}

// 방명록 게시글 리스트를 조회하는 메서드
public List<GuestBookVO> getList(){
List<GuestBookVO> list = new ArrayList<GuestBookVO>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;

try {
conn = getConnection();

String sql = "SELECT no, name, pwd, content, reg_date" +
" FROM guestbook" +
" ORDER BY no desc";

pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();

while(rs.next()) {
GuestBookVO vo = new GuestBookVO();
vo.setNo(rs.getInt(1));
vo.setName(rs.getString(2));
vo.setPwd(rs.getString(3));
vo.setContent(rs.getString(4));
vo.setRegDate(rs.getString(5));

list.add(vo);
}
}
catch (SQLException e) {
System.out.println("에러: " + e);
}

return list;
}


// 게시글을 등록하는 메서드
public boolean insert(GuestBookVO vo ) {
boolean result = false;
Connection conn = null;
PreparedStatement pstmt = null;

try {
conn = getConnection();

String sql = "INSERT INTO guestbook VALUES (null, ?, password(?), ?, (SELECT SYSDATE()) )";
pstmt = conn.prepareStatement(sql);

pstmt.setString(1, vo.getName());
pstmt.setString(2, vo.getPwd());
pstmt.setString(3, vo.getContent());
int count = pstmt.executeUpdate();

result = (count == 1);
}
catch (SQLException e) {
e.printStackTrace();
}
return result;
}


// 게시글 삭제를 위해 게시글에 설정된 비밀번호를 조회하는 메서드
public String getPwd(int no) {
String pwd = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;

try {
conn = getConnection();

String sql = "SELECT pwd FROM guestbook WHERE no=?";
pstmt = conn.prepareStatement(sql);

pstmt.setInt(1, no);
rs = pstmt.executeQuery();

// 조회되는 데이터가 1개여도 rs.next() 메서드를 호출해야 한다.
if(rs.next()) {
pwd = rs.getString(1);
}

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

return pwd;
}



// 게시글 삭제를 위해 삭제하려는 사용자가 입력한 비밀번호를 MySQL에서 암호화해서 조회하는 메서드
public String getInputPwd(String pwd) {
String parsePwd = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;

try {
conn = getConnection();

String sql = "SELECT password(?)";
pstmt = conn.prepareStatement(sql);

pstmt.setString(1, pwd);
rs = pstmt.executeQuery();

if(rs.next()) {
parsePwd = rs.getString(1);
}

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

return parsePwd;
}


// 게시글을 삭제하는 메서드
public boolean delete(int no) {
boolean result = false;
Connection conn = null;
PreparedStatement pstmt = null;

try {
conn = getConnection();

String sql = "DELETE FROM guestbook WHERE no=?";
pstmt = conn.prepareStatement(sql);

pstmt.setInt(1, no);
int count = pstmt.executeUpdate();

result = (count == 1);
}
catch (SQLException e) {
e.printStackTrace();
}

return result;
}

}
  • 게시글 조회, 추가, 삭제를 할 수 있는 DAO를 구현했습니다.
    • JDBC가 익숙하지 않다면 여기를 참고해주세요.
  • 게시글을 추가할 때 비밀번호를 암호화해서 저장합니다.
    • 삭제를 할 때 비밀번호를 비교하기 위해 getPwd(), getInputPwd() 메서드가 필요합니다.
    • 암호화는 MySQL의 password() 함수를 사용했는데, 실제로는 단방향 암호화 방식을 사용하는 것이 좋습니다. ( 참고 )
  • 유의사항
    • SQL 문법에 따라 문자열을 표기하겠다고 따옴표 ( ' )를 사용하면 오류가 발생합니다.
    • 유사검색 LIKE 를 사용할 때 SQL내에 직접 %string% 으로 작성하면 오류가 발생합니다.

      • 해결방법으로 미리 "%" + String + "%" format을 만들어 준 후 SQL에 넣어주면 됩니다.




4. JSP 생성 - 메인 페이지 ( view )

다음으로 컨트롤러/뷰 페이지들을 작성하겠습니다.


우선 메인 페이지입니다.

index.jsp


<%@page import="guestbook.GuestBookDAO"%>
<%@page import="guestbook.GuestBookVO"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
List<GuestBookVO> list = new ArrayList<GuestBookVO>();
GuestBookDAO dao = new GuestBookDAO();

list = dao.getList();
%>

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>방명록</title>
</head>
<body>
<form action="/guestbook/add.jsp" method="post">
<table border="1" width="500">
<tr>
<td>이름</td><td><input type="text" name="name"></td>
<td>비밀번호</td><td><input type="password" name="pwd"></td>
</tr>
<tr>
<td colspan=4><textarea name="content" cols=60 rows=5></textarea></td>
</tr>
<tr>
<td colspan=4 align=right><input type="submit" VALUE=" 확인 "></td>
</tr>
</table>
</form>
<br>
<% if(list != null){
for(GuestBookVO vo : list){ %>
<table width="510" border="1">
<tr>
<td><%= vo.getNo() %></td>
<td><%= vo.getName() %></td>
<td><%= vo.getRegDate() %></td>
<td><a href="/guestbook/deleteform.jsp?no=<%= vo.getNo() %>">삭제</a></td>
</tr>
<tr>
<td><%= vo.getContent() %></td>
</tr>
</table>
<br>
<% } %>
<% } %>
</body>
</html>
  • 게시글을 등록하는 form 태그가 있습니다.
  • Java 코드로 게시글 목록들을 출력합니다.
    • Servlet이 없기 때문에, JSP에서 스크립틀릿( <%   %> ) 부분에 Java코드를 작성하여 DB에서 데이터를 가져옵니다.
    • Servlet에서 하는 작업을 JSP에서 했을 뿐입니다.
    • 다만, 모델2에서 Servlet에서는 list라는 객체만 넘겨주면 됐는데, 지금 모델1에서는 list를 얻기 위한 과정들을 모두 나열해야 한다는 점입니다.
  • 삭제 버튼을 누를 때 어떤 게시글을 삭제할 것인가를 알려주기 위해서 URl에 게시글 번호를 넘겨줍니다.





5. JSP 생성 - 게시글 추가 로직 ( controller )

다음으로는 게시글을 추가했을 때 DB에 추가하는 JSP 파일을 구현합니다.


add.jsp

<%@page import="guestbook.GuestBookDAO"%>
<%@page import="guestbook.GuestBookVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
request.setCharacterEncoding("utf-8");

String name = request.getParameter("name");
String pwd = request.getParameter("pwd");
String content = request.getParameter("content");

GuestBookVO vo = new GuestBookVO();

vo.setName(name);
vo.setPwd(pwd);
vo.setContent(content);

GuestBookDAO dao = new GuestBookDAO();
dao.insert(vo);


response.sendRedirect("/guestbook");
%>
  • Servlet에서 했던 작업을 그대로 JSP에서 수행합니다.
  • 보시는 add.jsp 파일은 클라이언트에게 보여줄 목적이 아닌, Java코드를 실행하기 위한 jsp 파일입니다.
  • 이런 식으로 모델1에서는 로직을 처리하기 위해 JSP파일을 만들어야 합니다.





7. JSP 생성 - 게시글 삭제 form ( view )

다음으로는 게시글을 삭제하기 위한 삭제 from을 구현합니다.


deleteform.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String no = request.getParameter("no");
%>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>방명록</title>
</head>
<body>
<form method="post" action="/guestbook/delete.jsp">
<input type='hidden' name="no" value="<%= no %>">
<table>
<tr>
<td>비밀번호</td>
<td><input type="password" name="pwd"></td>
<td><input type="submit" value="확인"></td>
</tr>
</table>
</form>
<a href="/guestbook">메인으로 돌아가기</a>
</body>
</html>
  • index.jsp에서 삭제하려는 게시글의 번호를 URL로 넘겨주었습니다.
  • deleteform.jsp에서도 삭제하려는 게시글의 번호를 넘겨줘야 하는데, 여기서는 intput 태그의 hidden type으로 넘겨주었습니다.
  • index.jsp의 방식은 HTTP GET 방식에 어울리고, deleteform.jsp의 방식은 POST 방식에서 어울리는 게시글 번호 전달 방법입니다.





8. JSP 생성 - 게시글 삭제 로직 ( controller )

마지막으로 게시글 삭제를 처리하는 JSP파일입니다.


delete.jsp

<%@page import="guestbook.GuestBookDAO"%>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("utf-8");
Integer no = Integer.parseInt(request.getParameter("no"));
String Inputpwd = request.getParameter("pwd");

GuestBookDAO vo = new GuestBookDAO();
String dbPwd = vo.getPwd(no);
String parseInputPwd = vo.getInputPwd(Inputpwd);

if( dbPwd.equals(parseInputPwd)){
vo.delete(no);
}
response.sendRedirect("/guestbook");
%>
  • add.jsp와 유사하게 DB에 접근하기 위한 로직을 처리하는 코드입니다.



이제 구현이 끝났으므로 톰캣에 프로젝트 폴더를 등록하고,

URL에 localhost:8080/guestbook/index.jsp 에 요청해서 테스트를 해보세요.





이상으로 방명록 애플리케이션을 구현해보았습니다.

모델1이 어떤 점이 불편한지는 다음 글에서 모델2로 똑같이 방명록 애플리케이션을 만들면서 비교하면서 언급하도록 하겠습니다.


이번 글에서는 애플리케이션을 구현하는데 목적이 있으므로 구현 코드만 나열했습니다.

이전 글들에서 전부 언급했던 내용만으로 구현했으니 이해하는데 크게 어려움이 없을 것 같습니다.