본문 바로가기

Java 웹 개발

21.10.27 - 웹 개발 입문 53일차

홈페이지 - DBCP

DBCP 는 DataBase Connection Pool의 약자이며,

데이터베이스와 애플리케이션을 효율적으로 연결하는 커넥션 풀 라이브러리다

 

자바에서 DB에 직접 연결해서 처리하는 경우(=JDBC) 드라이버(Driver)를 로드하고 커넥션(connection) 객체를 받아와야 한다. 그러면 매번 사용자가 요청을 할 때마다 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하기 때문에 매우 비효율적이다. 이런 문제를 해결하기 위해서 커넥션풀(DBCP)를 사용한다.

 

1. 먼저 DBCP를 다운받는다. (DBCP – Download Apache Commons DBCP) - commons-dbcp2-2.9.0-bin.zip

 

2. 다운 받은 후 WEB-INF -> lib에 넣는다.

3. META_INF -> context.xml 를 생성한다.

4. 현재 프로젝트를 해석(실행)할 때 알아야 하는 정보를 설정

DBCP를 수행할 수 있는 자원(도구) 등록 
- driverClassName : DBMS에 연결하기 위한 드라이버 클래스 경로
- url : DBMS에 연결하기 위한 경로
- maxTotal : 총 연결(Connection)의 수
- minIdle : 유휴 연결(Connection) 최소 개수
- maxIdle : 유휴 연결(Connection) 최대 개수
- maxWaitMillis : 연결이 모두 사용중일 경우의 대기 시간

<?xml version="1.0" encoding="UTF-8"?>

<Context>
	<Resource 
						name="jdbc/oracle"
						type="javax.sql.DataSource"
						auth="Container"
						
						driverClassName="oracle.jdbc.OracleDriver"
						url="jdbc:oracle:thin:@localhost:1521:xe"
						username="계정아이디"
						password="계정비밀번호"
						
						maxTotal="20"
						minIdle="0"
						maxIdle="5"
						maxWaitMillis="3000"/>
	
</Context>

 

5. 연결 생성 : DBCP를 등록 하는 방법 (JdbcUtils에 생성)
- 연결을 직접 수행하는 것이 아니라 연결을 관리하는 업체를 두고 렌탈하는 방식
- 연결을 생성하는 시간이 줄어들어서 전체적인 성능과 안정성이 향상됨
- 설정 파일을 초기에 불러와야 한다.

package pj.beans;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class JdbcUtils {

	private static DataSource ds;
	
	static {
		//context.xml에 있는 "jdbc/oracle" 이라는 이름을 가진 자원의 참조 정보를 획득
		Context ctx;
		try {
			ctx = new InitialContext();
			ds = (DataSource) ctx.lookup("java:comp/env/jdbc/oracle");
		} 
		catch (Exception e) {
			System.err.println("DataSource 생성 오류");
			e.printStackTrace();
		}
	}
	
	public static Connection connect() throws Exception{
		return ds.getConnection();
	}
	
}

 

 

홈페이지 - 페이징

 

게시판에 하단에 있는 이전페이지, 다음페이지 목록 보기 구현하기

 

ex) < 1 2 3 4 5 6 7 8 9 10 >

 

- BoardDao 페이징 목록 기능  만들기

public List<BoardDto> listByRownum(int begin, int end) throws Exception {
	Connection con = JdbcUtils.connect2();

	String sql = "select * from ("
							+ "select rownum rn, TMP.* from ("
								+ "select * from board order by board_no desc"
							+ ")TMP"
					+ ") where rn between ? and ?";
	PreparedStatement ps = con.prepareStatement(sql);
	ps.setInt(1, begin);
	ps.setInt(2, end);
	ResultSet rs = ps.executeQuery();

	List<BoardDto> list = new ArrayList<>();
	while(rs.next()) {
		BoardDto boardDto = new BoardDto();

		boardDto.setBoardNo(rs.getInt("board_no"));
		boardDto.setBoardWriter(rs.getString("board_writer"));
		boardDto.setBoardTitle(rs.getString("board_title"));
		boardDto.setBoardContent(rs.getString("board_content"));
		boardDto.setBoardTime(rs.getDate("board_time"));
		boardDto.setBoardRead(rs.getInt("board_read"));
		boardDto.setBoardReply(rs.getInt("board_reply"));
		boardDto.setBoardSuperno(rs.getInt("board_superno"));
		boardDto.setBoardGroupno(rs.getInt("board_groupno"));
		boardDto.setBoardDepth(rs.getInt("board_depth"));

		list.add(boardDto);
	}

	con.close();

	return list;
}

 

- BoardDao 페이징 검색 기능  만들기

public List<BoardDto> searchByRownum(String column, String keyword, int begin, int end) throws Exception {
	Connection con = JdbcUtils.connect2();

	String sql = "select * from ("
							+ "select rownum rn, TMP.* from ("
								+ "select * from board where instr(#1, ?) > 0 order by board_no desc"
							+ ")TMP"
					+ ") where rn between ? and ?";
	sql = sql.replace("#1", column);
	PreparedStatement ps = con.prepareStatement(sql);
	ps.setString(1, keyword);
	ps.setInt(2, begin);
	ps.setInt(3, end);
	ResultSet rs = ps.executeQuery();

	List<BoardDto> list = new ArrayList<>();
	while(rs.next()) {
		BoardDto boardDto = new BoardDto();

		boardDto.setBoardNo(rs.getInt("board_no"));
		boardDto.setBoardWriter(rs.getString("board_writer"));
		boardDto.setBoardTitle(rs.getString("board_title"));
		boardDto.setBoardContent(rs.getString("board_content"));
		boardDto.setBoardTime(rs.getDate("board_time"));
		boardDto.setBoardRead(rs.getInt("board_read"));
		boardDto.setBoardReply(rs.getInt("board_reply"));
		boardDto.setBoardSuperno(rs.getInt("board_superno"));
		boardDto.setBoardGroupno(rs.getInt("board_groupno"));
		boardDto.setBoardDepth(rs.getInt("board_depth"));

		list.add(boardDto);
	}

	con.close();

	return list;
}

 

- BoardDao 페이징에서 마지막 블록을 구하기 위하여 게시글 개수를 구하는 기능 (목록/검색)  만들기

public int count() throws Exception {
	Connection con = JdbcUtils.connect2();

	String sql = "select count(*) from board";
	PreparedStatement ps = con.prepareStatement(sql);
	ResultSet rs = ps.executeQuery();

	rs.next();

//	int count = rs.getInt("count(*)");
	int count = rs.getInt(1);

	con.close();

	return count;
}

 

- 페이징 모듈 구현

Pagination을 위한 도구 클래스
- 입력(준비물)
- 페이지번호(p)
- 검색분류(column)
- 검색어(keyword)
- 데이터 개수(count)
- 처리
- 출력(결과물) 

package home.beans;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

public class Pagination {
	//필수 데이터
	private int p;
	private int count;

	//선택 데이터
	private String column;
	private String keyword;

	//생성자를 이용하여 필수 데이터를 설정하도록 구현
	public Pagination(HttpServletRequest req) {
		try {
			this.p = Integer.parseInt(req.getParameter("p"));
			if(this.p <= 0) throw new Exception();
		}
		catch(Exception e) {
			this.p = 1;
		}
		
		this.column = req.getParameter("column");
		this.keyword = req.getParameter("keyword");
	}

	//계산 메소드
	private int pageSize = 10;
	private int blockSize = 10;
	private int begin, end;
	private int startBlock, finishBlock, lastBlock;
	private List<BoardDto> list;
	public void calculate() throws Exception {
		//count 계산
		BoardDao boardDao = new BoardDao();
		if(isSearch()) {
			this.count = boardDao.count(column, keyword);
		}
		else {
			this.count = boardDao.count();
		}
		//rownum 계산
		this.end = this.p * this.pageSize;
		this.begin = this.end - (this.pageSize - 1);

		//block 계산
		this.lastBlock = (this.count - 1) / this.blockSize + 1;
		this.startBlock = (this.p - 1) / this.blockSize * this.blockSize + 1;
		this.finishBlock = this.startBlock + (this.blockSize - 1);
		
		//list 계산
		if(this.isSearch()) {
			this.list = boardDao.searchByRownum(column, keyword, begin, end);
		}
		else {
			this.list = boardDao.listByRownum(begin, end);
		}
	}
	
	public int getPage() {
		return p;
	}
	public int getCount() {
		return count;
	}
	public String getColumn() {
		return column;
	}
	public String getKeyword() {
		return keyword;
	}
	public int getPageSize() {
		return pageSize;
	}
	public int getBlockSize() {
		return blockSize;
	}
	public int getBegin() {
		return begin;
	}
	public int getEnd() {
		return end;
	}
	public int getStartBlock() {
		return startBlock;
	}
	public int getFinishBlock() {
		return finishBlock;
	}
	public int getLastBlock() {
		return lastBlock;
	}

	//추가 : 이전이 존재하나요?
	public boolean isPreviousAvailable() {
		return this.startBlock > 1;
	}
	//추가 : 다음이 존재하나요?
	public boolean isNextAvailable() {
		return this.finishBlock < this.lastBlock; 
	}
	//추가 : 검색모드인가요?
	public boolean isSearch() {
		return this.column != null && !this.column.equals("") && this.keyword != null && !this.keyword.equals("");
	}
	//추가 : 진짜 마지막 블록 번호 반환
	public int getRealLastBlock() {
		return Math.min(this.finishBlock, this.lastBlock);
	}
	
	//추가 : 이전을 누르면 나오는 블록 번호
		public int getPreviousBlock() {
			return this.startBlock - 1;
		}
		//추가 : 다음을 누르면 나오는 블록 번호
		public int getNextBlock() {
			return this.finishBlock + 1;
		}
		//추가 : 컬럼이 특정 값인지 검사
		public boolean columnIs(String column) {
			return this.column != null && this.column.equals(column);
		}
		//추가 : null을 제거한 keyword 반환 메소드
		public String getKeywordString() {
			if(this.keyword == null) 
				return "";
			else
				return this.keyword;
		}
		@Override
		public String toString() {
			return "Pagination [p=" + p + ", count=" + count + ", column=" + column + ", keyword=" + keyword + ", pageSize="
					+ pageSize + ", blockSize=" + blockSize + ", begin=" + begin + ", end=" + end + ", startBlock="
					+ startBlock + ", finishBlock=" + finishBlock + ", lastBlock=" + lastBlock + "]";
		}

		public void setPageSize(int pageSize) {
			this.pageSize = pageSize;
		}
		public void setBlockSize(int blockSize) {
			this.blockSize = blockSize;
		}
		public List<BoardDto> getList() {
			return list;
		}		
}

 

- list.jsp 재설정

<%@page import="home.beans.Pagination"%>
<%@page import="home.beans.BoardDto"%>
<%@page import="java.util.List"%>
<%@page import="home.beans.BoardDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%
	//Pagination 모듈을 이용하여 계산을 처리하도록 위임
	Pagination pagination = new Pagination(request);
	//pagination.setPageSize(15);
	//pagination.setBlockSize(5);   
	pagination.calculate(); 	
%>

<%=pagination.toString()%>

<%-- 출력 --%>
<jsp:include page="/template/header.jsp"></jsp:include>

<h2>회원 게시판</h2>
<h6>타인에 대한 무분별한 비판은 제재 대상입니다</h6>

<a href="write.jsp">글쓰기</a>
<br><br>

<table border="1" width="90%">
	<thead>
		<tr>
			<th>번호</th>
			<th width="45%">제목</th>
			<th>작성자</th>
			<th>작성일</th>
			<th>조회수</th>
		</tr>
	</thead>
	<tbody align="center">
		<%for(BoardDto boardDto : pagination.getList()){ %>
		<tr>
			<td><%=boardDto.getBoardNo()%></td>
			<td align="left"><a href="detail.jsp?boardNo=<%=boardDto.getBoardNo()%>"><%=boardDto.getBoardTitle()%></a></td>
			<td><%=boardDto.getBoardWriter()%></td>
			<td><%=boardDto.getBoardTime()%></td>
			<td><%=boardDto.getBoardRead()%></td>
		</tr>
		<%} %>
	</tbody>
</table>

<br>
<a href="write.jsp">글쓰기</a>

<!-- 페이지 네비게이터 -->
<br><br>
<%if(pagination.isPreviousAvailable()){ %>
	<%if(pagination.isSearch()){ %>
	<!-- 검색용 링크 -->
	<a href="list.jsp?column=<%=pagination.getColumn()%>&keyword=<%=pagination.getKeyword()%>&p=<%=pagination.getPreviousBlock()%>">&lt;</a>
	<%} else { %>
	<!-- 목록용 링크 -->
	<a href="list.jsp?p=<%=pagination.getPreviousBlock()%>">&lt;</a>
	<%} %>
	<%} else { %>
	<a>&lt;</a>
	<%} %>

<%for(int i = pagination.getStartBlock(); i <= pagination.getRealLastBlock(); i++){ %>
	<%if(pagination.isSearch()){ %>
	<!-- 검색용 링크 -->
	<a href="list.jsp?column=<%=pagination.getColumn()%>&keyword=<%=pagination.getKeyword()%>&p=<%=i%>"><%=i%></a>
	<%}else{ %>
	<!-- 목록용 링크 -->
	<a href="list.jsp?p=<%=i%>"><%=i%></a>
	<%} %>
<%} %>

<%if(pagination.isNextAvailable()){ %>
	<%if(pagination.isSearch()){ %>
		<!-- 검색용 링크 -->
		<a href="list.jsp?column=<%=pagination.getColumn()%>&keyword=<%=pagination.getKeyword()%>&p=<%=pagination.getNextBlock()%>">&gt;</a>
	<%} else { %>
		<!-- 목록용 링크 -->
		<a href="list.jsp?p=<%=pagination.getNextBlock()%>">&gt;</a>
	<%} %> 
<%} else {%>
	<a>&gt;</a>
<%} %>
<br><br>

<!-- 검색창 -->
<form action="list.jsp" method="get">

	<select name="column">
		<%if(pagination.columnIs("board_title")){ %>
		<option value="board_title" selected>제목</option>
		<%}else{ %>
		<option value="board_title">제목</option>
		<%} %>

		<%if(pagination.columnIs("board_content")){ %>
		<option value="board_content" selected>내용</option>
		<%}else{ %>
		<option value="board_content">내용</option>
		<%} %>

		<%if(pagination.columnIs("board_writer")){ %>
		<option value="board_writer" selected>작성자</option>
		<%}else{ %>
		<option value="board_writer">작성자</option>
		<%} %>

	</select>

	<input type="search" name="keyword" placeholder="검색어 입력" required value="<%=pagination.getKeywordString()%>">

	<input type="submit" value="검색">

</form>


<jsp:include page="/template/footer.jsp"></jsp:include>

 

 

페이징 구현된 모습