프로젝트 개요 및 요약
이 포스팅은 Spring과 mybatis, Oracle을 연동하는 과정에 대해 다룹니다. 프로젝트 생성부터 mybatis에 관한 초기 설정 및 연동, 테스트로 이어집니다. 테스트는 유저 정보를 가지는 테이블을 생성하고 더미 데이터를 넣어 데이터를 읽고 데이터를 추가하는 것으로 확인합니다.
과정도 과정이지만 단계가 넘어가면서 현재 단계가 이전 단계와 어떤 관계가 있는지, 생성하는 파일과 코드의 의미는 무엇인지 등에 대해서 자세히 서술해 보았습니다.
의존성 주입에 있어서는 생성자 주입이 아닌 어노테이션을 통한 자동 주입으로 구성하였습니다. 이 포스팅을 보는 사람이라면 스프링에 익숙치 않은 사람일 것이라 생각해 가장 간단한 자동 주입을 선택했습니다.
작업 환경
- Eclipse 2021-12
- Java 11
- STS4 - Spring legacy project
- Oracle 11g Express edition R2
- Sql developer
- Tomcat 9.0
프레임워크, API, 라이브러리
- mybatis
- mybatis-spring
- spring-jdbc
- commons-dbcp
- ojdbc 6
- spring-tx
- spring-test
- jUnit
- annotation-api
- jackson-core, jackson-databind
스프링 프로젝트에 의존성 주입하기
스프링 프로젝트를 생성한다.
- 이클립스에서 [File-New-Spring Legacy Project] 선택.
- 프로젝트 명 입력 후 템플릿에서 Spring MVC Project 선택 후 Next 선택.
- "com.example.app_name"과 같이 패키지 명 입력 후 finish 선택.
- package 이름 마지막은 클라이언트가 서버로 접속할 때 ip 주소, 포트 다음으로 구분되는 웹 어플리케이션 이름의 기본값이 된다.
- 이 때 입력하는 패키지 명은 servlet-context.xml에 기본적으로 설정되는 component-scan 태그의 기본 값이 된다. - 프로젝트의 자바 및 스프링 버전 최신화. (참조)
스프링 프로젝트가 정상적으로 생성되었다면 프로젝트를 진행하기 위해 필요한 의존성을 주입해야 한다. pom.xml 파일을 열고 아래에 있는 코드를 추가하거나 MavenRepository에 접속하여 원하는 버전을 찾도록 한다. 각 dependency 태그에 달린 주석에 있는 url로 접속해도 된다.
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
- mybatis : 아파치에서 제공하는 자바 퍼시스턴스 프레임워크로 기존 JDBC를 사용할 때 꼭 처리해줘야 했던 작업(close, PreparedStatement, ResultSet 등)을 자동으로 해주고 처리 과정을 간소화 해준다. 또한 SQL과 Java 코드를 분리해서 관리할 수 있고 쿼리도 기존보다 쉽게 사용할 수 있다.
- mybatis-spring : 스프링에서 mybatis와의 연동을 돕는 프레임워크.
- spring-jdbc : 스프링에서 JDBC 처리를 도와주는 라이브러리.
- spring-tx : 트랜잭션 처리를 위한 라이브러리.
- commons-dbcp : Database Connection Pool을 사용하기 위한 라이브러리.
- ojdbc6 : 오라클에서 제공하는 JDBC. 원래는 Maven에서 ojdbc를 지원하지 않아 수동으로 추가해줘야 했으나 20년 12월부터 지원하기 시작한 것으로 보인다.
- spring-test : 스프링에서 JUnit과 같은 단위 테스트를 위한 라이브러리. (JUnit은 기본적으로 추가되어 있음)
- annotation-api : Java 버전에 따라 더 이상 지원하지 않게 된 어노테이션(Resource 등)을 사용할 수 있게 해주는 API로 프로젝트에 설정한 자바 버전에 따라 필요하지 않을 수도 있으나 자바 버전이 높을 수록 추가해야 할 확률이 높다. 잘 모르겠다면 추가하자.
- jackson-core, jackson-databind : 본 예제에서는 클라이언트와 상호작용 할 수 있는 화면, 즉 view를 만들어주지 않는다. URL로 데이터를 요청하고 쿼리 수행 결과만 JSON 형태로 받는 이른바 API 서버 역할을 하게 될 것이다. Jackson 라이브러리는 클라이언트에 대한 응답으로 JSON 또는 XML로 응답할 수 있도록 해준다.
추가적으로 jUnit의 버전을 수정한다. 스프링 프로젝트를 생성하면 jUnit에 대한 의존성이 기본적으로 주입되어 있는데 여기서 버전만 4.7 → 4.12 로 수정해준다.(JUnit의 버전은 낮을수록 최신이다.) 의존성 주입, 버전 수정이 모두 끝났다면 프로젝트를 우클릭하여 [Maven → Update Project]로 프로젝트를 업데이트해준다.
앞서 언급했듯 Maven은 원래 ojdbc를 지원하지 않았기 ojdbc.jar 을 다운받고 프로젝트에 수동으로 추가해주는 작업이 필요했었다. 혹시 모르니 수동으로 추가하는 방법도 아래 접어 놓겠다.
오라클 공식 홈페이지 Oracle JDBC Download에 접속해서 자신이 사용중인 오라클 버전에 맞는 ojdbc를 다운받는다. 필자는 가장 범용성이 좋은 ojdbc8.jar을 다운받았다.

ojdbc를 다운받았다면 이클립스로 돌아와 프로젝트를 우클릭 하여 [Build path → Configure Build Path...]로 들어간다. 위와 같은 화면이 나오면 상단 탭이 "Libraries"인 것을 확인하고 Modulepath를 클릭, 우측의 "Add External JARs..."를 선택해 방금 다운로드한 ojdbc8.jar을 추가한다. 라이브러리 목록에 ojdbc가 추가되었다면 하단의 Apply를 눌러 저장해준다.

좌측 상단의 검색창에 assembly를 검색하면 나오는 "Deployment Assembly"를 클릭하면 위와 같은 화면으로 진입한다. 우측의 "Add.."를 클릭하고 나오는 창에서 "Archives from File System"을 선택하고 다음을 눌러준다.

다음 화면에서 우측의 "Add.."를 선택해 ojdbc를 추가해준다.

프로젝트로 돌아와보면 Referenced Libraries가 추가된 것을 볼 수 있고 펼쳐보면 안에 "ojdbc8.jar"이 있는 것을 확인할 수 있다.
bean 등록하기
스프링에서는 싱글톤 특성을 지니는 객체를 컨테이너에 등록하여 사용하며 컨테이너에 등록된 객체를 bean 객체라고 한다. 데이터베이스에 접근하는 객체는 특정한 서비스에서만 사용되는 것이 아니기 때문에 여러 서비스에서 호출하는 것이 가능해야 한다. 그렇기 데이터베이스에 접근할 객체를 bean으로 등록해 줄 것이다.
데이터베이스 연결에 관련된 객체들을 등록하기 위해서 root-context.xml에 들어간다.
처음 root-context.xml을 열었다면 위와 같이 기본적인 코드만 존재할 것이다. 여기서 하단 탭의 namespace를 누른다.
1. Marketplace로 들어가 Spring을 다운로드할 때 "Spring Tools Add-on..."을 다운로드 하였는지, 다운로드 했었다면 업데이트 항목이 있는지 확인한다. 설치하지 않았다면 설치해주고 Update 표시가 있다면 Update를 진행한다.
2. 프로젝트의 root-context.xml 파일에 우클릭하여 [Open-with → Other..]를 선택한다. 에디터를 선택하는 창이 나오는데 상단 체크박스가 "Internal editors"인 것을 확인한 후 spring을 검색하여 나오는 목록에서 "Spring Config Editor"를 선택하고 종료한다.
Namespaces를 누르면 위와 같은 화면을 볼 수 있는데 Namespaces의 목록은 pom.xml에서 주입한 의존성에 따라 구성된다. root-context에 추가하고자 하는 namespace를 선택하면 실제 코드 <beans>태그에 추가된다. 위 처럼 체크한 뒤 다시 하단 탭의 Source를 눌러 xml 코드로 이동한다.
정상적으로 수행됬다면 위와 같이 <beans> 태그가 수정된 것을 확인할 수 있다. 이제부터 bean 객체를 추가한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- SqlSessionTemplate -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Transaction -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
root-context.xml 에 위와 같은 코드를 추가한다. <bean> 태그는 기본적으로 id와 class를 가지는데 id는 식별자, class는 객체화하는 클래스를 나타낸다. xml로 생성한다 한들 결국 객체로 생성하는 것이기 때문에 객체화 할 클래스를 지정해주는 것이라고 보면 된다.
bean 객체 생성에 사용된 클래스를 살펴보자.
- BasicDataSource : DBMS에 접속하기 위한 정보와 DBCP를 사용하기 위한 기본 설정을 초기화한다. JDBC를 사용할 때처럼 url, driver, user id, user password 등을 입력한다. 이는 SqlSessionFactory를 빌드할 때 참조될 것이다.(민감한 정보는 따로 빼내서 관리하는 것이 일반적이지만 예제이기에 하드 코딩으로 되어 있음을 주의.)
- SqlSessionFactoryBean : 스프링에서 SqlSessionFactory를 빌드하는 컴포넌트로, SqlSessionFactory는 SqlSession을 생성 및 관리하는 역할을 한다. 위에서 생성한 dataSource를 참조하여 SqlSessionFactory를 빌드한다.
- SqlSessionTemplate : MyBatis에서의 SqlSession을 대체한다. MyBatis의 SqlSession은 SQL구문을 실행하고 트랜잭션을 제어하는 역할을 하는데 Thread-safe 하지 않다는 문제점을 가지고 있었고 이 때문에 필요에 따라 Thread를 생성하고 새로운 인스턴스를 할당해야 했다. 하지만 mybatis-spring 프레임워크에서 제공하는 SqlSessionTemplate은 SqlSession을 Thread-safe하게 사용할 수 있어 따로 관리해 줄 필요가 없다.
추가적으로 constructor-arg 속성을 사용하였는데 이름에서 유추할 수 있듯이 생성자를 지정할 수 있다. 앞서 bean 태그는 스프링 컨테이너에 bean 객체를 생성한다고 했는데 SqlSessionTemplate 클래스는 인자가 없는 생성자가 없기 때문에 이 옵션을 필수로 작성해야 한다. - DataSourceTransactionManager : JDBC 기반의 트랜잭션 관리자다. 트랜잭션 관리자를 사용하려면 DataSource에 대한 의존 설정이 필요하기 때문에 앞서 생성한 dataSource 객체를 참조할 수 있도록 한다.
- <tx:annotation-driven transaction-manager="id"> : id에 TransactionManager bean 객체의 id를 지정하면 어노테이션 기반의 트랜잭션을 활성화할 수 있다. 이 옵션을 활성화하면 프로젝트가 구동될 때 @Transactional이 적용된 메서드를 탐색하게 된다.
DataSource의 경우 DBMS에 연결하기 위한 설정과 DBCP(Database Connection Pool)를 사용하기 위한 초기 설정을 갖는데 필자는 dbms에 연결하기 위한 최소한의 정보인 driverClassName, url, username, password만을 입력했다. 오라클 DBMS를 설치하면서 별 다른 설정을 건드리지 않았으면 driver와 url은 그대로 사용하면 되고 username과 password는 본인이 사용 중인 db에 접속할 계정 정보를 입력해주면 된다. 필자는 예제로 가장 많이 사용되는 계정인 scott / tiger를 생성해 사용하였다. 이 외에 필요에 따라 initialSize, maxActive, maxIdle 등을 사용하여 커넥션 풀에 대한 프로퍼티를 추가할 수 있다.
DataSource, SqlSession Test
연결이 정상적으로 되었는지 확인하기 위해서 테스트를 수행한다. 스프링에서는 동적 웹 프로젝트에서 했던 것처럼 특정 파일만을 선택해서 서버에 올리는 것이 불가능하다. 프로젝트를 통째로 서버에 올려서 테스트해 볼 수도 있겠지만 기본적으로 의존성 주입이 되어 있는 jUnit을 사용하여 단위 테스트를 진행하도록 하자.
스프링 프로젝트에서는 테스트를 위한 디렉터리를 따로 제공하고 있다. "src/test/java"를 열고 테스트를 위한 클래스(JDBCTests.java)를 생성하고 다음 코드를 입력한다.
package com.tistory.ku_develog.persistence;
import java.sql.Connection;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class JDBCTest {
@Autowired
DataSource dataSource;
@Autowired
SqlSessionFactory sqlSessionFactory;
private static final Logger logger = Logger.getLogger(JDBCTest.class);
@Before
public void start() {
logger.info("Test start.");
}
@After
public void end() {
logger.info("Test end.");
}
@Test
public void ConnectionTest() throws Exception {
try(Connection con = dataSource.getConnection();
SqlSession sqlSession = sqlSessionFactory.openSession()) {
logger.info(con);
logger.info(sqlSession);
}
}
}
사용된 어노테이션과 클래스는 아래 접은 글에 표시해두었으니 궁금하다면 펼쳐 보기 바란다. 테스트는 "DataSource 객체에서 커넥션을 얻어올 수 있는가"와 "SqlSessionFatctory에서 SqlSession 객체를 얻어올 수 있는가"를 확인한다. root-context.xml를 작성할 때 코드를 주의 깊게 봤다면 사용하려는 멤버 변수들이 앞서 등록한 bean 객체와 연관이 있음을 알 수 있다.
일반적인 try-catch-finally 구문이 아닌 try with resources구문을 사용하여 테스트한다. try( .. ) 안에는 자원을 사용한 후 반납할 필요가 있는 객체를 선언할 수 있는데 여기에 선언된 객체들은 작업( {..} )이 끝나면 자동으로 자원을 반납한다. 단, 자바 7 이후부터 지원하는 기능이기 때문에 버전이 낮다면 일반적인 try-catch-finally를 구성하도록 한다.
테스트를 수행하기 전에 파일을 저장하겠냐는 메시지 혹은 자동 저장이 되지 않기 때문에 파일을 수동으로 저장해야 한다. 테스트 할 메소드인 ConnectionTest에 커서를 위치한 후 우클릭을 하여 [Run as... → JUnit Test]를 눌러 테스트를 진행한다.
JUnit Annotation
RunWith (SpringJUnit4ClassRunner.class) | - JUnit의 테스트 범위를 확장할 때 사용하는 어노테이션이다. JUnit은 Java에서 단위 테스트를 지원하기 위한 프레임워크로, Spring에서 제공하는 프레임워크가 아니기 때문에 스프링에서 사용할 수 있도록 확장해주는 작업이다. - SpringJUnit4ClassRunner 를 지정해주면 테스트를 수행할 때 Application Context, 즉 스프링 컨테이너를 만들고 관리하는 작업을 해준다. |
ContextConfiguration | 반드시 RunWith와 같이 사용해야 하는 어노테이션이다. 그 이유는 이 어노테이션이 RunWith로 인해 생성될 Application Context 설정 파일 위치를 지정하는 역할을 하기 때문이다. 설정 파일은 기본적으로 servlet-context.xml, root-context.xml 등이 있는데 테스트하려는 bean에 따라서 하나만 넣거나 중괄호를 사용해 둘 다 넣어줄 수 있다. |
Spring-test Annotation
Test | 테스트를 수행 할 메서드를 지정한다. |
Before | 테스트를 수행하기 전에 수행될 메서드를 지정한다. |
After | 테스트를 수행한 후 수행될 메서드를 지정한다. |
멤버 변수
- DataSource : CP(Connection Pool)에 있는 Connection을 관리하기 위한 클래스. getConnection() 메서드를 호출하면 CP에서 Free 상태인 Connection 객체를 얻을 수 있다.
- SqlSessionFactory : MyBatis에서 제공하는 SqlSession 객체를 가지고 있는 클래스.
- Logger : Log4j에서 제공하는 로그를 출력하기 위한 클래스.
콘솔 탭과 JUnit 탭에 위와 같이 출력되었다면 테스트가 성공한 것이다. 커넥션 풀에서 얻어온 커넥션 객체의 정보와 SqlSessionFactory에서 얻은 SqlSession 정보가 출력된 것을 볼 수 있다.
Spring과 오라클 간의 연동이 성공적으로 수행된 것을 확인하였다. 이제 테스트에 사용할 테이블을 생성하자.
테스트를 위한 회원 정보 table 생성하기
CREATE TABLE member_t (
id varchar(20) not null primary key,
name varchar2(30) not null,
age number not null
);
INSERT INTO member_t VALUES('user1', '홍길동', 36);
INSERT INTO member_t VALUES('user2', 'Robert', 22);
INSERT INTO member_t VALUES('user3', '이영희', 21);
테이블 생성은 Sql developer로 수행하였다. 다른 툴을 사용해도 되고 콘솔로 하는 것이 편하다면 그렇게 해도 된다. 테이블은 테스트 용으로 만들어진 임시 테이블인 만큼 간단하다. 아이디, 사용자 이름, 나이를 가지며 아이디가 주키가 된다. 빠른 테스트를 위해 미리 데이터를 삽입해놓도록 하자.
다음 포스팅으로 이어집니다.
[Spring] STS4, Oracle, myBatis 연동하기 (2/3)
[바로가기 목록] 1. 프로젝트 생성 및 환경 구축하기 2. 스프링과 mybatis 연동하기 (현재 포스팅) 3. 서버 생성 및 테스트 Spring, myBatis 연동 앞서 스프링과 오라클이 연동된 것을 확인했다. 이 상태
ku-develog.tistory.com