김영한 님의 스프링 DB 2편 - 데이터 접근 활용 기술 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard
@SpringBootTest
는 @SpringBootApplication
가 붙은 클래스를 찾아 해당 클래스의 설정을 사용한다 ➜ 해당 클래스의 @Import
에서 JdbcTemplateV3Config.class
를 사용
➜ 테스트도 JdbcTemplate
을 통해 실제 DB를 호출한다
➜ 테스트에서 실제 DB를 호출하면 기존 DB에 있던 데이터들로 인해 영향을 받는다
➜ 즉, 로컬에서 사용하는 어플리케이션 서버와 테스트가 같은 DB를 사용하기 때문에
문제가 발생할 수도 있다
➜ 테스트 전용 데이터베이스를 별도로 운영해서 문제를 해결한다
H2 에서 jdbc:h2:~/testcase
에 접속해 test용 DB 생성 ( 최초 1회 )
/test/resources
하위의 application.properties
에 테스트용 DB로 설정 정보 변경
spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase
➜ 반복 테스트를 위해 트랜잭션을 활용해 각 테스트가 끝날때마다 DB를 롤백시킨다
트랜잭션 시작 ➜ 테스트 실행 ➜ 트랜잭션 롤백 과정을 반복
테스트가 끝난 후 트랜잭션을 강제로 롤백시키면 테스트하면서 추가한 데이터가 제거된다
테스트하면서 데이터를 저장했는데 중간에 테스트가 실패해서 롤백을 호출하지 못해도 커밋하지 않았기 때문에 DB에 반영되지 않는다
데이터를 커밋하지 않아도 같은 세션에서는 임시로 추가한 데이터를 조회할 수 있기 때문에 테스트를 수행하는데 문제가 발생하지 않는다
class ItemRepositoryTest {
@Autowired
PlatformTransactionManager transactionManager;
TransactionStatus status;
@BeforeEach
void beforeEach() {
// 테스트 전, 트랜잭션 시작
status = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
@AfterEach
void afterEach() {
// 테스트 후, 트랜잭션 롤백
transactionManager.rollback(status);
}
}
트랜잭션 매니저는 PlatformTransactionManager
를 주입 받아서 사용
스프링부트는 자동으로 적절한 트랜잭션 매니저를 스프링 빈으로 등록해준다
@BeforeEach
: 테스트 전에 호출되며, 여기서 트랜잭션을 시작
@AfterEach
: 테스트 후에 호출되며, 여기서 트랜잭션을 롤백
Repository 에서는 트랜잭션 동기화 매니저를 통해 커넥션을 획득하기 때문에 같은 커넥션을 사용하게 된다
스프링은 트랜잭션을 적용하고 롤백하는 방식을 @Transactional
하나로 처리한다
클래스 레벨에 @Transactional
를 붙이게 되면 테스트 내 모든 메서드에 적용된다
@Transactional
은 로직이 성공하면 커밋되는데 어떻게 테스트에서 사용이 가능할까?
➜ @Transactional
이 테스트에 있으면 스프링은 테스트를 트랜잭션 안에서 실행하고
테스트가 끝나면 트랜잭션을 자동으로 롤백시킨다
참고> 테스트 종료 후 커밋하기 : @Commit
나 @Rollback(value=false)
를 사용
참고> Service나 Repository에 @Transactional
이 있는 경우, Test에서 트랜잭션이 시작되었다면 Service나 Repository의 @Transactional
이 테스트의 트랜잭션에 참여
테스트용으로 DB를 사용하면 종료 후 데이터를 전부 삭제하거나 DB 자체를 제거해도 된다
H2는 JVM 안에서 메모리 모드로 동작하는 기능이 있는데 이를 임베디드 모드라고 한다
즉, 어플리케이션을 실행할 때 H2도 JVM 메모리에 포함해서 실행할 수 있다
어플리케이션이 종료되면 임베디드 모드로 동작하는 H2 종료 및 모든 데이터 삭제
@SpringBootApplication
public class ItemServiceApplication {
@Bean
@Profile("test")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver"); // H2 DB 드라이버 지정
dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1"); // 메모리 DB를 띄운다
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}
데이터 소스를 만들때 jdbc:h2:mem:db
로 설정하면 임베디드 모드로 동작하는 H2 데이터베이스를 사용할 수 있다
임베디드 모드에서는 데이터베이스 커넥션 연결이 모두 끊어지면 데이터베이스도 종료되는데, DB_CLOSE_DELAY=-1
가 이를 방지하는 설정이다
but> 이렇게 설정한 후 테스트를 실행하면 오류가 발생
➜ 메모리 DB는 어플리케이션이 종료될 때 함께 사라져서 테이블이 없기 때문
테이블 생성을 위해 JDBC나 JdbcTemplate으로 테이블을 생성해도 되지만 불편하다
스프링부트는 SQL 스크립트를 실행해서 어플리케이션 로딩 시점에 DB를 초기화한다
resources
하위에 schema.sql
이라는 이름으로 SQL 파일을 만들어야한다
SQL 파일에 테이블 생성 쿼리를 작성하면 메모리 DB가 생성될 때마다 테이블이 생성된다
스프링부트는 임베디드 DB에 대한 설정도 기본으로 제공한다
또한 DB에 대한 별다른 설정이 없으면 임베디드 DB를 사용한다
application.properties
에 설정한 DB 정보와 임베디드 DB를 위한 DataSource
설정을 없애면 스프링부트는 임베디드 모드로 접근하는 DataSource
를 만들어서 제공한다