[JSP] 18. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (2)
2부에서는 페이징, DB구현부, 모델, 서비스 영역에 대해서 소개하겠다.
1. [JSP] 18. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (1)
14. 페이징네이션
실제경로: /target/generated-sources/annotations/com/smile/web/model
패키지명: com.smile.web.logic
파일명: Paging.java
아마 이거 하나면, 현존에 있는 페이지 기법은 충분히 소화되지 않겠냐는 생각이다.
물론 정답은 아니다. 오류가 있을 수도 있다.
이러한 복잡한 문제에 직면했을 때, 수학을 공부 많이 해야 한다고 생각한다.
머리가 좋으신 분들이 많으시니깐 더 좋은 방법이 있다면, 개선해봐도 좋을 듯 싶다.
package com.smile.web.logic;
public class Paging {
private long pageSize; // 게시 글 수
private long firstPageNo; // 첫 번째 페이지 번호
private long prevPageNo; // 이전 페이지 번호
private long startPageNo; // 시작 페이지 (페이징 네비 기준)
private long pageNo; // 페이지 번호
private long endPageNo; // 끝 페이지 (페이징 네비 기준)
private long nextPageNo; // 다음 페이지 번호
private long finalPageNo; // 마지막 페이지 번호
private long totalCount; // 게시 글 전체 수
private long dbStartNum; // db 조회 (시작번호)
private long dbEndNum; // db 조회 (종료번호)
/**
* @return the pageSize
*/
public long getPageSize() {
return pageSize;
}
/**
* @param pageSize the pageSize to set
*/
public void setPageSize(long pageSize) {
this.pageSize = pageSize;
}
/**
* @return the firstPageNo
*/
public long getFirstPageNo() {
return firstPageNo;
}
/**
* @param firstPageNo the firstPageNo to set
*/
public void setFirstPageNo(long firstPageNo) {
this.firstPageNo = firstPageNo;
}
/**
* @return the prevPageNo
*/
public long getPrevPageNo() {
return prevPageNo;
}
/**
* @param prevPageNo the prevPageNo to set
*/
public void setPrevPageNo(long prevPageNo) {
this.prevPageNo = prevPageNo;
}
/**
* @return the startPageNo
*/
public long getStartPageNo() {
return startPageNo;
}
/**
* @param startPageNo the startPageNo to set
*/
public void setStartPageNo(long startPageNo) {
this.startPageNo = startPageNo;
}
/**
* @return the pageNo
*/
public long getPageNo() {
return pageNo;
}
/**
* @param pageNo the pageNo to set
*/
public void setPageNo(long pageNo) {
this.pageNo = pageNo;
}
/**
* @return the endPageNo
*/
public long getEndPageNo() {
return endPageNo;
}
/**
* @param endPageNo the endPageNo to set
*/
public void setEndPageNo(long endPageNo) {
this.endPageNo = endPageNo;
}
/**
* @return the nextPageNo
*/
public long getNextPageNo() {
return nextPageNo;
}
/**
* @param nextPageNo the nextPageNo to set
*/
public void setNextPageNo(long nextPageNo) {
this.nextPageNo = nextPageNo;
}
/**
* @return the finalPageNo
*/
public long getFinalPageNo() {
return finalPageNo;
}
/**
* @param finalPageNo the finalPageNo to set
*/
public void setFinalPageNo(long finalPageNo) {
this.finalPageNo = finalPageNo;
}
/**
* @return the totalCount
*/
public long getTotalCount() {
return totalCount;
}
/**
* @param totalCount the totalCount to set
*/
public void setTotalCount(long totalCount) {
this.totalCount = totalCount;
this.makePaging();
this.makeDbPaging();
}
/**
* 페이징 생성 (setTotalCount에서 호출함)
*/
private void makePaging() {
if (this.totalCount == 0)
return;
// 게시 글 전체 수가 없는 경우
if (this.pageNo == 0)
this.setPageNo(1); // 기본 값 설정
if (this.pageSize == 0)
this.setPageSize(10); // 기본 값 설정
long finalPage = (totalCount + (pageSize - 1)) / pageSize; // 마지막 페이지
if (this.pageNo > finalPage)
this.setPageNo(finalPage); // 기본 값 설정
if (this.pageNo < 0 || this.pageNo > finalPage)
this.pageNo = 1; // 현재 페이지 유효성 체크
boolean isNowFirst = pageNo == 1 ? true : false; // 시작 페이지 (전체)
boolean isNowFinal = pageNo == finalPage ? true : false; // 마지막 페이지 (전체)
long startPage = ((pageNo - 1) / 10) * 10 + 1; // 시작 페이지 (페이징 네비 기준)
long endPage = startPage + 10 - 1; // 끝 페이지 (페이징 네비 기준)
if (endPage > finalPage) { // [마지막 페이지 (페이징 네비 기준) > 마지막 페이지] 보다 큰 경우
endPage = finalPage;
}
this.setFirstPageNo(1); // 첫 번째 페이지 번호
if (isNowFirst) {
this.setPrevPageNo(1); // 이전 페이지 번호
} else {
this.setPrevPageNo(((pageNo - 1) < 1 ? 1 : (pageNo - 1))); // 이전 페이지 번호
}
this.setStartPageNo(startPage); // 시작 페이지 (페이징 네비 기준)
this.setEndPageNo(endPage); // 끝 페이지 (페이징 네비 기준)
if (isNowFinal) {
this.setNextPageNo(finalPage); // 다음 페이지 번호
} else {
this.setNextPageNo(((pageNo + 1) > finalPage ? finalPage : (pageNo + 1))); // 다음 페이지 번호
}
this.setFinalPageNo(finalPage); // 마지막 페이지 번호
}
private void makeDbPaging() {
long current = this.getPageNo();
long weight = this.getPageSize();
long endNum = 0;
this.setDbEndNum( current * weight );
endNum = this.getDbEndNum();
this.setDbStartNum( (endNum - weight) + 1 );
}
/**
* @return the dbStartNum
*/
public long getDbStartNum() {
return dbStartNum;
}
/**
* @param dbStartNum the dbStartNum to set
*/
public void setDbStartNum(long dbStartNum) {
this.dbStartNum = dbStartNum;
}
/**
* @return dbEndNum
*/
public long getDbEndNum() {
return dbEndNum;
}
/**
* @param dbEndNum the dbEndNum to set
*/
public void setDbEndNum(long dbEndNum) {
this.dbEndNum = dbEndNum;
}
}
파일명: Paging.java
[첨부(Attachments)]
실제경로: /target/generated-sources/annotations/com/smile/web/model
패키지명: com.smile.web.model
파일명: Board.java
게시판 모델에 대한 설계이다.
package com.smile.web.model;
import java.sql.Timestamp;
public class Board {
private long id;
private String name;
private String subject;
private String memo;
private long count;
private Timestamp regidate;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public Timestamp getRegidate() {
return regidate;
}
public void setRegidate(Timestamp regidate) {
this.regidate = regidate;
}
}
파일명: Board.java
[첨부(Attachments)]
16. BoardService.java - Service 영역
실제경로: /target/generated-sources/annotations/com/smile/web/service
패키지명: com.smile.web.service
게시판 서비스에 필요한 영역을 구현한 것이다.
package com.smile.web.service;
import java.util.List;
import com.smile.web.model.Board;
public class BoardService {
private static BoardService service = null;
private static BoardServiceImpl dao = null;
private BoardService() {}
public static BoardService getInstance() {
if(service == null){
service = new BoardService();
dao = BoardServiceImpl.getInstatnce();
}
return service;
}
public List<Board> getBoardList(long startNum, long endNum){
return dao.getBoardList(startNum, endNum);
}
public long getTotalCount() {
return dao.getTotalCount();
}
public List<Board> getBoardKeywordList(String keyword, long startNum, long endNum){
return dao.getBoardKeywordList(keyword, startNum, endNum);
}
public long getTotalKeywordCount(String keyword) {
return dao.getTotalKeywordCount(keyword);
}
}
파일명: BoardService.java
[첨부(Attachments)]
16. BoardServiceImpl.java - Service 영역(DAO)
DAO 영역에 관한 것이다. (실제 DB를 처리하는 영역)
package com.smile.web.service;
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 com.smile.web.db.DBFactory;
import com.smile.web.model.Board;
public class BoardServiceImpl {
private static BoardServiceImpl boardDAO;
private static DBFactory dbPool;
private BoardServiceImpl() {}
public static BoardServiceImpl getInstatnce() {
if ( boardDAO == null ) {
boardDAO = new BoardServiceImpl();
dbPool = new DBFactory();
}
return boardDAO;
}
public List<Board> getBoardList(long startNum, long endNum){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Board node = null;
List<Board> boardList = new ArrayList<Board>();
String sql = "select * from (select /*+INDEX_DESC(tbl_board pk_board) */ rownum rn, A.*" +
" from board A order by id desc) " +
"where rn >= ? and rn <= ?";
//System.out.println(sql);
//System.out.println(startNum + "/" + endNum);
try {
conn = dbPool.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, startNum);
pstmt.setLong(2, endNum);
rs = pstmt.executeQuery();
while ( rs.next() ) {
// 데이터가 존재할 때, 노드 생성
node = new Board();
node.setId(rs.getLong(2));
node.setSubject(rs.getNString(3));
node.setMemo(rs.getNString(4));
node.setName(rs.getNString(5));
boardList.add(node);
System.out.println("rs:" + rs.getLong(1));
}
}catch(Exception ex) {
System.out.println("오류 발생: " + ex);
}
finally {
dbPool.close(conn, pstmt, rs);
}
return boardList;
}
public List<Board> getBoardKeywordList(String keyword, long startNum, long endNum){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Board node = null;
String allKeyword = "%" + keyword + "%";
List<Board> boardList = new ArrayList<Board>();
String sql = "select * " +
"from (select /*+INDEX_DESC(tbl_board pk_board) */ rownum rn, A.* " +
"from board A where subject like ? order by id desc) " +
"where rn >= ? and rn <= ?";
//System.out.println(sql);
try {
conn = dbPool.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setNString(1, allKeyword);
pstmt.setLong(2, startNum);
pstmt.setLong(3, endNum);
rs = pstmt.executeQuery();
while ( rs.next() ) {
// 데이터가 존재할 때, 노드 생성
node = new Board();
node.setId(rs.getLong(2));
node.setSubject(rs.getNString(3));
node.setMemo(rs.getNString(4));
node.setName(rs.getNString(5));
boardList.add(node);
//System.out.println("rs:" + rs.getLong(1));
}
}catch(Exception ex) {
System.out.println("오류 발생: " + ex);
}
finally {
dbPool.close(conn, pstmt, rs);
}
return boardList;
}
public long getTotalCount() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Board node = null;
long cnt = 0;
String sql = "select count(*) from board";
System.out.println("sql:" + sql);
try {
conn = dbPool.getConnection();
//System.out.println( conn.getSchema() );
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
//System.out.println( rs.getFetchSize() );
if ( rs.next() ) {
cnt = rs.getLong(1);
System.out.println("전체갯수:" + cnt + "/" + rs.getLong(1));
}
}catch(Exception ex) {
System.out.println("오류 발생: " + ex);
}
finally {
dbPool.close(conn, pstmt, rs);
}
return cnt;
}
public long getTotalKeywordCount(String keyword) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Board node = null;
String allKeyword = "%" + keyword + "%";
long cnt = 0;
String sql = "select count(*) from board where subject like ?";
//System.out.println("sql:" + sql + "/키:" + keyword);
try {
conn = dbPool.getConnection();
/// System.out.println( conn.getSchema() );
pstmt = conn.prepareStatement(sql);
pstmt.setNString(1, allKeyword);
rs = pstmt.executeQuery();
/// System.out.println( rs.getFetchSize() );
if ( rs.next() ) {
cnt = rs.getLong(1);
System.out.println("특정 갯수:" + cnt + "/" + rs.getLong(1));
}
}catch(Exception ex) {
System.out.println("오류 발생: " + ex);
}
finally {
dbPool.close(conn, pstmt, rs);
}
return cnt;
}
}
파일명: BoardServiceImpl.java
[첨부(Attachments)]
이 부분이 의외로 어려울 수 있다고 본다. 머리를 조금 써야 한다.
[개선 후 SQL 파일 내용]
-- Oracle 11 - 자동번호 생성 테이블 정의
-- Table 생성 (BOARD)
-- NEW.ID (Table의 id를 가리킴)
CREATE TABLE board
(
id NUMBER PRIMARY KEY,
name VARCHAR2(30),
subject VARCHAR2(30),
memo NCLOB,
count NUMBER,
regidate DATE
);
-- Sequence 정의
CREATE SEQUENCE board_sequence
START WITH 1
INCREMENT BY 1;
-- Trigger 생성
-- BEFORE INSERT on '테이블명'
CREATE OR REPLACE TRIGGER board_trigger
BEFORE INSERT
ON board
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT board_sequence.nextval INTO :NEW.ID FROM dual;
END;
/* 데이터 추가 */
INSERT INTO board (name, subject, memo, count, regidate) VALUES ('홍길동', '안녕하세요.', '메모메모', '0', '2020-09-29 11:11:00');
/* 데이터 등록 후 커밋할 것(대량 정보 처리 후) */
COMMIT;
-- 싱글 쿼리 (페이징)
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from board order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1
-- 특정 싱글 쿼리 SQL
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from board where subject like '%야해해%' order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1
파일명: board-tbl-oracle_개선후.sql (변경해도 되는 쿼리) / board-tbl-oracle_개선전.sql (이 글의 16번 소스코드에 적용된 페이징 쿼리)
(16번 파일의 페이징 쿼리를 수정해서 사용해도 됨.)
-> 개선 전 쿼리라고 해서 파일을 두 개로 두었음.
[첨부(Attachments)]
board-tbl-oracle.zip (업데이트: 2020-10-11)
페이징 로직 설계가 되었다면, 다음 계획해야 할 작업이 실질적으로는 이 작업이다.
의외로 작업을 하다보면, 골치가 아픈 문제들이 많이 생긴다.
[Oracle Databases 코너에 쿼리 관련 작업에 대해서 추가로 보충하여 소개하도록 하겠다.]
- 단일 테이블 게시물 쿼리 방법, 뷰 테이블 쿼리 방법, 테이블 조인(2개 이상) 사용 방법
-> 태스트 결과로는 "Java 페이징 로직"은 크게 문제가 없다.
다만, 나머지 부분은 쿼리라고 본다.
css 하나 만들어준다. 보안 영역에 만들지 말고 외부 영역에 만들어준다.
그림 23. style.css 프로젝트 위치
생성할 폴더:
/src/main/webapp/board
/src/main/webapp/board/css
생성할 파일
/src/main/web/app/board/css/style.css
사실 style.css을 먼저 만들고 나서 작업하는 건 절대적으로 아니다.
웹 페이지를 보고 하나 하나 왕복 화면 전환 등 복합적으로 하면서 수많은 태스트과정을 거쳐서 작성하게 된다.
css 작업을 하는데 있어서도 어떻게 하면 깔끔하면서 표준을 지킬지 고민을 많이 해봐야 한다고 본다.
/* 1. 제목 */
h3{
font-size:20px;
font-family:'Nanum Gothic';
text-align:center;
}
/* 2. 게시판 */
/* 게시판 목록 출력 */
.board_list{
border-top:1px solid #e2e2e2;
border-bottom:1px solid #e2e2e2;
font-size:12px;
font-family:'Nanum Gothic';
width:900px;
margin:auto;
text-align:center;
}
/* 제목 */
.board_list th{
border-right:1px solid #666;
border-bottom:1px solid #666;
font-size:15px;
font-family:'Nanum Gothic';
height:20px;
text-align:center;
background-color:#eeeeee;
}
/* 내용 */
.board_list td{
border-right:1px solid #e2e2e2;
font-size:15px;
font-family:'Nanum Gothic';
height:20px;
text-align:center;
}
/* 3. 페이징 네이션 */
.paginate{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
}
.paginate .first{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
margin-right:10px;
}
.paginate .prev{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
margin-right:10px;
}
.paginate .next{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
margin-right:10px;
}
.paginate .last{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
margin-right:10px;
}
.paginate .choice{
font-size:20px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
margin-right:10px;
}
/* 4. 링크 */
a{
text-decoration:none;
color:#666;
}
/* 5. 검색 영역 */
.searchArea{
font-size:15px;
font-family:'Nanum Gothic';
font-color:#666;
text-align: center;
}
파일명: style.css
[첨부(Attachments)]
이번에 소개할 것은 include를 실제 적용한 코드이다.
그림 24. board 폴더 내 파일들 (작업할 부분)
생성할 폴더:
/src/main/webapp/WEB-INF/views/
/src/main/webapp/WEB-INF/views/board
생성할 파일
/src/main/webapp/WEB-INF/views/board/list.jsp
/src/main/webapp/WEB-INF/views/board/search.jsp
/src/main/webapp/WEB-INF/views/board/paging.jsp
list.jsp를 호출하면, search.jsp, paging.jsp도 함께 호출이 된다.
기능을 전문적으로 분리하여 설계한 것이다.
list.jsp에 관한 것이다.
코드를 보고 한눈에 알 수 없는 사람들을 위해서 실제 동작 결과화면을 소개하겠다.
그림 25. 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" %>
<%@ page import = "java.util.*" %>
<%@ page import = "com.smile.web.model.*" %>
<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시물 목록</title>
<style>
@import url('https://fonts.googleapis.com/css?family=Nanum+Gothic:400,700,800');
</style>
<link href="css/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<%
List<Board> boardList = (List<Board>)request.getAttribute("list");
%>
<h3>게시물 목록</h3>
<!-- 목록 출력 -->
<table class="board_list">
<tr>
<th style="width:15%;">
번호(Num)
</th>
<th>
제목(Subject)
</th>
<th style="width:13%;">
글쓴이(Author)
</th>
<th style="width:13%; border-right:none;">
조회수(Count)
</th>
</tr>
<%
for(Board board:boardList){
%>
<tr>
<td style="width:15%;">
<%= board.getId() %>
</td>
<td>
<%= board.getSubject() %>
</td>
<td style="width:13%;">
<%= board.getName() %>
</td>
<td style="width:13%; border-right:none;">
<%= board.getCount() %>
</td>
</tr>
<%
}
%>
</table>
<!-- 페이징 -->
<jsp:include page="/WEB-INF/views/board/paging.jsp">
<jsp:param name="customURL" value="${pagingUrl}" />
<jsp:param name="firstPageNo" value="${paging.firstPageNo}" />
<jsp:param name="prevPageNo" value="${paging.prevPageNo}" />
<jsp:param name="startPageNo" value="${paging.startPageNo}" />
<jsp:param name="pageNo" value="${paging.pageNo}" />
<jsp:param name="endPageNo" value="${paging.endPageNo}" />
<jsp:param name="nextPageNo" value="${paging.nextPageNo}" />
<jsp:param name="finalPageNo" value="${paging.finalPageNo}" />
</jsp:include>
<!-- 검색 -->
<jsp:include page="/WEB-INF/views/board/search.jsp" />
</body>
파일명: list.jsp
[첨부(Attachments)]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="paginate">
<a href="${param.customURL}page=${param.firstPageNo}" class="first">처음 페이지</a>
<a href="${param.customURL}page=${param.prevPageNo}" class="prev">이전 페이지</a>
<span>
<c:forEach var="i" begin="${param.startPageNo}" end="${param.endPageNo}" step="1">
<c:choose>
<c:when test="${i eq param.pageNo}"><a href="${param.customURL}page=${i}" class="choice">${i}</a></c:when>
<c:otherwise><a href="${param.customURL}page=${i}">${i}</a></c:otherwise>
</c:choose>
</c:forEach>
</span>
<a href="${param.customURL}page=${param.nextPageNo}" class="next">다음 페이지</a>
<a href="${param.customURL}page=${param.finalPageNo}" class="last">마지막 페이지</a>
</div>
파일명: paging.jsp
[첨부(Attachments)]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="searchArea">
<form id="searchForm" action="list.do" method='get'>
<select name="type">
<option value="T">제목</option>
<option value="C">내용</option>
<option value="W">작성자</option>
</select>
<input type="text" name="keyword">
<button class="">검색</button>
</form>
</div>
파일명: search.jsp
[첨부(Attachments)]
23. 데이터베이스 작업시 성능 - 꼭 파악하면서 작업해보기
오늘 날 게시판 뿐만 아니라 DB의 비중이 매우 중요한 시대에 직면해 있다.
성능 측정 꼭 해보기 바란다.
동작도 물론 중요한데, 결과는 정확한지 등 많이 고민하고 수 십번, 수 백법 이상 찍어봐야 한다.
그림 26. 데이터베이스 작업 모습 - 오라클 SQL Developer
페이징네이션 기반의 게시판 프로젝트에 대해서 살펴보았다.
1. [Oracle] 오라클 데이터타입(DataType) 총정리, https://coding-factory.tistory.com/416, Accessed by 2020-09-29, Last Modified 2019-11-03.
2. [JSP]include 와 forward 의 페이지 이동, https://jerryjerryjerry.tistory.com/31, Accessed by 2020-09-29, Last Modified 2018-04-13.
3. 오라클 페이징 쿼리, 오라클 paging 방법 - 개발자 삽질 일기, https://programmer93.tistory.com/4, Accessed by 2020-09-29, Last Modified 2019.
4. [OracleDB] 페이징(Paging) 처리하는 법, https://m.blog.naver.com/wideeyed/221796538283, Accessed by 2020-09-29, Last Modified 2020-02-04.
'소프트웨어(SW) > JSP' 카테고리의 다른 글
[JSP] 20. MyBatis-3.5.5, HikariCP 3.4.2 연동 - Maven(Servlet) Spring 제거버전 (Oracle 19g) - Java 방식 (2) | 2020.10.01 |
---|---|
[JSP] 19. MyBatis-3.5.5 와 Maven / Servlet 연동하기 (Oracle 19g) - Java 방식 (2) | 2020.10.01 |
[JSP] 17. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (1) (2) | 2020.09.30 |
[JSP] 16. 쿠키(Cookie) - 프로젝트(생성, 조회, 삭제) (2) | 2020.09.26 |
[JSP] 15. Jsp/Servlet(MVC) - Session(세션) 프로젝트(로그인) - (2) (2) | 2020.09.25 |