본문으로 건너뛰기

DBCP로 DB 자원 효율적으로 관리하기


DataBase Connection Pool의 약자로 사용한 connection 객체, Statement 객체와 같은 자원들을 효율적으로 사용하기 위한 방법이다.

Tomcat에 미리 DB을 연결해놓고, 필요할 때마다 빌려서 사용하고 반환해주는 것이다.

DBCP를 사용할 때 기본적으로 JDBC , DBCP , JNDI 을 사용하게 된다.

사용되는 3가지를 정리해 주세요 😏

JDBC(Java Database Connectivity)

Java와 DB가 통신할 수 있게 해주는 API

DBCP(DataBase Connection Pool)

DB와 연결된 커넥션을 미리 만들어서 Pool속에 저장해 두고 있다가 필요할 때 커넥션을 Pool에서 쓰고 다시 풀에 반환하는 기법

JNDI(Java Naming and Directory Interface)

네이밍 패키지의 클래스를 가지고 이름으로 객체를 획득하는 것 각 데이터베이스에 대한 서비스를 디렉토리 형태로 등록할 수 있다.

명명 서비스 및 디렉토리 서비스에 접근하기 위한 API

종합적으로

JDBC으로 연결된 DB를 DBCP를 이용해 Pool속에 저장해 두고 있다가 필요할 때 JNDI를 이용해 쓰고 반환하는 DB커넥션 방법론 이다.

설치 및 설정

  • JDBC
    • 이전 회차에서 DBMS 드라이버 설치 완료 되어있어야함
  • DBCP
    • Tomcat 7.0 이상 버전에서는 Tomcat lib에 포함되어있음
  • JNDI
    • javax.sql package 사용

context.xml 생성

DB Connection시 참고가 되는 데이터 이다.

web\META-INF\context.xml 의 경로에 위치하게 된다.

필요한 디렉토리와 .xml 파일은 IDE를 사용하지 않고 직접 생성해도 문제가 없다.

info

상위 톰켓에서는 context.xml 파일과 연동하기 위해 web.xml을 수정하지 않아도 된다.

파일 시스템을 이용하여 생성

  1. META-INF 디렉토리 생성
  2. context.xml 파일 생성
  3. context.xml 기본 스키마을 작성해 준다.
    context.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
    </Context>

IDE를 이용하여 생성

  1. 프로젝트 구조 클릭
  2. 모듈에서 추가할려는 모듈 클릭 후 클릭 오른쪽 애플리케이션 및 서버별 설명자 추가 클릭
  3. 아래와 같이 설정 후 확인 클릭 설명자에 Tomcat Context Descriptor 으로 적혀있음
  4. web\META-INF\context.xml이 생긴것을 볼 수 있다.

context.xml 작성

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
name="Local-Mysql" // connection pool의 이름(임의로 해도 되지만 보통 'jdbc/db의 이름'으로 많이 해준다. 자바에서 connection pool을 사용할 때 이 name 값을 통해 호출
auth="Container" // 인증하는 부분. 이 톰캣 컨테이너를 connection pool로 사용하겠다고 명시해주는 부분.
type="javax.sql.DataSource" // connection을 만들어주는 객체. API.
username="Please enter your ID" // DB Id
password="Please enter your password" // DB Pw
driverClassName="com.mysql.cj.jdbc.Driver" // driver 패키지 이름
url="jdbc:mysql://localhost:3306/test?serverTimezone=UTC" // DB 경로
maxActive="20" // connection pool을 미리 만들어 놓을 갯수. 만약 4로 설정해놓고 전부 소모한 다음 요청이 또 들어와도 에러는 나지 않고, 컨테이너가 알아서 하나를 더 만들어준다.
maxWait="-1" // 컨테이너가 자동으로 connection pool을 생성할 때 기다리는 시간. 이 시간동안 기다려도 resource가 부족하여 새로 생성 못할 시는 에러 발생
maxIdle="10" // 사용되지 않고 풀에 저장될 수 있는 최대 커넥션 갯수. 음수일 경우 제한이 없음
/>
</Context>
caution

서버 동기화 시간이 지정되지 않으면 connection에 실패할 수 있다.

Test Code 작성

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page import="java.sql.*" %>
<%@page import="javax.sql.*" %>
<%@page import="javax.naming.*" %>
<html>
<head>
<title>DataBase Connection Pool</title>
</head>
<body>
<%
// 연결을 위한 Connection 객체
Connection con = null;
// 통신하기 위한 PreparedStatement 객체
PreparedStatement pstmt = null;
// select 결과 값을 담기 위한 ResultSet 객체
ResultSet rs = null;

// 실제로 DB에 접근하는 부분
try
{
// 연결하려는 DB이름 (context.xml에서 해당 이름과 일치하는 DB 가져옴)
// String dbName = "Server-Mysql";
String dbName = "Local-Mysql";

// Connection Pool을 찾는 과정
Context init = new InitialContext();
// context.xml에서 설정한 Resource Name 부분
DataSource ds = (DataSource) init.lookup("java:comp/env/" + dbName);
// DataSource로 부터 Connection을 가져옴
con = ds.getConnection();

out.print("DBCP Success");
System.out.println("DBCP Success");
}
catch (Exception ex)
{
out.print("DBCP Fail");
System.out.println("DBCP Fail");
System.out.println("Exception: " + ex.getMessage());
}
finally
{
// 자원 해제는 똑같이 해주어야 한다.
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (con != null)
con.close();
}
%>
</body>
</html>
caution

DAO는 Singleton 패턴으로 사용되어야 한다.

caution

사용 후 반드시 해당 connection을 free 해줘야 한다.

  1. DAO(Data Access Object)가 실행될 때 1회 Connection Pool을 찾고
  2. DAO의 메소드가 실행될 떄 마다 DataSource로 부터 connection을 가져오고 메소드가 종료될 때 connection을 free 해주는 패턴으로 사용되어야 한다. (특수 전략에 따라 바뀔 수 있음)

실행

앗, 근데 context.xml 가 공개적으로 노출해야 하는 경우 어떻게 하죠? 😨

해당 파일을 그래도 github에 공유하게되면 DB 계정정보가 포함 된 context.xml 이 공개적으로 노출됩니다.

그럼 해결 방법은 무엇일까요? 방법은 다음 챕터에 기록되어 있습니다!


parkgang