서블릿과 JSP로 메모장을 구현해본다.
🔻개발 순서
1. 주제
나만의 메모장
2. 업무
CRUD 위주의 구현
- 메모 쓰기(C)
- 메모 목록(R)
- 메모 읽기(R)
- 메모 수정(U)
- 메모 삭제(D)
3. 전체 구성(페이지 관계도)
4. DB
파일 경로 | 파일명 | 역할 |
Memo (root폴더) | script.sql | sql문 작성 |
sql문을 sql툴에서 작성하여 자바에서 사용하면 편하다.
5. 기능별 파일 생성하기
공통 파일
파일 경로 | 파일명 | 역할 |
webapp > WEB-INF > views > inc | asset.jsp | 정적 자원들을 관리하고 출력 |
webapp > WEB-INF > views > inc | header.jsp | 웹 페이지의 상단 부분 |
템플릿
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | Template.java | template.jsp |
JDBC
서블릿 | JSP | |
경로 | com.test.memo | webapp > lib |
파일명 | DBUtil.java | ojdb6.jar |
DB 담당자
경로 | 파일명 | 역할 |
com.test.memo.repository | MemoDAO.java | DB 담당 |
com.test.memo.medel | MemoDTO.java | DB 객체 담당 |
DB 담당자를 저장하는 패키지로 com.test.persist, com.test.repository, com.test.dao 으로 많이 사용한다.
메모 쓰기
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | Add.java | add.jsp |
처리 파일 명(안보이는 페이지) | AddOk.java | addok.jsp |
메모 목록
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | List.java | list.jsp |
메모 읽기
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | View.java | view.jsp |
메모 수정
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | Edit.java | edit.jsp |
처리 파일명(안보이는 페이지) | EditOk.java | editok.jsp |
메모 삭제
서블릿 | JSP | |
경로 | com.test.memo | webapp > WEB-INF > views |
파일명(보이는 페이지) | Del.java | del.jsp |
처리 파일명(안보이는 페이지) | DelOk.java | delok.jsp |
라이브러리
경로 | 파일명 | 역할 |
webapp > WEB-INF > lib | ||
ojdbc6.jar | JDBC | |
jstl-1.2.jar | JSTL | |
cos.jar | 파일 입출력 | |
lombok.jar | 코드 생성 | |
json-simple-1.1.1.jar | JSON |
6. 구현
🔻구현하기
@WebServlet 어노테이션 이용
Template.java 에서 가상주소를 만들수 있다.
@WebServlet("/template.do")
Template
코드를 작성하다보면 반복되는 소스코드들이 존재한다.
이럴 때 코드 조각으로 만들어두면 코드 작성 시간이 줄어들고 효율성이 높아진다.
Template.java
package com.test.memo;
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("/template.do")
public class Template extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/template.jsp");
dispatcher.forward(req, resp);
}
}
Template.java 코드 조각만들기
1. Window - Preferences
2. Java - Templates
'New' 클릭
3. New Template
Name, Context, Desription, Pattern의 내용을 채워 넣는다.
클래스 이름 에 'insert Variable' 버튼을 통해 'primary_type_name'를 넣으면 코드조각을 사용했을 때, 현재 파일 이름이 자동으로 들어간다.
같은 name 변수로 지정하면 한 쪽 커서가 잡혀있고 입력을 할 때 다른 한 쪽도 같이 입력된다.
코드 조작 사용 모습
템플릿 등록할 때 지정한 name을 일부 입력하고 'ctrl + space bar'를 누르면 앞서 입력한 템플릿이 뜬다.
가상주소를 입력하고 ctrl + shfit + O 를 import 해주면 공통된 코드를 쉽고 빠르게 작성할 수 있다.
template.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
목록보기, 메모쓰기, 수정, 삭제 등...
</body>
</html>
jsp 파일도 템플릿을 만들어 공통된 코드 작업을 간편하게 만들어준다.
jsp 파일에는 코드 조각 기능이 없어 코드 복붙으로 사용한다.
공통 파일
asset.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
asset.jsp의 역할은 "asset"은 보통 자산을 의미하는데, 이 파일은 CSS, JavaScript, 이미지 등 웹 페이지에서 사용하는 정적 자원들을 관리하고 출력하는 역할을 한다. 각 페이지에서 공통으로 사용되는 스타일 시트나 JavaScript 파일들을 이 파일에서 로드하고, 페이지에 포함한다.
header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<title>Memo</title>
<h1>Memo <small>JSP Model 2</small></h1>
header.jsp는 웹 페이지의 상단에 표시되는 요소들을 관리하고 출력하는 역할을 한다.
예를 들어, 웹 페이지의 로고, 타이틀, 메뉴 등의 요소를 이 파일에서 관리를 한다.
DB 연결
DBUtil.java
package com.test.memo;
import java.sql.Connection;
import java.sql.DriverManager;
public class DBUtil {
private static Connection conn;
public static Connection open() {
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String id = "hr";
String pw = "java1234";
try {
//JDBC 드라이버를 로딩한다.(관련 클래스 정보를 확인한다)
Class.forName("oracle.jdbc.driver.OracleDriver");
//Connection 객체 생성과 동시에 오라클 접속
conn = DriverManager.getConnection(url, id, pw);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//오버로딩
public static Connection open(String server, String id, String pw) {
String url = "jdbc:oracle:thin:@"+ server +":1521:xe";
try {
//JDBC 드라이버를 로딩한다.(관련 클래스 정보를 확인한다)
Class.forName("oracle.jdbc.driver.OracleDriver");
//Connection 객체 생성과 동시에 오라클 접속
conn = DriverManager.getConnection(url, id, pw);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
DBUtil.java는 다른 파일에서도 동일하게 사용한다.
사용시 주의점은 접속하는 DB에 따라 서버, id, pw를 잘 확인하여 hr계정이 아닌 경우 오버로딩 된 메소드를 활용한다.
DB 담당
MemoDTO.java
package com.test.memo.model;
public class MemoDTO {
private String seq;
private String name;
private String pw;
private String memo;
private String regdate;
public String getSeq() {
return seq;
}
public void setSeq(String seq) {
this.seq = seq;
}
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}
MemoDTO는 tblMemo의 레코드 한 줄과 구성이 똑같다.
MemoDAO.java
package com.test.memo.repository;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import com.test.memo.DBUtil;
import com.test.memo.model.MemoDTO;
public class MemoDAO {
//코드 분산이 핵심!
private Connection conn;
private Statement stat;
private PreparedStatement pstat;
private ResultSet rs;
public MemoDAO() {
//만들어질 때 Conncetion 작업을 함녀 add를 호출했을 때 이미 연결된 상태가 된다.
this.conn = DBUtil.open();
}
public int add(MemoDTO dto) {
//name, pw, memo 를 DB에 insert하기
try {
String sql = "insert into tblMemo(seq, name, pw, memo, regdate) values (seqMemo.nextVal, ?, ?, ?, default)";
pstat = conn.prepareStatement(sql);
//dto 상자를 열어 사용한다.
pstat.setString(1, dto.getName());
pstat.setString(2, dto.getPw());
pstat.setString(3, dto.getMemo());
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//catch 절을 통해 내려왔으므로 0을 반환한다.
return 0;
}
public ArrayList<MemoDTO> list() {
//결과테이블을 돌려줘야하는데 ResultSet이 아닌 JDBC가 쓸 수 있는 자료형으로 돌려줄 수 있을까 > ArrayList로 옮겨 담아 반환한다.
ArrayList<MemoDTO> list = new ArrayList<MemoDTO>();
try {
String sql = "select * from tblMemo order by seq desc";
stat = conn.createStatement();
//결과목록
rs = stat.executeQuery(sql);
//rs를 list로 옮겨담는 작업
while (rs.next()) {
//루프돌때마다 레코드 1줄을 MemoDTO 1개씩 맵핑
MemoDTO dto = new MemoDTO();
dto.setSeq(rs.getString("seq"));
dto.setName(rs.getString("name"));
dto.setPw(rs.getString("pw"));
dto.setMemo(rs.getString("memo"));
dto.setRegdate(rs.getString("regdate"));
list.add(dto);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public MemoDTO get(String seq) {
try {
String sql = "select * from tblMemo where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, seq);
rs = pstat.executeQuery();
MemoDTO dto = new MemoDTO();
if (rs.next()) {
dto.setSeq(rs.getString("seq"));
dto.setName(rs.getString("name"));
dto.setPw(rs.getString("pw"));
dto.setMemo(rs.getString("memo"));
dto.setRegdate(rs.getString("regdate"));
return dto;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public int edit(MemoDTO dto) {
try {
String sql = "update tblMemo set name=?, memo = ? where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, dto.getName());
pstat.setString(2, dto.getMemo());
pstat.setString(3, dto.getSeq());
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public boolean check(MemoDTO dto) {
try {
String sql = "select count(*) as cnt from tblMemo where seq = ? and pw = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, dto.getSeq());
pstat.setString(2, dto.getPw());
rs = pstat.executeQuery();
if (rs.next()) {
return rs.getInt("cnt") == 1 ? true : false;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public int del(String seq) {
try {
String sql = "delete from tblMemo where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, seq);
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
메모 목록
List.java
package com.test.memo;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/list.do")
public class List extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 + 2
MemoDAO dao = new MemoDAO();
ArrayList<MemoDTO> list = dao.list();
//내용 자르기
for (MemoDTO dto : list) {
if (dto.getMemo().length() > 12) {
dto.setMemo(dto.getMemo().substring(0, 12) + "...");
}
}
//3.
req.setAttribute("list", list);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/list.jsp");
dispatcher.forward(req, resp);
}
}
List.java 에서는 메모 목록을 읽어오기 위해 총 3가지 작업을 처리한다.
1. DB에서 select 해오는 것을 DAO 위임
2. 결과테이블 반환받기
3. list.jsp 호출하여 결과테이블 전달(list)
1번 DB 작업은 위임받는 클래스를 따로 만들어 작업한다 > DB 담당자: MemoDAO.java
문제가 발생했을 때, DAO파일만 확인할 수 있게 담당을 확실하게 구분짓고 절대 서블릿에서 DB작업을 하지 않는다.
하나의 계층이(Controller, Servlet) 또 다른 계층에게(MemoDAO) 데이터를 전송할 때 포장해서 넘긴다.
포장하는 것은 컬렉션(HashMap) 또는 객체로 만든다는 것으로 DTO 클래스를 생성하여 사용한다.
전송하는 데이터가 2개 이상이면 DTO에 담아서 전송한다.
2번 작업에서 ResultSet 는 DB작업의 객체이므로 resultset으로 반환받으면 안된다. 그래서 DAO에서 반환을 받을 때 ArrayList로 변환해서 반환받는다.
또한, 내용이 길면 글자가 메모 박스를 넘어서 보기 좋지 않다. jsp에서는 뷰역할만 하기때문에 특정 글자수를 넘어가는 글자를 자르게 보이는 작업은 서블릿에서 한다.
UserDAO - list()
public ArrayList<MemoDTO> list() {
ArrayList<MemoDTO> list = new ArrayList<MemoDTO>();
try {
String sql = "select * from tblMemo order by seq desc";
stat = conn.createStatement();
//결과목록
rs = stat.executeQuery(sql);
//rs를 list로 옮겨담는 작업
while (rs.next()) {
//루프돌때마다 레코드 1줄을 MemoDTO 1개씩 맵핑
MemoDTO dto = new MemoDTO();
dto.setSeq(rs.getString("seq"));
dto.setName(rs.getString("name"));
dto.setPw(rs.getString("pw"));
dto.setMemo(rs.getString("memo"));
dto.setRegdate(rs.getString("regdate"));
list.add(dto);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
결과테이블을 ResultSet이 아닌 JDBC가 쓸 수 있는 자료형인 ArrayList로 옮겨 담아 반환한다.
rs가 존재할 때까지 반복문을 돌아 dto를 생성하고 ArrayList 에 담는다. dto가 담긴 ArrayList를 반환한다.
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"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
<style>
#list {
display: grid;
grid-template-columns : repeat(3, 1fr);
gap: 10px;
}
#list .item {
border: 1px solid #ccc;
cursor : pointer;
}
#list .item div {
padding: 5px;
}
#list .item div:nth-child(2) {
height: 50px;
}
#list .item div:nth-child(odd) {
background-color : #eee;
}
</style>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<div id="list">
<c:forEach items="${list}" var="dto">
<div class="item" onclick = "location.href='/memo/view.do?seq=${dto.seq}';">
<div>${dto.seq}. ${dto.name}</div>
<div>${dto.memo}</div>
<div>${dto.regdate}</div>
</div>
</c:forEach>
</div>
<div>
<input type="button" value="쓰기" onclick="location.href='/memo/add.do';">
</div>
</body>
</html>
List.java에서 받은 list 속성으로 메모 목록을 화면에 출력한다.
메모 작성
Add.java
package com.test.memo;
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("/add.do")
public class Add extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/add.jsp");
dispatcher.forward(req, resp);
}
}
Add.java 에서 정보 전달을 할 작업이 없어서 add.jsp만 호출한다.
add.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
<style>
</style>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<form method="POST" action="/memo/addok.do">
<table class="vertical">
<tr>
<th>이름</th>
<td><input type="text" name="name" required></td>
</tr>
<tr>
<th>암호</th>
<td><input type="password" name="pw" required></td>
</tr>
<tr>
<th>메모</th>
<td><textarea name="memo" required class="full"></textarea></td>
</tr>
</table>
<div>
<input type="button" value="돌아가기" onclick="location.href='/memo/list.do';">
<input type="submit" value="쓰기">
</div>
</form>
</body>
</html>
가상주소의 /는 html을 의미하나 javascript에서는 root context가 있어야 한다.
새로 작성한 메모의 값을 AddOk.java로 전송한다.
AddOk.java
package com.test.memo;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/addok.do")
public class AddOk extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String pw = req.getParameter("pw");
String memo = req.getParameter("memo");
//2.
MemoDAO dao = new MemoDAO();
MemoDTO dto = new MemoDTO();
dto.setName(name);
dto.setPw(pw);
dto.setMemo(memo);
int result = dao.add(dto);
//3.
req.setAttribute("result", result);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/addok.jsp");
dispatcher.forward(req, resp);
}
}
AddOk.java 에서는 새로 작성된 메모의 값을 받아 DB처리와 JSP 호출하는 3가지 작업을 처리한다.
1. 데이터 수신(name, pw, memo)
2. DB 작업(insert) 위임
3. JSP 호출하여 피드백
3번 작업에서는 addok.jsp로 DB에 insert한 결과를 넘긴다.
UserDAO - add(dto)
public int add(MemoDTO dto) {
try {
String sql = "insert into tblMemo(seq, name, pw, memo, regdate) values (seqMemo.nextVal, ?, ?, ?, default)";
pstat = conn.prepareStatement(sql);
//dto 상자를 열어 사용한다.
pstat.setString(1, dto.getName());
pstat.setString(2, dto.getPw());
pstat.setString(3, dto.getMemo());
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//catch 절을 통해 내려왔으므로 0을 반환한다.
return 0;
}
insert에 성공하면 1, 실패하면 0을 반환한다.
addok.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">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<script>
<c:if test ="${result == 1}">
location.href = '/memo/list.do';
</c:if>
<c:if test ="${result != 1}">
alert('실패');
history.back();
</c:if>
</script>
</body>
</html>
addok.jsp에서는 AddOk.java에서 넘어온 result를 기준으로 insert에 성공하여 1일 때는 list 페이지로 보여주고 실패했을 때는 안내메시지를 띄우고 다시 이전 페이지로 돌아간다.
이전 페이지로 돌아가는 방법은 2가지가 있다.
1. location.href = '/이전페이지 주소'
location.href 는 페이지를 새로 요청하여 컨트롤의 입력값들이 리셋된다.
2. history.back()
이 방법은 작성했던 페이지 그대로 되돌아가서 컨트롤의 입력값들도 복구된다.
보안이 높은 페이지는 location을 사용하고 그게 아니라면 사용자의 편의를 위해 주로 history.back()를 사용한다.
메모 읽기
View.java
package com.test.memo;
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;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/view.do")
public class View extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
String seq = req.getParameter("seq");
//2.
MemoDAO dao = new MemoDAO();
//하나의 데이터를 넘기기때문에 DTO에 담지않고 전송한다.
MemoDTO dto = dao.get(seq);
//메모의 엔터처리
dto.setMemo(dto.getMemo().replace("\r\n", "<br>"));
//3.
req.setAttribute("dto", dto);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/view.jsp");
dispatcher.forward(req, resp);
}
}
View.java에서는 선택한 메모의 상세페이지를 보여주기 위한 DB 작업을 한다.
list.jsp에서 list에 onclick 이벤트로 전송한 seq 번호를 DB 작업을 통해 메모를 view.jsp에 전송한다.
1. 데이터 수신(seq)
2. DB에서 select 해오는 것을 DAO 위임
3. 결과테이블 반환받기
4. view.jsp 호출하여 결과테이블 dto 전달
view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<table class="vertical">
<tr>
<th>번호</th>
<td>${dto.seq}</td>
</tr>
<tr>
<th>이름</th>
<td>${dto.name}</td>
</tr>
<tr>
<th>메모</th>
<td>${dto.memo}</td>
</tr>
<tr>
<th>날짜</th>
<td>${dto.regdate}</td>
</tr>
</table>
<div>
<input type="button" value="돌아가기" onclick="location.href='/memo/list.do';">
<input type="button" value="수정하기" onclick="location.href='/memo/edit.do?seq=${dto.seq}';">
<input type="button" value="삭제하기" onclick="location.href='/memo/del.do?seq=${dto.seq}';">
</div>
</body>
</html>
View.java에서 수신한 dto로 화면에 출력한다.
메모 상세화면에서 '돌아가기'를 누르면 메모 리스트 페이지, '수정하기'를 누르면 메모 수정 페이지, '삭제하기'를 누르면 삭제 페이지로 이동한다.
수정과 삭제는 추후 DB 작업을 할 수 있도록 seq 데이터를 함께 전송한다.
메모 수정
Edit.java
package com.test.memo;
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;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/edit.do")
public class Edit extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
String seq = req.getParameter("seq");
//2.
MemoDAO dao = new MemoDAO();
MemoDTO dto = dao.get(seq);
//3.
req.setAttribute("dto", dto);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/edit.jsp");
dispatcher.forward(req, resp);
}
}
Edit.java에서는 메모를 수정하기 위해 수정 버튼을 눌렀을 때 입력 컨트롤에 이전 값들이 들어갈 수 있도록 DB 작업을 한다.
1. 데이터 수신(seq)
2. DB에서 select 해오는 것을 DAO 위임
3. 결과테이블 반환받기
4. edit.jsp 호출하여 결과테이블 dto 전달
MemoDAO.java - get(seq)
public MemoDTO get(String seq) {
try {
String sql = "select * from tblMemo where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, seq);
rs = pstat.executeQuery();
MemoDTO dto = new MemoDTO();
if (rs.next()) {
dto.setSeq(rs.getString("seq"));
dto.setName(rs.getString("name"));
dto.setPw(rs.getString("pw"));
dto.setMemo(rs.getString("memo"));
dto.setRegdate(rs.getString("regdate"));
return dto;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
edit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<form method="POST" action="/memo/editok.do">
<table class="vertical">
<tr>
<th>이름</th>
<td><input type="text" name="name" required value=${dto.name}></td>
</tr>
<tr>
<th>암호</th>
<td><input type="password" name="pw" required></td>
</tr>
<tr>
<th>메모</th>
<!-- textarea 사이에 공백을 무조건 인식하기때문에 화면에서도 인식된다. -->
<td><textarea name="memo" required class="full">${dto.memo}</textarea></td>
</tr>
</table>
<div>
<input type="button" value="돌아가기" onclick="location.href='/memo/list.do';">
<input type="submit" value="수정하기">
</div>
<input type="hidden" name="seq" value="${dto.seq}">
</form>
</body>
</html>
수정 페이지는 입력 페이지와 유사하다.
여러개의 정보 전달할 때는 POST 방식으로 전달하는데 글 번호로 같이 전송해야하므로 히든태그로 사용자에게 보이지않도록 넘긴다.
EditOk.java
package com.test.memo;
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;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/editok.do")
public class EditOk extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
//수신된 데이터 인코딩
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String memo = req.getParameter("memo");
String seq = req.getParameter("seq");
String pw = req.getParameter("pw"); //글 수정, 삭제 권한 확인용
//2.
MemoDAO dao = new MemoDAO();
MemoDTO dto = new MemoDTO();
dto.setName(name);
dto.setMemo(memo);
dto.setSeq(seq);
dto.setPw(pw);
//글 번호에 대한 pw가 맞는지 검사
boolean flag = dao.check(dto);
int result = 0;//성공(1), 실패(0), 암호틀림(2)
if (flag) {
result = dao.edit(dto);
} else {
result = 2;
}
//3.
req.setAttribute("result", result);
req.setAttribute("seq", seq);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/editok.jsp");
dispatcher.forward(req, resp);
}
}
EditOk.java 에서는 edit.jsp 입력한 메모 정보를 수신 받아 DB에 update하고 editok.jsp를 통해 피드백을 한다.
1. 데이터 수신(name, memo, seq, pw)
2. DB에서 update 하는 것을 DAO 위임
3. 결과값 result 를 반환받아 editok.jsp로 전달하여 피드백
2번 작업에서 추가로 내가 수정한 메모가 이전 등록했을 때의 메모 pw가 동일한지 MemoDAO를 통해 확인 작업을 한다.
동일하다면 성공의 값으로 1을 반환하여 MemDAO의 edit()를 실행하고 실패했을 땐 0, 암호가 달랐다면 2를 반환한다.
수정 완료 화면을 보여줄 editok.jsp에게 result 결과와 글 번호를 전송한다.
MemoDAO - check(dto)
public boolean check(MemoDTO dto) {
try {
String sql = "select count(*) as cnt from tblMemo where seq = ? and pw = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, dto.getSeq());
pstat.setString(2, dto.getPw());
rs = pstat.executeQuery();
if (rs.next()) {
return rs.getInt("cnt") == 1 ? true : false;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
seq와 pw값을 입력받아 결과로 1가 나왔을 때 true를 반환한다. 현재 수정한 글이 이전 작성 때 쓴 pw가 다르면 글을 수정할 수 없다.
UserDAO - edit(dto)
public int edit(MemoDTO dto) {
try {
String sql = "update tblMemo set name=?, memo = ? where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, dto.getName());
pstat.setString(2, dto.getMemo());
pstat.setString(3, dto.getSeq());
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
위에서 제약사항을 검토하고 성공했을 때 해당 edit 메소드가 작동한다.
editok.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">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<script>
<c:if test ="${result == 1}">
location.href = '/memo/view.do?seq=${seq}';
</c:if>
<c:if test ="${result == 0}">
alert('실패');
history.back();
</c:if>
<c:if test ="${result == 2}">
alert('암호틀림');
history.back();
</c:if>
</script>
</body>
</html>
EditOk.java로 부터 수신받은 result를 이용하여 성공하면 수정 완료된 메모 상세 페이지로 이동하고, 실패 또는 암호가 틀렸을 때는 입력값이 그대로 있는 history.back()으로 이동한다.
메모 삭제
Del.java
package com.test.memo;
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("/del.do")
public class Del extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
String seq = req.getParameter("seq");
//2.
req.setAttribute("seq", seq);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/del.jsp");
dispatcher.forward(req, resp);
}
}
Del.java에서는 총 2가지 업무를 처리한다.
1. 데이터 수신(seq)
2. JSP 호출하기
메모 상세 페이지에서 삭제를 눌렀을 때, seq 번호가 넘어오기로 하였다.
그 번호를 수신받아 view.jsp에게 넘긴다.
del.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
<link rel="stylesheet" href="http://pinnpublic.dothome.co.kr/cdn/example-min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://pinnpublic.dothome.co.kr/cdn/example-min.js"></script>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<form method="POST" action="/memo/editok.do">
<table class="vertical">
<tr>
<th>암호</th>
<td><input type="password" name="pw" required></td>
</tr>
</table>
<div>
<input type="button" value="돌아가기" onclick="location.href='/memo/list.do';">
<input type="submit" value="삭제하기">
</div>
<!-- Del.java에서 setAttribute로 넘긴 변수명 잘 확인하기 -->
<input type="hidden" name="seq" value="${seq}">
</form>
</body>
</html>
DelOk.java
package com.test.memo;
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;
import com.test.memo.model.MemoDTO;
import com.test.memo.repository.MemoDAO;
@WebServlet("/delok.do")
public class DelOk extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
String seq = req.getParameter("seq");
String pw = req.getParameter("pw");
//2.
MemoDAO dao = new MemoDAO();
MemoDTO dto = new MemoDTO();
dto.setSeq(seq);
dto.setPw(pw);;
int result = 0;
if (dao.check(dto)) {
result = dao.del(seq);
} else {
result =2;
}
//3.
req.setAttribute("result", result);
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/delok.jsp");
dispatcher.forward(req, resp);
}
}
DelOk.java에서는 메모를 삭제하는 DB 작업을 한다.
1. 데이터 수신(seq, pw)
2. 암호확인하여 권환있는지 확인한다.
3. DB에 delete작업을 DAO에게 위임
4. 피드백을 위해 JSP 호출하기
2번 작업은 삭제하려는 메모의 비밀번호를 다시 입력받아 권한있는 사용자 인지 확인한다.
권한있는 (일치하는) 회원이 있다면 MemoDAO의 del 메소드를 호출하여 결과값을 받는다.
MemoDAO - del(seq)
public int del(String seq) {
try {
String sql = "delete from tblMemo where seq = ?";
pstat = conn.prepareStatement(sql);
pstat.setString(1, seq);
return pstat.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
seq를 매개변수로 받아 삭제된 행의 갯수를 반환받는다.
delok.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">
<%@ include file = "/WEB-INF/views/inc/asset.jsp" %>
</head>
<body>
<%@ include file = "/WEB-INF/views/inc/header.jsp" %>
<script>
<c:if test ="${result == 1}">
location.href = '/memo/list.do';
</c:if>
<c:if test ="${result == 0}">
alert('실패');
history.back();
</c:if>
<c:if test ="${result == 2}">
alert('암호틀림');
history.back();
</c:if>
</script>
</body>
</html>
수정했을 때와 마찬가지로 삭제 결과가 1이면 성공, 그 외 숫자이면 실패 또는 암호가 틀린 것이다.
'SERVER(Servlet, JSP)' 카테고리의 다른 글
[Server] MVC 디자인 패턴 기초 (1) | 2023.10.23 |
---|---|
[JDBC] DB 연결할 때 자주 발생하는 오류 및 해결 방법 (0) | 2023.10.22 |
[JSP] 페이지 이동과 내장 객체 저장소의 생명주기(★) (0) | 2023.10.19 |
[JSP] 이미지 뷰어(업로드 및 삭제처리) (0) | 2023.10.18 |
[JSP] cos library를 활용한 파일 업로드/다운로드 (0) | 2023.10.18 |