Study Web Development

2월 11일

이전으로

Servlet & JSP

9. MVC

mvcPage

프로젝트 생성 및 설정
  1. table.sql 하단에 다음의 내용을 추가
/* 상품 관리 */
CREATE TABLE zitem(
	item_num NUMBER NOT NULL,
	name VARCHAR2(30) NOT NULL,
	price NUMBER(8) NOT NULL,
	quantity NUMBER(5) NOT NULL,
	photo1 VARCHAR2(60) NOT NULL,
	photo2 VARCHAR2(60) NOT NULL,
	detail CLOB NOT NULL,
	reg_date DATE DEFAULT SYSDATE NOT NULL,
	modify_date DATE,
	status NUMBER(1) NOT NULL,
	CONSTRAINT zitem_pk PRIMARY KEY(item_num)
);

CREATE SEQUENCE zitem_seq;
  1. WEB-INF 폴더 오른쪽 클릭하고 새 파일 shop.properties 생성
# 상품 관리/관리자
/item/list.do=kr.item.action.AdminListAction
/item/writeForm.do=kr.item.action.AdminWriteFormAction
/item/write.do=kr.item.action.AdminWriteAction
  1. WEB-INF 폴더의 web.xml를 열고, 왼쪽 하단의 Source 탭 선택 후 <init-param> 태그 사이의 내용을 다음처럼 수정
  	<init-param>
  		<param-name>propertiesPath</param-name>
  		<param-value>/WEB-INF/member.properties,/WEB-INF/board.properties,/WEB-INF/shop.properties</param-value>
  	</init-param>
자바빈
  1. src/main/java 오른쪽 클릭하고 새 패키지 kr.item.vo 생성 후 새 클래스 ItemVO 생성
package kr.item.vo;

import java.sql.Date;

public class ItemVO {
	private int item_num; // 상품 번호
	private String name; // 상품명
	private int price; // 가격
	private int quantity; // 판매 수량
	private String photo1; // 상품 사진 파일명1
	private String photo2; // 상품 사진 파일명2
	private String detail; // 상품 정보
	private Date reg_date; // 등록일
	private Date modify_date; // 수정일
	private int status; // 상품 표시 여부
	
	public int getItem_num() {
		return item_num;
	}
	public void setItem_num(int item_num) {
		this.item_num = item_num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}
	public String getPhoto1() {
		return photo1;
	}
	public void setPhoto1(String photo1) {
		this.photo1 = photo1;
	}
	public String getPhoto2() {
		return photo2;
	}
	public void setPhoto2(String photo2) {
		this.photo2 = photo2;
	}
	public String getDetail() {
		return detail;
	}
	public void setDetail(String detail) {
		this.detail = detail;
	}
	public Date getReg_date() {
		return reg_date;
	}
	public void setReg_date(Date reg_date) {
		this.reg_date = reg_date;
	}
	public Date getModify_date() {
		return modify_date;
	}
	public void setModify_date(Date modify_date) {
		this.modify_date = modify_date;
	}
	public int getStatus() {
		return status;
	}
	public void setStatus(int status) {
		this.status = status;
	}
}
DAO
  1. src/main/java 오른쪽 클릭하고 새 패키지 kr.item.dao 생성 후 새 클래스 ItemDAO 생성
package kr.item.dao;

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

import kr.item.vo.ItemVO;
import kr.util.DBUtil;

public class ItemDAO {
	// 싱글턴 패턴
	private static ItemDAO instance = new ItemDAO();
	public static ItemDAO getInstance() {
		return instance;
	}
	private ItemDAO() {}
	
	// 관리자 : 상품 등록
	public void insertItem(ItemVO item) throws Exception {
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "INSERT INTO zitem (item_num, name, price, quantity, photo1, photo2, detail, status) "
				+ "VALUES (zitem_seq.NEXTVAL, ?, ?, ?, ?, ?, ?, ?)";
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setString(1, item.getName());
			pstmt.setInt(2, item.getPrice());
			pstmt.setInt(3, item.getQuantity());
			pstmt.setString(4, item.getPhoto1());
			pstmt.setString(5, item.getPhoto2());
			pstmt.setString(6, item.getDetail());
			pstmt.setInt(7, item.getStatus());
			// SQL문 실행
			pstmt.executeUpdate();
		}
		catch(Exception e) {
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
		}
	}
	
	// 관리자/사용자 : 전체 상품 갯수/검색 상품 갯수
	public int getItemCount(String keyfield, String keyword, int status) throws Exception {
		int count = 0;
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		String sub_sql = "";
		int cnt = 0; // 가변적 ? 번호
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			
			// 검색 처리
			if(keyword!=null && !"".equals(keyword)) {
				if(keyfield.equals("1")) sub_sql = "AND name LIKE ?";
				else if(keyfield.equals("2")) sub_sql = "AND detail LIKE ?";
			}
			
			// SQL문 작성
			sql = "SELECT COUNT(*) FROM zitem WHERE status > ? " + sub_sql; // 전체 검색시에는 인자로 0 전달
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(++cnt, status);
			if(keyword != null && !"".equals(keyword)) {
				pstmt.setString(++cnt, "%" + keyword + "%");
			}
			// SQL문을 실행해서 결과 행을 ResultSet에 담아 반환
			rs = pstmt.executeQuery();
			if(rs.next()) {
				count = rs.getInt(1);
			}
		}
		catch(Exception e) {
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(rs, pstmt, conn);
		}
		
		return count;
	}
	
	// 관리자/사용자 : 전체 목록/검색 목록
	public List<ItemVO> getListItem(int startRow, int endRow, String keyfield, String keyword, int status) throws Exception {
		List<ItemVO> list = null;
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		String sub_sql = "";
		int cnt = 0; // 가변적 ? 번호
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			
			// 검색 처리
			if(keyword!=null && !"".equals(keyword)) {
				if(keyfield.equals("1")) sub_sql = "AND name LIKE ?";
				else if(keyfield.equals("2")) sub_sql = "AND detail LIKE ?";
			}
			
			// SQL문 작성
			sql = "SELECT * FROM (SELECT i.*, ROWNUM AS rnum "
				+ "FROM (SELECT * FROM zitem WHERE status > ? " + sub_sql
				+ " ORDER BY item_num DESC) i) WHERE rnum >= ? AND rnum <= ?";
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(++cnt, status);
			if(keyword!=null && !"".equals(keyword)) {
				pstmt.setString(++cnt, "%" + keyword + "%");
			}
			pstmt.setInt(++cnt, startRow);
			pstmt.setInt(++cnt, endRow);
			// SQL문을 실행해서 결과 행들을 ResultSet에 담아 반환
			rs = pstmt.executeQuery();
			list = new ArrayList<ItemVO>();
			while(rs.next()) {
				ItemVO item = new ItemVO();
				item.setItem_num(rs.getInt("item_num"));
				item.setName(rs.getString("name"));
				item.setPrice(rs.getInt("price"));
				item.setQuantity(rs.getInt("quantity"));
				item.setPhoto1(rs.getString("photo1"));
				item.setPhoto2(rs.getString("photo2"));
				item.setReg_date(rs.getDate("reg_date"));
				item.setModify_date(rs.getDate("modify_date"));
				item.setStatus(rs.getInt("status"));
				
				// 자바빈을 ArrayList에 저장
				list.add(item);
			}
		}
		catch(Exception e) {
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(rs, pstmt, conn);
		}
		
		return list;
	}
	
	// 관리자/사용자 : 상품 상세
	public ItemVO getItem(int item_num) throws Exception {
		ItemVO item = null;
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = null;
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			// SQL문 작성
			sql = "SELECT * FROM zitem WHERE item_num=?";
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(1, item_num);
			// SQL문을 실행해서 결과 행을 ResultSet에 담아 반환
			rs = pstmt.executeQuery();
			if(rs.next()) {
				item = new ItemVO();
				item.setItem_num(item_num);
				item.setName(rs.getString("name"));
				item.setPrice(rs.getInt("price"));
				item.setQuantity(rs.getInt("quantity"));
				item.setPhoto1(rs.getString("photo1"));
				item.setPhoto2(rs.getString("photo2"));
				item.setDetail(rs.getString("detail"));
				item.setStatus(rs.getInt("status"));
			}
		}
		catch(Exception e) {
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(rs, pstmt, conn);
		}
		
		return item;
	}
	
	// 관리자 : 상품 수정
	public void updateItem(ItemVO item) throws Exception {
		Connection conn = null;
		PreparedStatement pstmt = null;
		String sql = null;
		String sub_sql = "";
		int cnt = 0; // 가변적 ? 번호
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			
			if(item.getPhoto1()!=null) sub_sql += ", photo1=?";
			if(item.getPhoto2()!=null) sub_sql += ", photo2=?";
			
			// SQL문 작성
			sql = "UPDATE zitem SET name=?, price=?, quantity=?" + sub_sql 
				+ ", detail=?, modify_date=SYSDATE, status=? WHERE item_num=?";
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setString(++cnt, item.getName());
			pstmt.setInt(++cnt, item.getPrice());
			pstmt.setInt(++cnt, item.getQuantity());
			if(item.getPhoto1()!=null) pstmt.setString(++cnt, item.getPhoto1());
			if(item.getPhoto2()!=null) pstmt.setString(++cnt, item.getPhoto2());
			pstmt.setString(++cnt, item.getDetail());
			pstmt.setInt(++cnt, item.getStatus());
			pstmt.setInt(++cnt, item.getItem_num());
			// SQL문 실행
			pstmt.executeUpdate();
		}
		catch (Exception e) {
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt, conn);
		}
	}

	// 관리자 : 상품 삭제
	public void deleteItem(int item_num) throws Exception {
		Connection conn = null;
		PreparedStatement pstmt = null;
		PreparedStatement pstmt2 = null;
		String sql = null;
		
		try {
			// 커넥션 풀로부터 커넥션 할당
			conn = DBUtil.getConnection();
			// 오토 커밋 해제
			conn.setAutoCommit(false);
			
			// 삭제하고자 하는 상품이 카트에 담겨 있으면 카트에 저장된 상품을 삭제
			// SQL문 작성
			sql = "DELETE FROM zcart WHERE item_num=?";
			// PreparedStatement 객체 생성
			pstmt = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt.setInt(1, item_num);
			// SQL문 실행
			pstmt.executeUpdate();
			
			// 상품 테이블에서 상품 삭제
			// SQL문 작성
			sql = "DELETE FROM zitem WHERE item_num=?";
			// PreparedStatement 객체 생성
			pstmt2 = conn.prepareStatement(sql);
			// ?에 데이터를 바인딩
			pstmt2.setInt(1, item_num);
			// SQL문 실행
			pstmt2.executeUpdate();
			
			// SQL문 실행이 모두 성공하면
			conn.commit();
		}
		catch(Exception e) {
			// SQL문 실행이 하나라도 실패하면
			conn.rollback();
			throw new Exception(e);
		}
		finally {
			// 자원 정리
			DBUtil.executeClose(null, pstmt2, null);
			DBUtil.executeClose(null, pstmt, conn);
		}
	}

}
Model
  1. src/main/java 오른쪽 클릭하고 새 패키지 kr.item.action 생성 후 새 클래스 AdminListAction 생성
package kr.item.action;

import java.util.List;

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

import kr.controller.Action;
import kr.item.dao.ItemDAO;
import kr.item.vo.ItemVO;
import kr.util.PagingUtil;

public class AdminListAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			return "redirect:/member/loginForm.do";
		}
		
		Integer user_auth = (Integer)session.getAttribute("user_auth");
		if(user_auth!=3) { // 관리자로 로그인하지 않은 경우
			return "/WEB-INF/views/common/notice.jsp";
		}
		
		// 관리자로 로그인한 경우
		String pageNum = request.getParameter("pageNum");
		if(pageNum==null) pageNum = "1";
		
		String keyfield = request.getParameter("keyfield");
		String keyword = request.getParameter("keyword");
		
		ItemDAO dao = ItemDAO.getInstance();
		int count = dao.getItemCount(keyfield, keyword, 0); // status가 0이면 미표시(1)와 표시(2)를 모두 출력
		
		// 페이지 처리
		PagingUtil page = new PagingUtil(keyfield, keyword, Integer.parseInt(pageNum), count, 20, 10, "list.do");
		
		List<ItemVO> list = null;
		if(count>0) {
			list = dao.getListItem(page.getStartCount(), page.getEndCount(), keyfield, keyword, 0); // status가 0이면 미표시(1)와 표시(2)를 모두 출력
		}
		
		request.setAttribute("count", count);
		request.setAttribute("list", list);
		request.setAttribute("pagingHtml", page.getPagingHtml());
		
		// JSP 경로 반환
		return "/WEB-INF/views/item/admin_list.jsp";
	}

}
  1. 새 클래스 AdminWriteFormAction 생성
package kr.item.action;

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

import kr.controller.Action;

public class AdminWriteFormAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			return "redirect:/member/loginForm.do";
		}
		
		Integer user_auth = (Integer)session.getAttribute("user_auth");
		if(user_auth!=3) { // 관리자로 로그인하지 않은 경우
			return "/WEB-INF/views/common/notice.jsp";
		}
		
		// 관리자로 로그인한 경우
		return "/WEB-INF/views/item/admin_writeForm.jsp";
	}

}
  1. 새 클래스 AdminWriteAction 생성
package kr.item.action;

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

import com.oreilly.servlet.MultipartRequest;

import kr.controller.Action;
import kr.item.dao.ItemDAO;
import kr.item.vo.ItemVO;
import kr.util.FileUtil;

public class AdminWriteAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			return "redirect:/member/loginForm.do";
		}
		
		Integer user_auth = (Integer)session.getAttribute("user_auth");
		if(user_auth!=3) { // 관리자로 로그인하지 않은 경우
			return "/WEB-INF/views/common/notice.jsp";
		}
		
		// 관리자로 로그인한 경우
		MultipartRequest multi = FileUtil.createFile(request);
		
		ItemVO item = new ItemVO();
		item.setName(multi.getParameter("name"));
		item.setPrice(Integer.parseInt(multi.getParameter("price")));
		item.setQuantity(Integer.parseInt(multi.getParameter("quantity")));
		item.setPhoto1(multi.getFilesystemName("photo1"));
		item.setPhoto2(multi.getFilesystemName("photo2"));
		item.setDetail(multi.getParameter("detail"));
		item.setStatus(Integer.parseInt(multi.getParameter("status")));
		
		ItemDAO dao = ItemDAO.getInstance();
		dao.insertItem(item);
		
		// JSP 경로 반환
		return "redirect:/item/list.do";
	}

}
View
  1. common 폴더의 header.jsp를 열고 <c:if test="${!empty user_num && user_auth == 3}"> 태그 사이의 내용을 다음처럼 수정
	<c:if test="${!empty user_num && user_auth == 3}">
		<a href="${pageContext.request.contextPath}/member/memberList.do">회원 관리</a>
		<a href="${pageContext.request.contextPath}/item/list.do">상품 관리</a>
	</c:if>
  1. views 폴더의 하위 폴더로 item 생성 후 새 JSP 파일 admin_list.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품 목록</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="page-main">
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>
	<h2>상품 목록</h2>
	<form id="search_form" action="list.do" method="get">
		<ul class="search">
			<li>
				<select name="keyfield">
					<option value="1">상품명</option>
					<option value="2">내용</option>
				</select>
			</li>
			<li>
				<input type="search" size="16" name="keyword" id="keyword">
			</li>
			<li>
				<input type="submit" value="검색">
			</li>
		</ul>
	</form>
	<div class="list-space align-right">
		<input type="button" value="상품 등록" onclick="location.href = 'writeForm.do';">
		<input type="button" value="목록으로" onclick="location.href = 'list.do';">
		<input type="button" value="홈으로" onclick="location.href = '${pageContext.request.contextPath}/main/main.do';">
	</div>
	<c:if test="${count==0}">
	<div class="result-display">
		표시할 상품이 없습니다.
	</div>
	</c:if>
	<c:if test="${count>0}">
	<table>
		<tr>
			<th>상품명</th>
			<th>가격</th>
			<th>재고 수</th>
			<th>등록일</th>
			<th>상태</th>
		</tr>
		<c:forEach var="item" items="${list}">
		<tr>
			<td><a href="modifyForm.do?item_num=${item.item_num}">${item.name}</a></td>
			<td><fmt:formatNumber value="${item.price}"/></td>
			<td><fmt:formatNumber value="${item.quantity}"/></td>
			<td>${item.reg_date}</td>
			<td>
				<c:if test="${item.status==1}">미표시</c:if>
				<c:if test="${item.status==2}">표시</c:if>
			</td>
		</tr>
		</c:forEach>
	</table>
	<div class="align-center">
		${pagingHtml}
	</div>
	</c:if>
</div>
</body>
</html>
  1. item 폴더에 새 JSP 파일 admin_writeForm.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품 등록</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	$(function() {
		$('#write_form').submit(function(event) {
			if($('input[type=radio]:checked').length<1) {
				alert('상품 표시 여부를 지정하세요!');
				return false;
			}
			
			let isValid = true; // submit()의 return 값 지정
			
			$('li').each(function() {
				let input = $(this).find('input[type!="button"],textarea');
				if(!input.val().trim()) {
					let word = $(this).find('label').text();
					let last = word.charCodeAt(word.length-1);
					let post;
					if(last>''.charCodeAt(0) && last<''.charCodeAt(0)) {
						post = (last - ''.charCodeAt(0)) % 28 > 0 ? '' : '';
					}
					else {
						post = '을/를';
					}
					if(input.attr('type')=='file') post += ' 선택하세요!';
					else post += ' 입력하세요!';
					alert(word + post);
					input.val('').focus();
					isValid = false; // submit()의 return 값 지정
					return false; // each() 루프 중단
				}
			}); // end of each
			
			return isValid;
		}); // end of submit
	});
</script>
</head>
<body>
<div class="page-main">
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>
	<h2>상품 등록</h2>
	<form action="write.do" method="post" enctype="multipart/form-data" id="write_form">
		<ul>
			<li>
				<label>상품 표시 여부</label>
				<input type="radio" name="status" value="1" id="status1">미표시
				<input type="radio" name="status" value="2" id="status2">표시
			</li>
			<li>
				<label for="name">상품명</label>
				<input type="text" name="name" id="name" maxlength="10">
			</li>
			<li>
				<label for="price">가격</label>
				<input type="number" name="price" id="price" min="1" max="99999999">
			</li>
			<li>
				<label for="quantity">수량</label>
				<input type="number" name="quantity" id="quantity" min="0" max="99999">
			</li>
			<li>
				<label for="photo1">상품 사진1</label>
				<input type="file" name="photo1" id="photo1" accept="image/jpeg,image/gif,image/png">
			</li>
			<li>
				<label for="photo2">상품 사진2</label>
				<input type="file" name="photo2" id="photo2" accept="image/jpeg,image/gif,image/png">
			</li>
			<li>
				<label for="detail">상품 설명</label>
				<textarea rows="5" cols="30" name="detail" id="detail"></textarea>
			</li>
		</ul>
		<div class="align-center">
			<input type="submit" value="등록">
			<input type="button" value="목록으로" onclick="location.href = 'list.do';">
		</div>
	</form>
</div>
</body>
</html>

다음으로