이전 포스팅에서 Mybatis 개념 및 설정 방법에 대해서 알아봤습니다.
오늘은 Mybatis를 응용해서 한 줄 메모장을 만들어서 메모 목록, 메모 보기, 수정, 삭제 기능을 넣어보겠습니다.
■ 한 줄 메모장
한 줄 메모장 구현은 MVC 패턴으로 진행됩니다.
MVC 패턴에 대해서 궁금하신 분들은 아래 포스팅을 참고해 보세요!
각 기능의 역할은 다음과 같습니다.
Contoller
MemoController.java
Model
MemoDTO.java
MemoDAO.java
View
memo.jsp : ajax 요청 페이지, 메모입력
memo_list.jsp : 메모목록
memo_view.jsp : 메모 보기, 수정, 삭제 기능
그럼 먼저 데이터를 담아두기 위한 MEMO Table을 만들어봅시다.
■ SQL
<SQL>
create table memo(
idx number not null primary key,
writer varchar2(50) not null,
memo varchar2(300) not null,
post_date date default sysdate
);
테이블 생성 후 insert문으로 간단한 데이터를 넣어줍니다.
<insert>
insert into memo (idx, writer, memo) values(1,'kim','첫번째 메모');
insert into memo (idx, writer, memo) values(2,'김철수','두번째 메모');
insert into memo(idx, writer, memo)
values ((select nvl(max(idx)+1,1) from memo),'lee','세번째 메모');
세 번째 데이터는 idx 값을 직접 넣어주지 않고 nvl 함수를 활용해서 max(idx)+1 값을 넣어주었습니다.
그럼 만약에 칼럼값이 null이라면 치환 값인 1이 나와야겠죠?
SQL 함수를 공부하고 계신 분들이라면 아래 포스팅을 참고하시면 도움이 되실 거예요!
Table 생성을 하고 데이터를 추가했습니다. 그럼 이제 View단으로 넘어가서 ajax 요청 페이지 및 메모 입력 페이지, DTO 페이지를 만들어 봅시다.
■ MemoDTO.java
DTO에서는 getter/setter , 생성자, toString 정도만 생성해 줍니다.
package memo.dto;
public class MemoDTO {
private int idx;
private String writer;
private String memo;
private String post_date;
//getter,setter, 생성자, toString
public int getIdx() {
return idx;
}
public void setIdx(int idx) {
this.idx = idx;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public String getPost_date() {
return post_date;
}
public void setPost_date(String post_date) {
this.post_date = post_date;
}
public MemoDTO() {
}
public MemoDTO(int idx, String writer, String memo, String post_date) {
this.idx = idx;
this.writer = writer;
this.memo = memo;
this.post_date = post_date;
}
@Override
public String toString() {
return "MemoDTO [idx=" + idx + ", writer=" + writer + ", memo=" + memo + ", post_date=" + post_date + "]";
}
}
■ memo.jsp
한 줄 메모장에서는 이름과 메모를 작성하고 확인 버튼을 클릭 시 저장하는 코드를 구현합니다.
그러면 <input> 태그와 <input> 태그 타입을 button 타입으로 설정해서 값을 보냅니다.
이 작업에서 요청을 보내는 과정을 자바 스크립트에서 구현합니다.
이름 : <input id="writer" size="10"> <br>
메모 : <input id="memo" size="40">
<input type="button" id="btnSave" value="확인">
결과 테이블을 표출하기 위해 id 값을 설정합니다.
<div id="result"></div>
값을 전송하기 위해 자바 스크립트 코드를 ajax 방식으로 구현합니다.
여기서 사용되는 이벤트는 click()입니다.
<script type="text/javascript">
$(function() {
list();
$('#btnSave').click(function() {
insert();
});
그럼 이제 list()를 생성해서 ajax 방식으로 Controller에 전달할 수 있게 코드를 만듭니다.
function list() {
//검색옵션과 검색할 키워드를 전달
var param="searchkey="+$("#searchkey").val()+"&search="+$("#search").val();
$.ajax({
type: "post",
url: "${path}/memo_servlet/list.do",
data:param,
success: function(result) {
$("#result").html(result);
}
});
}
url에서 전송 방식은 memo_servlet /list.do로 지정했습니다.
여기서 list.do는
페이지를 별도로 생성하는 게 아니라 값을 전달하고 받을 수 있는 연결 다리로
생각하시면 좋을 거 같습니다.
여기까지만 작업하면 너무 간단하는 생각에 기능을 좀 더 추가해서 검색 옵션을 추가해 봅시다.
<select id="searchkey">
<option value="writer">이름</option>
<option value="memo">메모</option>
<option value="writer_memo">이름+메모</option>
</select>
<input id="search" value="${search}">
<input type="button" id="btnSearch" value="조회">
id ="search"의 value 값을 EL 식을 추가했습니다.
그 이유는 검색 옵션과 검색할 키워드를 전달하기 위해서입니다.
//검색옵션과 검색할 키워드를 전달
var param="searchkey="+$("#searchkey").val()+"&search="+$("#search").val();
검색 옵션에서는 이 한 줄이 정말 큰 역할을 해줍니다.
다음으로 들어오는 데이터를 보내주는 코드를 구현합니다.
function insert() {
var writer =$("#writer").val();
var memo =$("#memo").val();
var param ="writer="+writer+"&memo"+memo;
$.ajax({
type:"post",
url:"${path}/memo_servlet/insert.do",
data:param,
success: function() { //call back 함수
list();
$("#writer").val("");
$("#memo").val("");
}
});
}
여기까지 memo.jsp 페이지 작업을 끝냈습니다.
<memo.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="../include/jquery-3.6.3.min.js"></script>
<%@ include file="../include/header.jsp" %>
<script type="text/javascript">
$(function() {
list();
$('#btnSave').click(function() {
insert();
});
//검색버튼 클릭
$("#btnSearch").click(function() {
list();
});
});
function list() {
//검색옵션과 검색할 키워드를 전달
var param="searchkey="+$("#searchkey").val()+"&search="+$("#search").val();
$.ajax({
type: "post",
url: "${path}/memo_servlet/list.do",
data:param,
success: function(result) {
$("#result").html(result);
}
});
}
function insert() {
var writer =$("#writer").val();
var memo =$("#memo").val();
var param ="writer="+writer+"&memo"+memo;
$.ajax({
type:"post",
url:"${path}/memo_servlet/insert.do",
data:param,
success: function() { //call back 함수
list();
$("#writer").val("");
$("#memo").val("");
}
});
}
</script>
</head>
<body>
<h2>한줄메모장</h2>
이름 : <input id="writer" size="10"> <br>
메모 : <input id="memo" size="40">
<input type="button" id="btnSave" value="확인">
<br>
<select id="searchkey">
<option value="writer">이름</option>
<option value="memo">메모</option>
<option value="writer_memo">이름+메모</option>
</select>
<input id="search" value="${search}">
<input type="button" id="btnSearch" value="조회">
<div id="result"></div>
</body>
</html>
다음으로 Controller를 만들어 봅시다.
Servlet -> doGet에서 작업합니다.
■ MemoController.java
memo.jsp 에서 전송방식을 list.do , insert.do 값을 넣어주었습니다.
사람마다 코드 구현은 다르지만 저는 이 값을 if문으로 처리합니다.
<MemoController.java>
@WebServlet("/memo_servlet/*") //공통 uri
public class MemoController extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//사용자가 요청한 주소
String uri=request.getRequestURI();
MemoDAO dao=new MemoDAO();
if(uri.indexOf("list.do") != -1) { //세부 uri
//검색옵션 + 검색키워드
String searchkey = request.getParameter("searchkey");
String search= request.getParameter("search");
List<MemoDTO> list=dao.listMemo(searchkey,search);//메모 목록 리턴
//request 영역에 저장
request.setAttribute("list", list);
//포워딩
String page="/memo/memo_list.jsp";
RequestDispatcher rd=request.getRequestDispatcher(page);
rd.forward(request, response);
}else if(uri.indexOf("insert.do") != -1) {
String writer = request.getParameter("writer");
String memo = request.getParameter("memo");
MemoDTO dto = new MemoDTO();
dto.setWriter(writer);
dto.setMemo(memo);
//DAO에 레코드 저장요청
dao.insertMemo(dto);
Controller에서는 실질적인 코드 작업이 들어갑니다.
그래서 어떻게 데이터를 처리할 건지 먼저 정하고 구현하시는 게 좋습니다.
if문을 보시면 -1이 아니면 처리할 수 있게 했습니다.
indexOf()는 찾을 내용을 인덱스 처리해서 반환해 줍니다. 만약 내용이 없다면 -1을 반환해 줍니다.
그럼 반대로 내용이 있다면 0을 반환해 주겠죠
해석해 보면 "찾을 내용"이 -1이 아니면? 코드 실행할 수 있게 구현했습니다.
다음으로 DAO를 생성합니다.
■ MemoDAO.java
DAO에서는 데이터를 담아주기 위해 List <>(), Map <>()을 사용하는데,
여기서 주의할 점은 ArratList <>()는 에러가 발생합니다.
그 이유는 Mybatis는 시스템상 타입을 고정시켜 놨기 때문입니다.
Mybatis에서 파라미터(입력매개변수)는 1개만 전달할 수 있습니다.
그럼 Mybatis를 사용하기 위해 먼저
SqlSession session=MybatisManager.getInstance().openSession();
위 코드를 꼭 구현하셔야 합니다.
그럼 나머지 코드들도 알아봅시다.
public class MemoDAO {
//여기서는 List대신 ArrayList를 쓰면 에러가 난다. (mybatis 시스템상 타입을 고정시켜 놨다.)
public List<MemoDTO> listMemo(String searchkey, String search){
SqlSession session=MybatisManager.getInstance().openSession();
List<MemoDTO> list=null;
try {
if(searchkey.equals("writer_memo")) { //이름 + 메모 검색
list=session.selectList("memo.listAll",search); //search는 검색키워드
}else {
Map<String,String> map = new HashMap<>();
map.put("searchkey", searchkey);
map.put("search", search);
//mybatis에서 파라미터(입력매개변수)는 1개만 전달할 수 있음
list=session.selectList("memo.list",map);
}
// insert 때 보다 select때 처리가 더 좋다.
for(MemoDTO dto : list) {
String memo = dto.getMemo();
memo=memo.replace(" ", " "); //공백문자처리(스페이스 2개 변환)
memo=memo.replace("<", "<"); // less Than ~보다 작다
memo=memo.replace(">", ">"); // Greater Than ~보다 크다
//키워드 색상 처리
if(searchkey.equals("memo")) {
if(memo.indexOf(search) != -1) {
memo=memo.replace(search, "<font color='red'>"+ search+"</font>");
}
}
dto.setMemo(memo);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(session != null) session.close();//mybatis 객체 닫기
}
return list;
}
위 코드에서 session 객체에서 제공해 주는 selectList() 메서드를 통해서 mapper 파일에 요청을 했습니다.
list=session.selectList("memo.listAll",search); //search는 검색키워드
"memo.listAll"에서 memo는 mapper에서 지정한 namespace입니다.
listAll은 select문으로 보내는 id 값입니다.
그럼 바로 mapper 파일에서 작업을 해도 되지만,
Controller에서 insert.do 작업도 했기 때문에 DAO에 레코드 저장 요청을 보냈던,
insertMemo() 메서드도 구현을 해야 합니다.
public void insertMemo(MemoDTO dto) {
//mybatis 실행 객체 생성
SqlSession session=MybatisManager.getInstance().openSession();
session.insert("memo.insert", dto); //레코드 추가, Mybatis에서는 파라미터를 1개밖에 허용 안 한다
session.commit(); // Mybatis는 수동커밋, 자동커밋을 막았다.
}
※Mybatis는 자동 커밋이 안되기 때문에 수동 커밋 처리를 해주셔야 합니다.
Mybatis에 대해서 아직 생소하신 분들은 이전 포스팅을 참고해 보세요!
■ Mybatis
다음으로 mapper 파일에서 <select> , <insert> 작업을 합니다.
이전 포스팅에서 알아본 것처럼 mapper 파일은 SQL문을 작성하는 파일입니다.
<select>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="memo">
<!-- select에서 resultType="" 반드시 필요하다. -->
<select id="listAll" resultType="memo.dto.MemoDTO">
select idx,writer,memo,to_char(post_date, 'yyyy-mm-dd hh24:mi:ss') post_date
from memo
where writer like '%'||#{search}||'%' or memo like '%'||#{search}||'%'
order by idx desc
</select>
<select id="list" resultType="memo.dto.MemoDTO">
select idx,writer,memo,to_char(post_date, 'yyyy-mm-dd hh24:mi:ss') post_date
from memo
where ${searchkey} like '%'||#{search}||'%'
order by idx desc
</select>
<insert>
<!-- parameterType=""은 생략 가능하다. -->
<!-- #{변수}: 따옴표 포함 ex) #{writer}=> writer='kim', ${변수} : 따옴표 미포함 -->
<insert id="insert" parameterType="memo.dto.MemoDTO">
insert into memo(idx, writer, memo)
values ((select nvl(max(idx)+1,1)from memo), #{writer}, #{memo})
</insert>
mapper 파일 처리까지 완료되면 Controller에서 포워딩 처리 했던 memo_list.jsp 페이지를 생성합니다.
즉, 결과를 표출하는 View 단 페이지를 생성합니다.
■ memo_list.jsp
View 단 페이지에서는 코드량을 줄이기 위해 EL 기법, JSTL을 활용해서 코드를 구현했습니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="../include/jquery-3.6.3.min.js"></script>
<%@ include file="../include/header.jsp" %>
</head>
<body>
<table border="1" width="100%">
<tr>
<th>번호</th>
<th>이름</th>
<th width="50%">메모</th>
<th>날짜</th>
</tr>
<c:forEach var="row" items="${list}">
<tr>
<td>${row.idx}</td>
<td>${row.writer}</td>
<td><a href="${path}/memo_servlet/view.do?idx=${row.idx}">${row.memo}</a></td>
<td>${row.post_date}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
지금까지의 코드는 테이블 생성해서 결과 화면을 출력합니다.
여기서 추가로 진행해야 하는 건 memo_view.jsp 페이지를 생성해서 메모 클릭 시 내용을 좀 더 명확하게 볼 수 있고,
수정과 삭제 기능을 추가해야 합니다.
위 코드에서 <a> 태그를 통해서 memo_view 페이지로 전달될 수 있게 구현했습니다.
<td><a href="${path}/memo_servlet/view.do?idx=${row.idx}">${row.memo}</a></td>
■ memo_view.jsp ( 수정, 삭제 기능 추가)
메모를 수정하고, 삭제하려면 어떤 기능이 필요할까요? 간단하게 생각해 보자면 button입니다.
button 클릭 시 해당 이벤트가 발생할 수 있게 설정하기 위해 onclick() 처리를 하면 됩니다.
그럼 당연히 Controller, DAO에서 기능 추가를 해야 합니다.
<View 단>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="../include/jquery-3.6.3.min.js"></script>
<%@ include file="../include/header.jsp" %>
<script type="text/javascript">
function updateMemo() {
var writer=$("#writer").val();
var memo=$("#memo").val();
if(writer==""){
alert("이름을 입력하세요");
$("#writer").focus();
return;
}
if(memo==""){
alert("메모를 입력하세요");
$("#memo").focus();
return;
}
document.form1.action="${path}/memo_servlet/update.do";
document.form1.submit();
}
function deleteMemo() {
if(confirm("삭제하시겠습니까?")){
document.form1.action="${path}/memo_servlet/delete.do";
document.form1.submit();
}
}
</script>
</head>
<body>
<h2>메모 수정</h2>
<form name="form1" id="form1" method="post">
<table border="1" width="550px">
<tr>
<td>이름</td>
<td><input name="writer" id="writer" value="${dto.writer}"></td>
</tr>
<tr>
<td>메모</td>
<td><input name="memo" id="memo" size="40" value="${dto.memo}"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="hidden" name="idx" id="idx" value="${dto.idx}">
<input type="button" value="수정" onclick="updateMemo()">
<input type="button" value="삭제" onclick="deleteMemo()">
</td>
</tr>
</table>
</form>
</body>
</html>
<Controller>
}else if(uri.indexOf("view.do") != -1) {
int idx= Integer.parseInt(request.getParameter("idx"));
System.out.println("글번호:"+idx);
MemoDTO dto = dao.viewMemo(idx);
//request 영역에 저장
request.setAttribute("dto", dto);
String page = "/memo/memo_view.jsp";
//포워딩
RequestDispatcher rd = request.getRequestDispatcher(page);
rd.forward(request, response);
}else if(uri.indexOf("update.do") != -1) {
//글번호 hidden으로 넘어온 idx 처리
int idx = Integer.parseInt(request.getParameter("idx"));
String writer = request.getParameter("writer");
String memo = request.getParameter("memo");
MemoDTO dto = new MemoDTO();
dto.setIdx(idx);
dto.setWriter(writer);
dto.setMemo(memo);
dao.updateMemo(dto); //레코드 수정
//단순 페이지 이동
response.sendRedirect(request.getContextPath()+"/memo/memo.jsp");
}else if(uri.indexOf("delete.do") != -1) {
int idx = Integer.parseInt(request.getParameter("idx"));
dao.deleteMemo(idx);
response.sendRedirect(request.getContextPath()+"/memo/memo.jsp");
}
}
기능이 하나씩 추가가 되면 else if문 처리를 해주시면 됩니다.
(저처럼 Controller에서 먼저 메서드를 생성하시고 그 후에 DAO 작업을 하셔도 됩니다. 각자의 스타일이라서 자신한테 맞는 방법을 찾아서 하시면 될 거 같습니다. )
dao.updateMemo(dto); //레코드 수정
dao.deleteMemo(idx);
다음으로 DAO에서 기능 추가를 해봅시다.
<DAO>
public MemoDTO viewMemo(int idx) {
SqlSession session=MybatisManager.getInstance().openSession();
MemoDTO dto=session.selectOne("memo.view", idx);
//selectOne() 레코드 1개만 가져올 때
//selectList() 레코드 2개 이상 가져올 때(목록을 가져올 때)
session.close();
return dto;
}
public void updateMemo(MemoDTO dto) {
SqlSession session=MybatisManager.getInstance().openSession();
session.update("memo.update", dto);
session.commit();
session.close();
}
public void deleteMemo(int idx) {
SqlSession session=MybatisManager.getInstance().openSession();
session.update("memo.delete", idx);
session.commit();
session.close();
}
}
여기서 주의할 점은 update, delete 처리를 SQL에 보내야 합니다.
그렇다면 각 기능마다 Sqlsession 객체를 생성해야 합니다.
그럼 다음으로 mapper 파일에 보내는 id값에 SQL update, delete 처리를 합니다.
<Mapper 파일>
<!-- parameterType="type" 은 생략 가능하다 -->
<select id="view" parameterType="int" resultType="memo.dto.MemoDTO">
select * from memo where idx=#{idx}
</select>
<update id="update" parameterType="memo.dto.MemoDTO">
update memo set writer=#{writer}, memo=#{memo}
where idx=#{idx}
</update>
<delete id="delete" parameterType="int">
delete from memo
where idx=#{idx}
</delete>
지금까지 한 줄 메모장 코드 구현을 끝냈습니다.
그럼 이제 결과로 알아봅시다.
(실행은 첫 페이지인 memo.jsp에서 합니다.)
입력단에서 <div> 태그 id="result"가 SQL Table을 출력해서 보여줍니다.
그럼 여기서 데이터를 추가해 보겠습니다.
insert가 제대로 작동하는 걸 확인하고 이제 수정, 삭제 기능을 확인해 봅시다.
해당 메모를 클릭하시면 view 단 페이지가 열립니다.
먼저 수정 기능을 확인해 봅시다.
다음은 삭제 기능입니다.
삭제 기능은 버튼 클릭 시 confirm() 객체에서 먼저 알림이 전송해 주고 이어서 진행하면 됩니다.
삭제 기능까지 제대로 작동하는 걸 확인했습니다.
마치며
오늘은 지금까지 배워온 지식을 바탕으로 한 줄 메모장을 만들어 봤습니다.
주의할 점은 정말 많지만 특히, spelling 조심합시다!
코드량이 많아질수록 spelling 확인 안 하고 넘어가서 시간 버리는 일이 많이 발생합니다.
그럼 다음 응용 예제로 뵙겠습니다.
'[ View ] > JSP' 카테고리의 다른 글
[ JSP ] 페이지 나누기 (0) | 2023.03.22 |
---|---|
[ JSP ] Mybatis 응용 예제(방명록) (0) | 2023.03.19 |
[ JSP ] Mybatis 개념 및 설정 (0) | 2023.03.16 |
[ JSP ] JSTL(Jsp Standard Tag Library) (2) | 2023.03.15 |
[ JSP ] EL(Expression Language) (0) | 2023.03.14 |