[JAVA] JNDI방식으로 ConnectionPool 설정하기

Yuri Lee·2024년 4월 4일
2

JAVA

목록 보기
10/12

회사에서 개발서버를 교체하는 과정에서 DBPool에 접근 시 기존의 DB url을 통해 DB에 직접 접근하여 커넥션객체를 가져오는 방식 대신, JNDI방식으로 풀에 접근하여 커넥션객체를 가져오도록 변경하였다. 그 과정에서 겪었던 몇 가지 어려움들을 기록하고자 한다!

우선 개발환경은 SPRING을 기반으로 한 egovframe3.5.1버전이며 oracleDB를 사용한다.

JNDI란?

JNDI(Java Naming and Directory Interface)는 디렉터리 서비스에서 제공하는 데이터 및 객체를 발견하고 참고(lookup:함수)하기 위한 자바 API이다.

간단히 말하면 우리가 연결하고 싶은 데이터베이스의 ConnectionPool을 미리 Naming시켜주는 방법이다. WAS의 데이터베이스 정보에 JNDI 설정을 해놓으면 웹어플리케이션은 JNDI 이름만 호출하면 되는 것!

장점
1) DB 설정 정보를 파악하기 쉽다.
WAS 단에 설정 정보를 통해 DB가 몇 개 붙어있는지 파악하기 수월하다.
2) DB Connection Pool을 효율적으로 사용할 수 있다.
WAS 단에서 DB Pool을 하나로 관리하면 static 객체를 생성 후에 쉽게 가져다 쓸 수 있기 때문에 효율이 좋아진다. (하드코딩하지 않아도된다.)

이때, ConnectionPool이란?
매번 DB에 접근하기 위해 웹애플리케이션에서 연결/해제를 반복해야하는 비효율적인 방법대신 처음부터 톰캣이 실행될때 연결을 유지시켜주는 것!
톰캣 하나에 여러 애플리케이션을 올려사용하기 때문에 톰캣 하나만 실행함으로써 여러 웹앱의 DB접근의 유용성을 높여준다.

SPRING 설정파일 변경

우선, context-datasource.xml 파일에 dataSource를 JNDI방식으로 가져오게끔 변경해줬다.

변경 전

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${driverClassName}"></property>
  <property name="url" value="${url}"></property>
  <property name="username" value="${username}"></property>
  <property name="password" value="${password}"></property>
</bean>

변경 후

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="${globals.jndiname}"/> <!--globals.properties에 정의된 이름을 가져옴-->
  <property name="resourceRef" value="true"/>
</bean>

resourceRef는 "java:comp/env/"를 자동으로 붙여주는 설정이며 나는 true로 설정하였다. 개발서버로 연결 시 true/false 상관없이 연결이 가능했는데(그 이유는 모르겠음), 로컬 톰캣 상에서는 false는 경로문제로 jndiname을 찾지못해서 true로 설정하였다.

context-datasource.xml 에서 bean id를 설정할때 반드시 context-mapper.xml의 property name/ref와 같게 할 것.

참고 : https://www.egovframe.go.kr/home/qainfo/qainfoRead.do?menuNo=69&qaId=QA_00000000000007764

Tomcat 설정파일 변경

여기까진 수월했으나, Tomcat에 JNDI를 설정하며 헷갈리는 부분들이 많았다... Tomcat restart를 100번도 넘게 한듯...

도대체 context.xml, server.xml, web.xml 어떤 파일을 건드려야하냐...!

블로그마다 db종류와 spring유무에 따라 설정방식이 조금씩 달랐지만 많은 블로그에서 유사하게 눈에 띄었던 방식은 다음과 같았다.

아래는 실패했던 방식

1) server.xml 파일의 < GlobalNamingResources > 태그 내에 DB의 전역 자원을 등록

<GlobalNamingResources>
	...
    <Resource auth="Container"
    	      driverClassName="oracle.jdbc.OracleDriver"
              type="javax.sql.DataSource"
              initialSize="0"
              minIdle="5"
              maxTotal="20"
              maxIdle="20"
              maxWaitMillis="5000"
              url="jdbc:oracle:thin:@localhost:1521:xe"
              name="dbcp_myoracle"
              username="root"
              password="1234" />
</GlobalNamingResources>

2) context.xml 파일에 < Context > 태그 내에 < ResourceLink > 에 JNDI명을 설정

<Context>
	...
    <ResourceLink global="dbcp_myoracle" name="dbcp_myoracle"
		  type="javax.sql.DataSource" />
</Context>

3) web.xml 파일에 ref 지정 (해당 부분은 SPRING 설정파일에 정의되어있다면 건너뛰어도 되는듯...?)

<resource-ref>
    <description>Resource</description>
    <res-ref-name>jdbc/EmployeeDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

이런 유사한 방식으로 dirverClassName도 바꿔보고, type, Idle, factory등등 각종 속성을 여러가지로 바꿔봤지만 돌아오는건

에러 javax.naming.NameNotFoundException: Name jdbc is not bound in this Context

-> 내가 WAS에서 지정한 JNDI 이름의 DB를 찾지 못한다는 뜻이다.

그러다가 ChatGPT와의 대화에서 server.xml 파일을 직접 수정하는 것은 일반적으로 권장되지 않습니다. 라는 말을 들었다. 띠용. 많은 사람들이 server.xml을 수정하라고 했는데 수정하지 말라니. 이유를 더 검색해보니 1) Tomcat 업그레이드 및 호환성 문제 2) Tomcat 설정 변경 관리의 어려움 3) 보안 및 안정성 위험 으로 인해 context.xml을 수정하는게 더 나은 대안이라는 것이다.

context.xml에 JNDI 설정

따라서 'context.xml JNDI 설정'을 구글링해 나온 블로그를 참고하여 톰캣도 삭제했다가 다시 설치한 후 새로운 마음가짐으로 도전해봤다...^^

<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Resource name="jdbc/JNDIname" auth="Container" type="javax.sql.DataSource"
              maxActive="10" maxIdle="10" maxWait="10000"
              username="username" password="password"
              driverClassName="oracle.jdbc.OracleDriver"
              url="jdbc:oracle:thin:@localhost:1521:xe" />
</Context>

이렇게 설정하니까 드디어!!!!
jdbc/jndiName을 찾지 못한다는 에러가 아닌, 새로운 에러가 난것!^^
우선, JNDI 설정이 적용됐다는 얘기니 너무나도 다행... 휴 ChatGPT 당신은 영원한 내 친구❤️

에러 oracle.jdbc.driver.T4CConnection.isValid(I)Z

이 에러는 메서드 시그니처(매개변수의 형태)가 일치하지 않는 jdbc driver의 메서드 호출을 시도했을 때 발생하는 오류 메시지.

찾아본 해결방식은 두 가지로
1) 자바와 JDBC의 버전이 맞지않아 자바 1.8버전에 맞는 JDBC 8버전의 라이브러리를 넣으라는 것 (기존 JDBC버전은 14)
: 해당 방식은 시도X
2) Servers 프로젝트의 context.xml 파일의 태그 안에 factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 삽입

두 번째 방식으로 context.xml 파일을 수정하여 드디어 JDBC 연결 후 로컬에 접속을 성공했다!

context.xml 최종수정본!

<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Resource name="jdbc/JNDIname" auth="Container" type="javax.sql.DataSource"
              maxActive="10" maxIdle="10" maxWait="10000"
              username="username" password="password"
              driverClassName="oracle.jdbc.OracleDriver"
              url="jdbc:oracle:thin:@localhost:1521:xe" 
              factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" />
</Context>

factory를 설정함으로써 올바른 팩토리 클래스를 명시적으로 지정함으로써 버전 호환성 문제를 해결하고, 불명확성을 제거하고 의도한 클래스를 사용할 수 있다.

REFERENCES

profile
개발자 이유리

0개의 댓글