m1_board
WHERE ROWNUM<=10
과 같이 특정 값 이하 조건은 문제없이 작동하지만, WHERE ROWNUM>=2
처럼 1보다 큰 숫자 이상 조건은 항상 만족하지 못하기 때문detail.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="kr.board.dao.BoardDAO" %>
<%@ page import="kr.board.vo.BoardVO" %>
<%
int num = Integer.parseInt(request.getParameter("num"));
BoardDAO dao = BoardDAO.getInstance();
BoardVO boardVO = dao.getBoard(num);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 글 상세</title>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/layout.css">
</head>
<body>
<div class="page-main">
<h1>게시판 글 상세</h1>
<ul>
<li>글 번호 : <%= boardVO.getNum() %></li>
<li>제목 : <%= boardVO.getTitle() %></li>
<li>작성자 : <%= boardVO.getName() %></li>
</ul>
<hr size="1" width="100%" noshade>
<p>
<%= boardVO.getContent() %>
</p>
<div class="align-right">
작성일 : <%= boardVO.getReg_date() %>
<input type="button" value="수정" onclick="location.href = 'updateForm.jsp?num=<%= boardVO.getNum() %>';">
<input type="button" value="삭제" onclick="location.href = 'deleteForm.jsp?num=<%= boardVO.getNum() %>';">
<input type="button" value="목록" onclick="location.href = 'list.jsp';">
</div>
</div>
</body>
</html>
updateForm.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="kr.board.dao.BoardDAO" %>
<%@ page import="kr.board.vo.BoardVO" %>
<%
int num = Integer.parseInt(request.getParameter("num"));
BoardDAO dao = BoardDAO.getInstance();
BoardVO board = dao.getBoard(num);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 수정</title>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/layout.css">
<script type="text/javascript">
window.onload = function() {
document.getElementById('update_form').onsubmit = function() {
let list = document.getElementsByTagName('li');
for(let i=0;i<list.length;i++) {
let input = list[i].querySelector('input,textarea');
input.value = input.value.trim();
if(!input.value) {
let word = list[i].querySelector('label').textContent;
let post = (word.charCodeAt(word.length-1) - '가'.charCodeAt(0)) % 28 > 0 ? '을' : '를';
alert(word + post + ' 입력하세요!');
input.focus();
return false;
}
}
};
};
</script>
</head>
<body>
<div class="page-main">
<h1>글 수정</h1>
<form id="update_form" action="update.jsp" method="post">
<input type="hidden" name="num" value="<%= board.getNum() %>"> <%-- 회원 번호와 달리 글 번호는 세션에 저장하지 않으므로 type="hidden"으로 전달 --%>
<ul>
<li>
<label for="title">제목</label>
<input type="text" name="title" id="title" value="<%= board.getTitle() %>" size="30" maxlength="50">
</li>
<li>
<label for="name">이름</label>
<input type="text" name="name" id="name" value="<%= board.getName() %>" size="10" maxlength="10">
</li>
<li>
<label for="passwd">비밀번호</label>
<input type="password" name="passwd" id="passwd" size="10" maxlength="12"> <%-- 비밀번호는 수정 대상이 아니라, 인증용 --%>
* 글 등록시 입력한 비밀번호
</li>
<li>
<label for="content">내용</label>
<textarea rows="5" cols="40" name="content" id="content"><%= board.getContent() %></textarea> <%-- <textarea> 태그의 미리보기는 (value 속성이 아니라) 시작 태그와 끝 태그 사이에, 공백 없이 명시 --%>
</li>
</ul>
<div class="align-center">
<input type="submit" value="글 수정">
<input type="button" value="이전" onclick="history.go(-1);">
<input type="button" value="목록" onclick="location.href = 'list.jsp';">
</div>
</form>
</div>
</body>
</html>
update.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="kr.board.dao.BoardDAO" %>
<%@ page import="kr.board.vo.BoardVO" %>
<%
// 전송된 데이터 인코딩 처리
request.setCharacterEncoding("UTF-8");
%>
<jsp:useBean id="boardVO" class="kr.board.vo.BoardVO"/>
<jsp:setProperty property="*" name="boardVO"/>
<%
BoardDAO dao = BoardDAO.getInstance();
// 비밀번호 인증을 수행하기 위해 글 번호를 전달하고 한 건의 레코드 정보를 자바빈에 담아 반환
BoardVO db_board = dao.getBoard(boardVO.getNum());
boolean check = false;
if(db_board!=null) {
check = db_board.isCheckedPassword(boardVO.getPasswd());
}
if(check) { // 인증 성공
boardVO.setIp(request.getRemoteAddr());
dao.update(boardVO);
%>
<script type="text/javascript">
alert('글 수정을 완료했습니다.');
location.href = 'list.jsp';
</script>
<%
}
else { // 인증 실패
%>
<script type="text/javascript">
alert('비밀번호 불일치!');
history.go(-1);
</script>
<%
}
%>
m1_product
m1_product
입력 후 Next-Next-Generate web.xml depolyment descriptor 선택 후 FinishC:\javaWork\apps
에서 ojdbc8.jar
를 복사하여 C:\javaWork\workspace_jsp\m1_product\src\main\webapp\WEB-INF\lib
에 붙여넣기m1_board
프로젝트의 webapp/META-INF
폴더에서 context.xml
을 복사하여 m1_product
프로젝트의 webapp/META-INF
폴더에 붙여넣기m1_board
프로젝트의 webapp
폴더에서 css
폴더를 복사하여 m1_product
프로젝트의 webapp
폴더에 붙여넣기m1_product
프로젝트의 webapp
폴더 오른쪽 클릭 후 새 폴더 js
생성하고 jquery-3.6.0.min.js
파일을 해당 폴더로 이동src/main/webapp
폴더 오른쪽 클릭하고 새 폴더 sql
생성 후 table.sql
생성CREATE TABLE sproduct(
num NUMBER PRIMARY KEY,
name VARCHAR2(30) NOT NULL,
price NUMBER NOT NULL,
stock NUMBER NOT NULL,
origin VARCHAR2(30) NOT NULL,
content CLOB NOT NULL,
reg_date DATE DEFAULT SYSDATE NOT NULL
);
CREATE SEQUENCE sproduct_seq;
src/main/java
오른쪽 클릭하여 새 패키지 kr.util
생성하고 PagingUtil.java
를 해당 패키지로 이동src/main/java
오른쪽 클릭하여 새 패키지 kr.product.vo
생성하고 새 클래스 ProductVO
생성package kr.product.vo;
import java.sql.Date;
public class ProductVO {
private int num; // 상품 번호
private String name; // 상품명
private int price; // 가격
private int stock; // 재고
private String origin; // 원산지
private String content; // 상품 설명
private Date reg_date; // 등록일
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = 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 getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
}
src/main/java
오른쪽 클릭하여 새 패키지 kr.product.dao
생성하고 새 클래스 ProductDAO
생성package kr.product.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import kr.product.vo.ProductVO;
public class ProductDAO {
// 싱글턴 패턴
private static ProductDAO instance = new ProductDAO();
public static ProductDAO getInstance() {
return instance;
};
private ProductDAO() {}
// context.xml에서 설정 정보를 읽어들여 커넥션 풀로부터 커넥션을 할당받음
private Connection getConnection() throws Exception {
Context initCtx = new InitialContext();
DataSource ds = (DataSource)initCtx.lookup("java:comp/env/jdbc/xe");
return ds.getConnection();
}
// 자원 정리
private void executeClose(ResultSet rs, PreparedStatement pstmt, Connection conn) {
if(rs!=null) try {rs.close();} catch(SQLException e) {}
if(pstmt!=null) try {pstmt.close();} catch(SQLException e) {}
if(conn!=null) try {conn.close();} catch(SQLException e) {}
}
// 상품 저장
public void insert(ProductVO vo) throws Exception {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = null;
try {
conn = getConnection();
sql = "INSERT INTO sproduct (num, name, price, stock, origin, content) "
+ "VALUES (sproduct_seq.NEXTVAL, ?, ?, ?, ?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, vo.getName());
pstmt.setInt(2, vo.getPrice());
pstmt.setInt(3, vo.getStock());
pstmt.setString(4, vo.getOrigin());
pstmt.setString(5, vo.getContent());
pstmt.executeUpdate();
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(null, pstmt, conn);
}
}
// 상품의 총 갯수
public int getCount() throws Exception {
int count = 0;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = null;
try {
conn = getConnection();
sql = "SELECT COUNT(*) FROM sproduct";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if(rs.next()) {
count = rs.getInt(1);
}
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(rs, pstmt, conn);
}
return count;
}
// 상품 목록
public List<ProductVO> getList(int startRow, int endRow) throws Exception {
List<ProductVO> list = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = null;
try {
conn = getConnection();
sql = "SELECT * FROM (SELECT sp.*, ROWNUM AS rnum "
+ "FROM (SELECT * FROM sproduct ORDER BY num DESC) sp) "
+ "WHERE rnum >= ? AND rnum <= ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, startRow);
pstmt.setInt(2, endRow);
rs = pstmt.executeQuery();
list = new ArrayList<ProductVO>();
while(rs.next()) {
ProductVO vo = new ProductVO();
vo.setNum(rs.getInt("num"));
vo.setName(rs.getString("name"));
vo.setPrice(rs.getInt("price"));
vo.setStock(rs.getInt("stock"));
vo.setOrigin(rs.getString("origin"));
vo.setReg_date(rs.getDate("reg_date"));
list.add(vo);
}
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(rs, pstmt, conn);
}
return list;
}
// 상품 상세 정보
public ProductVO getProduct(int num) throws Exception {
ProductVO vo = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = null;
try {
conn = getConnection();
sql = "SELECT * FROM sproduct WHERE num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, num);
rs = pstmt.executeQuery();
if(rs.next()) {
vo = new ProductVO();
vo.setNum(num);
vo.setName(rs.getString("name"));
vo.setPrice(rs.getInt("price"));
vo.setStock(rs.getInt("stock"));
vo.setOrigin(rs.getString("origin"));
vo.setContent(rs.getString("content"));
vo.setReg_date(rs.getDate("reg_date"));
}
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(rs, pstmt, conn);
}
return vo;
}
// 상품 정보 수정
public void update(ProductVO vo) throws Exception {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = null;
try {
conn = getConnection();
sql = "UPDATE sproduct SET name=?, price=?, stock=?, origin=?, content=? WHERE num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, vo.getName());
pstmt.setInt(2, vo.getPrice());
pstmt.setInt(3, vo.getStock());
pstmt.setString(4, vo.getOrigin());
pstmt.setString(5, vo.getContent());
pstmt.setInt(6, vo.getNum());
pstmt.executeUpdate();
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(null, pstmt, conn);
}
}
// 상품 정보 삭제
public void delete(int num) throws Exception {
Connection conn = null;
PreparedStatement pstmt = null;
String sql = null;
try {
conn = getConnection();
sql = "DELETE FROM sproduct WHERE num=?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, num);
pstmt.executeUpdate();
}
catch(Exception e) {
throw new Exception(e);
}
finally {
executeClose(null, pstmt, conn);
}
}
}
webapp
폴더 오른쪽 클릭 후 새 폴더 views
생성하고 새 JSP 파일 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="<%= request.getContextPath() %>/css/layout.css">
</head>
<body>
<div class="page-main">
<h1>상품 등록</h1>
<form action="write.jsp" method="post">
<ul>
<li>
<label for="name">상품명</label>
<input type="text" name="name">
</li>
<li>
<label for="price">가격</label>
<input type="number" name="price" min="0">
</li>
<li>
<label for="stock">재고</label>
<input type="number" name="stock" min="0">
</li>
<li>
<label for="origin">원산지</label>
<input type="text" name="origin">
</li>
<li>
<label for="content">상품 설명</label>
<textarea rows="5" cols="40" name="content"></textarea>
</li>
</ul>
<div class="align-center">
<input type="submit" value="등록">
<input type="button" value="목록" onclick="location.href = 'list.jsp';">
</div>
</form>
</div>
<script type="text/javascript" src="<%= request.getContextPath() %>/js/validateLength.js"></script>
<script type="text/javascript">
document.querySelector('form').onsubmit = validateSubmit;
let txts = document.querySelectorAll('input[type="text"]');
for(let i=0;i<txts.length;i++) {
txts[i].onkeyup = function() {
while(getBytesLength(this.value)>30) {
this.value = this.value.slice(0, -1);
}
};
}
</script>
</body>
</html>
js
폴더에 새 JavaScript 파일 validateLength.js
생성function validateSubmit() {
let list = document.getElementsByTagName('li');
for(let i=0;i<list.length;i++) {
let input = list[i].querySelector('input,textarea');
input.value = input.value.trim();
if(!input.value) {
let word = list[i].querySelector('label').textContent;
let post = (word.charCodeAt(word.length-1) - '가'.charCodeAt(0)) % 28 > 0 ? '을' : '를';
alert(word + post + ' 입력하세요!');
input.focus();
return false;
}
}
}
function getBytesLength(str) {
let bytes = 0;
for(let i=0;i<str.length;i++) {
let unicode = str.charCodeAt(i);
bytes += unicode >> 11 ? 3 : (unicode >> 7 ? 2 : 1); // 2^11=2048로 나누었을 때 몫이 있으면 3bytes, 그보다 작은 수이면서 2^7=128로 나누었을 때 몫이 있으면 2bytes, 그 외에는 1byte
}
return bytes;
}
write.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="kr.product.dao.ProductDAO" %>
<%
request.setCharacterEncoding("UTF-8");
%>
<jsp:useBean id="vo" class="kr.product.vo.ProductVO"/>
<jsp:setProperty property="*" name="vo"/>
<%
ProductDAO dao = ProductDAO.getInstance();
dao.insert(vo);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품 등록 완료</title>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/layout.css">
</head>
<body>
<div class="page-main">
<h1>상품 등록 완료</h1>
<div class="result-display">
<div class="align-center">
상품 등록이 완료되었습니다.<br>
<input type="button" value="목록" onclick="location.href = 'list.jsp';">
<input type="button" value="추가" onclick="location.href = 'writeForm.jsp';">
</div>
</div>
</div>
</body>
</html>