웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.05.21

shine94 2020. 5. 21. 09:07

CRUD : Create Read Update Delete

 

[JSP15_JDBC] JDBC 연결 + 게시판 작성

1. 게시판 설계
1) 게시판 사이클
: 글 생성 - 글 목록보기 - 글 내용읽기 - 글 수정 - 글 삭제


2) 글 작성시 작성자와 글제목은 필수사항


3) 조회수 구현

 

 

2. Dynamic Web Project 생성 후 DB Connect

 : 데이터베이스 > 새 데이터베이스 연결

> Oracl, Project는 JSP15_JDBC > Next 클릭 

> Host, Datebase, Username, Password 기입 후 Test Connection해보고 정상적으로 연결되면 Finish 클릭

 

 

3. ER Diagram 사용하여 실습용 데이터베이스와 테이블 생성

** test_write_orcle.erm

** test_write_orcle.sql

/* Drop Tables */

DROP TABLE test_write CASCADE CONSTRAINTS;




/* Create Tables */

CREATE TABLE test_write
(
	wr_uid number NOT NULL,
	wr_subject varchar2(200) NOT NULL,
	wr_content clob,
	wr_name varchar2(40) NOT NULL,
	wr_viewcnt number DEFAULT 0,
	wr_regdate date DEFAULT SYSDATE,
	PRIMARY KEY (wr_uid)
);

-- 시퀀스
CREATE SEQUENCE TEST_WRITE_SEQ;

-- 기본데이터 작성
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '첫째글:방가요', '안녕하세요', '김희철', 0, '2017-03-02');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '둘째글:헤헤헤','1111', '김수길', 0, '2017-03-02');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '세째글:힘내세요', '7394', '최진덕' , 0, '2017-08-12');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '네째글: ... ', '9090', '이혜원', 0, '2018-02-09');
INSERT INTO TEST_WRITE VALUES
(TEST_WRITE_SEQ.nextval, '다섯째글: 게시판', '7531', '박수찬', 0, sysdate);

-- 저장되어 있는 데이터 확인
SELECT * FROM test_write;

 

 

4. 해당 DB의 JDBC 라이브러리 설치(Dynamic Web Project)
 : WebContent > Web-INF > lib 폴더에 붙여 넣기(반드시 해당 작업 필요!)

 

 

5. JDBC 연동 순서
1) JDBC 드라이버 로딩

 : Class.forName("oracle.jdbc.driver.OracleDriver");

 

2) 데이터베이스 연결
 : DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "scott0316", "tiger0316");

 

3) Statement 객체나 PreparedStatement 객체 생성


4) SQL 쿼리 실행
 : executeQuery() ResulteSet 리턴, executeUpdate() int 리턴


5) ResulteSet 리턴한 경우, ResultSet 객체로부터 데이터 추출
 : row 관련 메소드 - next(), previous(), first(), last(), ...
   Column값 추출 메소드 - getString(), getInt(), ...


6) 데이터베이스 자원 반납
 : close()

 

** Connection은 페이지당(request 당) 한 번만 생성하면 충분함
** 하나의 Connection으로부터 복수 개의 Statement, PreparedStatement 생성하여 운용 가능
** 자원 반납 절대 잊지 말자!!

 

 

6. 트랜잭션 (transaction)
 : 데이터 조작 동작의 단위,
   하나의 데이터 조작 동작을 하기 위해 한개 뿐만 아니라, 여러 개의 쿼리가 수행될 수도 있다. 

 

 

7. 필요한 jsp 만들기

: db_test, write, writeOk, list, view, update, updateOk, deleteOk

 

 

8. DB 커넥션 테스트, JDBC 기본 코드 작성(앞으로 트랜젝션이 발생하는 모든 페이지의 기본 베이스가 될 예정)

** db_test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%-- JDBC 관련 import --%>
<%@ page import = "java.sql.*" %>
<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	// * 여기에 쿼리문을 작성해준다.
%>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		// * 여기에 트랜젝션 실행문을 기재한다.
		
	} catch(Exception e) {
		e.printStackTrace();
		// * 여기서 예외 처리를 하든지, 예외 페이지를 설정해줘야 한다.
		
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>

<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>

 

** 정상적으로 DB 커넥션 완료...!!

 

 

9. 폼 유효성 체크 (form validation) 필수!! 
 : 서버 측에 parameter를 넘기기 전에, 반드시 데이터의 유효성 검사 (validation)을 해야 한다. 

 

 

10. 글 생성

 : name(작성자)와 subject(글제목)는 필수 입력 사항, submit 하기 전에 입력여부 반드시 확인 필요하며

   [잊지말자!] 프론트엔드와 백엔드 모두 유효성 검사는 필수다!!!!

 

** write.jsp : form 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글작성</title>
</head>
<script>
function chkSubmit() {	// 폼 검증
	frm = document.forms["frm"];
	
	var name = frm["name"].value.trim();
	var subject = frm["subject"].value.trim();
	
	// 프론트 엔드에서 폼 유효성 체크, 검증하기
	if(name == "") {
		alert('작성자 란은 반드시 입력해야 합니다.');
		frm["name"].focus();
		return false;
	}
	
	if(subject == "") {
		alert('제목은 반드시 작성해야 합니다.');
		frm["subject"].focus();
		return false;
	}
	return true;
}
</script>
<body>
<h2>글작성</h2>
<%-- 글 내용이 많을수 있기 때문에 POST 방식 사용 --%>
<form name="frm" action="writeOk.jsp" method="post" onsubmit="return chkSubmit()">
작성자:
<input type="text" name="name"/><br>
제목:
<input type="text" name="subject"/><br>
내용:<br>
<textarea name="content"></textarea>
<br><br>
<input type="submit" value="등록"/>
</form>
<br>
<button type="button" onclick="location.href='list.jsp'">목록으로</button>
</body>
</html>


** writeOk.jsp : DB 등록

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%-- JDBC 관련 import --%>
<%@ page import = "java.sql.*"%>
    
<% 
	// post 방식은 한글 인코딩이 반드시 필요!(누락되지 않도록 조심하기!!)
	request.setCharacterEncoding("utf-8");

	// 입력한 값 받아오기
	String name = request.getParameter("name");
	String subject = request.getParameter("subject");
	String content = request.getParameter("content");
	
	// 백엔드에서 폼 유효성 체크, 검증하기
	// name, subject 는 비어 있으면 안된다(not null)
	// null 이거나 빈 문자열이면 이전 화면으로 돌아가기
	if(name == null || subject == null ||
		name.trim().equals("") || subject.trim().equals("")) {
%>
		<script>
			alert('작성자 이름, 글 제목은 필수입니다!');
			history.back();	// history.go(-1)
		</script>
<%
		return;	// ★ [매우 매우 중요!] 더이상 JSP 프로세싱 하지 않도록 종료 ★
	}
%>

<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	String sql_insert = "INSERT INTO test_write "
		+ "(wr_uid, wr_subject, wr_content, wr_name) "
		+ "VALUES(test_write_seq.nextval, ?, ?, ?)"
		;
%>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		pstmt = conn.prepareStatement(sql_insert);
		
		pstmt.setString(1, subject);
		pstmt.setString(2, content);
		pstmt.setString(3, name);
		
		cnt = pstmt.executeUpdate();
		
	} catch(Exception e) {
		e.printStackTrace();	// 에러를 콘솔창에서 보고 싶을때
		//throw e;	// 에러를 직접 웹 페이지에서 보고 싶을때
		// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
		
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>

<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
		<script>
			alert('등록 실패!!');
			history.back();	// 브라우저가 직전에 직전 페이지(입력중 페이지로)
		</script>
<% } else {%>
		<script>
			alert('등록 성공, 리스트를 출력합니다!');
			location.href = "list.jsp";
		</script>
<% } %>

 

 

11. 서버쪽 검증이 제대로 먹혔는지 확인하는 방법
 : postman 이용하기

 

 

12. 글 목록보기

 : 글 생성 후 목록 보기를 만드는 이유는 jsp별 기능을 만들고

   정상적으로 반영됬는지 확인하기 위해서 번거롭게 DB에 직접 검색을 해야함,

   이러한 번거로움을 덜기 위해 리스트를 먼저 만듦.


** list.sql

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>

<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	final String SQL_WRITE_SELECT 
		= "SELECT * FROM test_write ORDER BY wr_uid DESC";  
%>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글 목록</title>
<style>
table {width: 100%;}
table, th, td {
	border: 1px solid black;
	border-collapse: collapse;
}
</style>
</head>
<body>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		pstmt = conn.prepareStatement(SQL_WRITE_SELECT);
		
		rs = pstmt.executeQuery();
		out.println("쿼리 성공<br>");
%>
		<hr>
		<h2>리스트</h2>
		<table>
			<tr>
				<th>UID</th>
				<th>제목</th>
				<th>작성자</th>
				<th>조회수</th>
				<th>등록일</th>
			</tr>
<%
			while (rs.next()) {
				out.println("<tr>");
				int uid = rs.getInt("wr_uid");
				String subject = rs.getString("wr_subject");
				String name = rs.getString("wr_name");
				int viewcnt = rs.getInt("wr_viewcnt");
				Date d = rs.getDate("wr_regdate");		// java.sql
				Time t = rs.getTime("wr_regdate");		// java.sql
				
				String regdate = "";
				if(d != null) {
					regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
							+ new SimpleDateFormat("hh:mm:ss").format(t);
				}

				out.println("<td>" + uid + "</td>");
				out.println("<td><a href='view.jsp?uid=" + uid + "'>" + subject + "</a></td>");
				out.println("<td>" + name + "</td>");
				out.println("<td>" + viewcnt + "</td>");
				out.println("<td>" + regdate + "</td>");

				out.println("</tr>");
			} // end while
%>
		
		</table>
		<br>
		<button onclick="location.href='write.jsp'">신규등록</button>
<%
	} catch(Exception e) {
		e.printStackTrace();
		// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
		
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>

<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>

</body>
</html>

 

 

13. 글내용 읽기

 : View에서는 조회수 증가, 해당 글 내용 읽어오는 2가지 쿼리를 진행해야한다,

   기본적으로 JDBC 프로그래밍은 Auto-Commit 되어 있기 때문에

   여러 개의 쿼리로 트랜잭션 처리할 때는 Auto-Commit를 false 설정해야 한다.

   Auto-Commit false 설정 후 모든 트렌젝션이 완료되었을때 commit() 처리,

   하나라도 쿼리 실패시 rollback() 처리해야 한다.

 

** view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>

<%
	// parameter 받아 오기
	int uid = Integer.parseInt(request.getParameter("uid"));
	// * 이 단계에서 parameter 검증 필요하나 생략.
%>

<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	// 여러 개의 쿼리가 하나의 트랜젝션인 경우 Auto-Commit을 하면 안된다.
	// 둘 다 성공 후 commit 되어야 하며 
	// 하나라도 commit 되지 않으면 rollback 해야하는데
	// Auto-Commit인 경우는 무조건 쿼리가 하나 끝나면 자동 저장되기 때문이다
	final String SQL_WRITE_INC_VIEWCNT	// 조회수 증가
		= "UPDATE test_write SET wr_viewcnt = wr_viewcnt + 1 WHERE wr_uid = ?";
		
	final String SQL_WRITE_SELECT_BY_UID	// 글 읽어오기
		= "SELECT * FROM test_write WHERE wr_uid = ?";
%>

<%
	String name = "";
	String subject = "";
	String content = "";
	String regdate = "";
	int viewcnt = 0;
%>

<%
	try{
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜잭션 실행
		// ★[완전 중요!] 여기서 무조건 Auto-commit 비활성화해줘야함!!★
		conn.setAutoCommit(false);
		
		// 쿼리들 수행
		pstmt = conn.prepareStatement(SQL_WRITE_INC_VIEWCNT);
		pstmt.setInt(1, uid);
		cnt = pstmt.executeUpdate();
		
		pstmt.close();
		
		pstmt = conn.prepareStatement(SQL_WRITE_SELECT_BY_UID);
		pstmt.setInt(1, uid);
		rs = pstmt.executeQuery();
		
		// 한개의 레코드만 select 된다.
		if(rs.next()){
			subject = rs.getString("wr_subject");
			content = rs.getString("wr_content");
			if(content == null) content = "";   // null 처리 
			name = rs.getString("wr_name");
			viewcnt = rs.getInt("wr_viewcnt");
			Date d = rs.getDate("wr_regdate");
			Time t = rs.getTime("wr_regdate");
			regdate = "";
			if(d != null){
			 	regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " " 
						+ new SimpleDateFormat("hh:mm:ss").format(t);
			}
		} else {
			// wr_uid 값의 레코드가 없는 뜻
%>
			<script>
				alert("해당 정보가 삭제되거나 없습니다");
				history.back();
			</script>
<%			
			return;   // 더이상 JSP 프로세싱 하지 않고 종료
		} // end if
		
		// ★[완전 중요!] 반드시 모든 쿼리 성공하면 commit 처리★
		// Auto-commit 비활성화되어 있기 때문에
		// commit을 안하면 진행했던 쿼리들이 저장이 안됨..!!
		conn.commit();
		
	} catch(Exception e){
		e.printStackTrace();
		
		// 예외 처리
		// ★[완전 중요!] 반드시 모든 쿼리 성공하면 commit 처리해야함과 반대로
		// 하나라도 쿼리 실패하면 rollback 해야한다..!!★
		conn.rollback();

	} finally {
		// 리소스 해제
		try {
			if(rs != null) rs.close();
			if(stmt != null) stmt.close();
			if(pstmt != null) pstmt.close();
			if(conn != null) conn.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}
%>
<%-- 위 트랜잭션이 마무리 되면 페이지 보여주기 --%>    
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>읽기 <%= subject %></title> <!-- title에 글제목 넣기 -->
</head>
<script>
// script 는 위치 구애를 받지 않는다
function chkDelete(uid) {
	// 삭제 여부, 다시 확인하고 진행하기
	var r = confirm('삭제하시겠습니까?');
	
	if(r) {
		location.href = 'deleteOk.jsp?uid=' + uid;
	}
}
</script>
<body>
<body>
<h2>읽기 <%= subject %></h2>
<br>
UID : <%= uid %><br>
작성자 : <%= name %><br>
제목 : <%= subject %><br>
등록일 : <%= regdate %><br>
조회수 : <%= viewcnt %><br>
내용: <br>
<hr>
<div>
<%= content %>
</div>
<hr>
<br>
<button onclick="location.href='update.jsp?uid=<%= uid%>'">수정하기</button>
<button onclick="location.href = 'list.jsp'">목록보기</button>
<button onclick="chkDelete(<%= uid %>)">삭제하기</button>
<button onclick="location.href = 'write.jsp'">신규등록</button>

</body>
</html>

 

 

14. 글수정
 : 작성자 그대로 제목과 내용만 수정 가능할 수 있도록 만듦.
** update.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>    

<%  // parameter 받아오기
	int uid = Integer.parseInt(request.getParameter("uid"));
	// * 이 단계에서 parameter 검증이 필요하나 생략
%>
 
<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery() 
	int cnt = 0;   			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	final String SQL_WRITE_SELECT_BY_UID	// 글 읽어 오기
		= "SELECT * FROM test_write WHERE wr_uid = ?";
%>

<%
	String name = "";
	String subject = "";
	String content = "";
	String regdate = "";
	int viewcnt = 0;
%>

<%
	try{
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜잭션 실행
		pstmt = conn.prepareStatement(SQL_WRITE_SELECT_BY_UID);
		pstmt.setInt(1, uid);
		rs = pstmt.executeQuery();
		
		// 한개의 레코드만 select 된다.
		if(rs.next()){
			subject = rs.getString("wr_subject");
			content = rs.getString("wr_content");
			if(content == null) content = "";   // null 처리 
			name = rs.getString("wr_name");
			viewcnt = rs.getInt("wr_viewcnt");
			Date d = rs.getDate("wr_regdate");
			Time t = rs.getTime("wr_regdate");
			regdate = "";
			if(d != null){
			 	regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " " 
						+ new SimpleDateFormat("hh:mm:ss").format(t);
			}
		} else {
			// wr_uid 값의 레코드가 없는 뜻
%>
			<script>
				alert("해당 정보가 삭제되거나 없습니다");
				history.back();
			</script>
<%			
			return;   // 더이상 JSP 프로세싱 하지 않고 종료
		} // end if
		
	} catch(Exception e){
		e.printStackTrace();
		// 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함

	} finally {
		// 리소스 해제
		try {
			if(rs != null) rs.close();
			if(stmt != null) stmt.close();
			if(pstmt != null) pstmt.close();
			if(conn != null) conn.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}
%>
<%-- 위 트랜잭션이 마무리 되면 페이지 보여주기 --%>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>수정 <%= subject %></title>
</head>
<script>
function chkSubmit(){
	frm = document.forms['frm'];
	var subject = frm['subject'].value.trim();
	
	if(subject == ""){
		alert("제목은 반드시 작성해야 합니다");
		frm['subject'].focus();
		return false;
	}
	return true;
}
</script>
<body>
<h2>수정</h2>
<form name="frm" action="updateOk.jsp" method="post" onsubmit="return chkSubmit()">
<input type="hidden" name="uid" value="<%= uid %>"/>
작성자 : <%= name %><br> <%-- 작성자 이름 변경 불가 --%>
제목 : 
<input type="text" name="subject" value="<%= subject %>"/><br>
내용: <br>
<textarea name="content"><%= content %></textarea>
<br>
<input type="submit" value="수정"/>
</form>

<button onclick="history.back()">이전으로</button>
<button onclick="location.href='list.jsp'">목록보기</button>
</body>
</html>


** updateOk.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ page import = "java.sql.*" %>

<%
	// parameter 받아오기
	request.setCharacterEncoding("utf-8");	// 한글 인코딩 반드시!
	
	// * 이 단계에서 parameter 검증 필요하나 생략.
	
	int uid = Integer.parseInt(request.getParameter("uid"));
	String subject = request.getParameter("subject");
	String content = request.getParameter("content");
	
%>
<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	final String SQL_WRITE_UPDATE
		= "UPDATE test_write SET wr_subject = ?, wr_content = ? WHERE wr_uid = ?";
%>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		pstmt = conn.prepareStatement(SQL_WRITE_UPDATE);
		pstmt.setString(1, subject);
		pstmt.setString(2, content);
		pstmt.setInt(3, uid);
		
		cnt = pstmt.executeUpdate();
		
	} catch(Exception e) {
		e.printStackTrace();
		// 예외 처리
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>

<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
		<script>
			alert('수정 실패');
			history.back();
		</script>
<% } else { %>
		<script>
			alert('수정 성공');
			location.href = "view.jsp?uid=<%= uid %>";
		</script>
<% } %>

 

 

15. 글 삭제
** deleteOk.jsp

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

<%
	// parameter 받아오기
	int uid = Integer.parseInt(request.getParameter("uid"));
%>
<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	final String SQL_WRITE_DELETE_BY_UID
		= "DELETE FROM test_write WHERE wr_uid = ?";
%>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		pstmt = conn.prepareStatement(SQL_WRITE_DELETE_BY_UID);
		pstmt.setInt(1, uid);
		
		cnt = pstmt.executeUpdate();
		
	} catch(Exception e) {
		e.printStackTrace();
		// 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
		
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>

<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>
<% if(cnt == 0) { %>
		<script>
			alert('삭제 실패');
			history.back();
		</script>
<% } else { %>
		<script>
			alert('삭제 성공');
			location.href = "list.jsp";	<%-- 삭제후에는 list 로 가자 --%>
		</script>
<% } %>

 

 

16. 페이지 코드 작성 흐름
1) Parameter 받아오기, 유효성 검증

 

2) 페이지에 필요한 데이터 처리, 트랜잭션

 

3) 결과 출력 (HTML 작성)

 

 

[Pagination (페이징)]

1. 페이징(Pagination) 구현시 고려할 요소
1) 한 페이지(Page)당 몇 개의 글 목록을 나타낼 것인가?

 : SQL 함수 count(*) 사용

2) 한 페이징(Pagination)당 몇 개의 페이지(Page)를 표현할 것인가?

 : 몇 번째(fromRow)부터 몇 개(pageRows)를 SELECT할 것인가?
   > SQL ROWNUM 사용

 

 

2. Pagination(페이징) 구현을 위해 많은 데이터가 필요, 개수 늘리기

-- 저장되어 있는 데이터 확인
SELECT * FROM test_write;

-- 다량의 데이터 필요
SELECT * FROM test_write ORDER BY wr_uid DESC;
INSERT INTO test_write(wr_uid, wr_subject, wr_content, wr_name)
	SELECT test_write_seq.nextval, wr_subject, wr_content, wr_name FROM test_write;

 

 

3. ROWNUM 
 : 몇 번째(fromRow)부터 몇 개(pageRows)을 구현하기 위해서는 

   SELECT로 추출하는 ROW들에게 순서가 필요,

   오라클에서는 ROWNUM 객체를 이용하면 Row들에게 순서대로 번호를 줄 수 있음

-- ROWNUM 출력
SELECT ROWNUM, wr_name FROM test_write;

-- 페이지당 1부터 5 추출
SELECT ROWNUM, wr_name FROM test_write
	WHERE ROWNUM >= 1 AND ROWNUM < 1 + 5;

 

 

4. list.jsp에서 pagination.jsp를 include하는 구조

** list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ page import = "java.sql.*" %>
<%@ page import = "java.text.SimpleDateFormat" %>

<%
	int curPage = 1;	// 현재 페이지 (디폴트 1 page)
	
	// 현재 몇 페이지인지 parameter 받아 오기  + 검증
	String pageParam = request.getParameter("page");
	if(pageParam != null && !pageParam.trim().equals("")) {
		try {
			// 1이상의 자연수 이어야 한다
			int p = Integer.parseInt(pageParam);
			if(p > 0) {curPage = p;}
		} catch (NumberFormatException e) {
			// page parameter 오류는 별도의 exception 처리 안함 
		}
	}
	
%>

<%!
	// JDBC 관련 기본 객체변수
	Connection conn = null;
	Statement stmt = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	// SELECT 결과, executeQuery()
	int cnt = 0;			// DML 결과, executeUpdate()
	
	// Connection 에 필요한 값 세팅
	final String DRIVER = "oracle.jdbc.driver.OracleDriver";
	final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
	final String USERID = "scott0316";
	final String USERPW = "tiger0316";
%>

<%!
	// 쿼리문 준비
	//final String SQL_WRITE_SELECT 
	//	= "SELECT * FROM test_write ORDER BY wr_uid DESC";

	//페이징
	// 글 목록 전체 개수 가져오기
	final String SQL_WRITE_COUNT_ALL = "SELECT count(*) FROM test_write";
	
	// fromRow 부터 pageRows 만큼 SELECT
	// (몇번째) 부터 (몇개) 만큼
	final String SQL_WRITE_SELECT_FROM_ROW =  "SELECT * FROM " + 
			"(SELECT ROWNUM AS RNUM, T.* FROM (SELECT * FROM test_write ORDER BY wr_uid DESC) T) " + 
			"WHERE RNUM >= ? AND RNUM < ?";
	
	// 페이징 관련 세팅 값들
	int writePages = 10;	// 한 [페이징] 에 몇개의 '페이지' 를 표현할 것인가?
	int pageRows = 8;		// 한 '페이지' 에 몇개의 글을 리스트업 할 것인가?
	int totalPage = 0;		// 총 몇 '페이지' 분량인가?
%>

<%
	try {
		Class.forName(DRIVER);
		out.println("드라이버 로딩 성공" + "<br>");
		conn = DriverManager.getConnection(URL, USERID, USERPW);
		out.println("conn 성공" + "<br>");
		
		// 트랜젝션 실행
		pstmt = conn.prepareStatement(SQL_WRITE_COUNT_ALL);
		rs = pstmt.executeQuery();
		
		if(rs.next()) {
			cnt = rs.getInt(1);	// count(*)
		}
		
		rs = pstmt.executeQuery();
		pstmt.close();
		
		totalPage = (int)Math.ceil(cnt / (double)pageRows);	// 총 몇페이지 분량
		
		int fromRow = (curPage - 1) * pageRows + 1;	// 몇 번째 row 부터?
		
		pstmt = conn.prepareStatement(SQL_WRITE_SELECT_FROM_ROW);
		pstmt.setInt(1, fromRow);
		pstmt.setInt(2, fromRow + pageRows);
		rs = pstmt.executeQuery();
		
		//out.println("쿼리 성공<br>");
%>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>글 목록 <%= curPage %>페이지</title>
<style>
table {width: 100%;}
table, th, td {
	border: 1px solid black;
	border-collapse: collapse;
}
</style>

<!-- 페이징 -->
<link rel="stylesheet" type="text/css" href="CSS/common.css"/>
<script src="https://kit.fontawesome.com/bb29575d31.js"></script>

</head>
<body>
		<hr>
		<h2>리스트 <%= curPage %>페이지</h2>
		<h4><%= cnt %>개</h4> <!-- 전체 글 개수 -->
		<table>
			<tr>
				<th>row</th> <!-- row 번호 -->
				<th>UID</th>
				<th>제목</th>
				<th>작성자</th>
				<th>조회수</th>
				<th>등록일</th>
			</tr>
<%
			while (rs.next()) {
				out.println("<tr>");
				
				int rnum = rs.getInt("rnum");	// rownum 받아오기
				
				int uid = rs.getInt("wr_uid");
				String subject = rs.getString("wr_subject");
				String name = rs.getString("wr_name");
				int viewcnt = rs.getInt("wr_viewcnt");
				Date d = rs.getDate("wr_regdate");		// java.sql
				Time t = rs.getTime("wr_regdate");		// java.sql
				
				String regdate = "";
				if(d != null) {
					regdate = new SimpleDateFormat("yyyy-MM-dd").format(d) + " "
							+ new SimpleDateFormat("hh:mm:ss").format(t);
				}

				out.println("<td>" + rnum + "</td>");  // rownum 찍어주기
				out.println("<td>" + uid + "</td>");
				out.println("<td><a href='view.jsp?uid=" + uid + "&page=" + curPage + "'>" + subject + "</a></td>");
				out.println("<td>" + name + "</td>");
				out.println("<td>" + viewcnt + "</td>");
				out.println("<td>" + regdate + "</td>");

				out.println("</tr>");
			} // end while
%>
		
		</table>
		<br>
		<button onclick="location.href='write.jsp'">신규등록</button>
<%
	} catch(Exception e) {
		e.printStackTrace();
		// * 여기서 예외 처리, 콘솔창이나 에러 페이지를 통해 에러 확인이 가능함
		
	} finally {
		// 리소스 해제
		try {
			if(rs != null) {rs.close();}
			if(stmt != null) {stmt.close();}
			if(pstmt != null) {pstmt.close();}
			if(conn != null) {conn.close();}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
%>
<%-- 위 트랜젝션이 마무리되면 페이지 보여주기 --%>

<%-- 페이지 --%>
<%-- 페이징 --%>
<jsp:include page="pagination.jsp">
	<jsp:param value="<%= writePages %>" name="writePages"/>
	<jsp:param value="<%= totalPage %>" name="totalPage"/>
	<jsp:param value="<%= curPage %>" name="curPage"/>
</jsp:include>

</body>
</html>

 

** pagination.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 한페이징에 표시될 페이지수 --> writePages
	// 총 페이지수 --> totalPage
	// 현재 페이지 --> curPage
	int writePages = Integer.parseInt(request.getParameter("writePages"));
	int totalPage = Integer.parseInt(request.getParameter("totalPage"));
	int curPage = Integer.parseInt(request.getParameter("curPage"));
	// ※ 사실 위 단계에서도 파라미터 검증 필요하다
	
	// 위 url에 추가로 붙어야 할 것들.  (옵션)
	String add = request.getParameter("add"); 
	if(add == null){ add = ""; }
	
	// 페이징 버튼 링크 url 주소에 넣을 문자열 준비
	String url = request.getRequestURL().toString() + "?page=";
	
	String str = "";   // 최종적으로 페이징에 나타날 HTML 문자열 <li> 태그로 구성

	// 페이징에 보여질 숫자들 (시작숫자 start_page ~ 끝숫자 end_page)
    int start_page = ( ( (int)( (curPage - 1 ) / writePages ) ) * writePages ) + 1;
    int end_page = start_page + writePages - 1;

    if (end_page >= totalPage){
    	end_page = totalPage;
    }
	
	//■ << 표시 여부
	if(curPage > 1){
		str += "<li><a href='" + url + "1" +  add + "' class='tooltip-top' title='처음'><i class='fa fa-angle-double-left'></i></a></li>\n";
	}
	
  	//■  < 표시 여부
    if (start_page > 1) 
    	str += "<li><a href='" + url + (start_page - 1) + add + "' class='tooltip-top' title='이전'><i class='fa fa-angle-left'></i></a></li>\n";
    
    //■  페이징 안의 '숫자' 표시	
	if (totalPage > 1) {
	    for (int k = start_page; k <= end_page; k++) {
	        if (curPage != k)
	            str += "<li><a href='" + url + k + add + "'>" + k + "</a></li>\n";
	        else
	            str += "<li><a href='#' class='active tooltip-top' title='현재페이지'>" + k + "</a></li>\n";
	    }
	}
	
	//■ > 표시
    if (totalPage > end_page){
    	str += "<li><a href='" + url + (end_page + 1) + add + "' class='tooltip-top' title='다음'><i class='fa fa-angle-right'></i></a></li>\n";
    }

	//■ >> 표시
    if (curPage < totalPage) {
        str += "<li><a href='" + url + totalPage + add + "' class='tooltip-top' title='맨끝'><i class='fa fa-angle-double-right'></i></a></li>\n";
    }

%>
<!-- top nav -->
<div class="center">
	<ul class="pagination">
	<%= str %>
	</ul>
</div>

 

 

5. CSS 추가

** WebContent > 01page > CSS > common.css

@charset "UTF-8";
.center {
    text-align: center;
}

ul.pagination{
	list-style-type:none
}

ul.pagination li{
    display: inline-block;
}

ul.pagination a {
    color: black;
    float: left;
    padding: 4px 8px;
    text-decoration: none;
    transition: background-color .3s;
    /* border: 1px solid #ddd; */
    /* margin: 0 4px; */
    margin: 0px;
}

ul.pagination a.active {
    background-color: #4CAF50;
    color: white;
    border: 1px solid #4CAF50;
}

ul.pagination a:hover:not(.active) {background-color: #ddd;}

 

 

6. 세 가지 정보가 pagination에 넘어가는 것이 중요!
1) writePages : 한 페이징(pagination)에 몇 개의 페이지(page)가 표시되나?

 

2) totalPage : 총 몇개의 페이지(page)인가?

 

3) curPage : 현재 페이지는 몇 페이지(page)인가?

'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글

2020.05.25  (0) 2020.05.25
2020.05.22  (0) 2020.05.22
2020.05.20  (0) 2020.05.20
2020.05.19  (0) 2020.05.19
2020.05.18  (0) 2020.05.18