데이터 접근 기술 - 테스트
- 김영한님의 스프링 DB 2편 강의를 통해 테스트의 원칙, 데이터베이스 분리 전략, 트랜잭션을 활용한 롤백, 그리고 임베디드 데이터베이스 설정 방법을 정리함
테스트 환경 설정
기본 개념
- 데이터 접근 기술을 테스트할 때는 실제 데이터베이스에 데이터를 저장하고 조회하는 것을 검증해야 함
설정 파일 구조

-
main - application.properties
1 2 3 4
spring.profiles.active=local spring.datasource.url=jdbc:h2:tcp://localhost/~/test spring.datasource.username=sa logging.level.org.springframework.jdbc=debug
-
test - application.properties
1 2 3 4
spring.profiles.active=test spring.datasource.url=jdbc:h2:tcp://localhost/~/test spring.datasource.username=sa logging.level.org.springframework.jdbc=debug
발생하는 문제
- 데이터 오염
- 서버 실행 시 저장된 데이터가 테스트에 영향을 줌
- 테스트 간 데이터가 공유되어 격리되지 않음

데이터베이스 분리 전략
해결 방법
-
테스트 전용 데이터베이스를 별도로 운영함

테스트 데이터베이스 생성
-
데이터베이스 파일 생성
- JDBC URL 연결
- jdbc:h2:~/testcase (최초 한번)
- jdbc:h2:tcp://localhost/~/testcase (이후)
- JDBC URL 연결
-
테이블 생성
1 2 3 4 5 6 7 8
drop table if exists item CASCADE; create table item ( id bigint generated by default as identity, item_name varchar(10), price integer, quantity integer, primary key (id) );
-
test 설정 변경
1 2 3
spring.profiles.active=test spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase spring.datasource.username=sa
테스트의 원칙
- 테스트는 다른 테스트와 격리되어야 함
- 테스트는 반복해서 실행할 수 있어야 함
트랜잭션을 활용한 데이터 롤백
트랜잭션 롤백 전략

수동 트랜잭션 관리
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@SpringBootTest class ItemRepositoryTest { @Autowired ItemRepository itemRepository; @Autowired PlatformTransactionManager transactionManager; TransactionStatus status; @BeforeEach void beforeEach() { // 각 테스트 전에 트랜잭션 시작 status = transactionManager.getTransaction( new DefaultTransactionDefinition() ); } @AfterEach void afterEach() { // 각 테스트 후에 트랜잭션 롤백 transactionManager.rollback(status); } }
-
실행 흐름

@Transactional 애노테이션 (권장)
-
1 2 3 4 5 6 7
@Transactional // 추가 @SpringBootTest class ItemRepositoryTest { @Autowired ItemRepository itemRepository; // BeforeEach, AfterEach 불필요 }
-
@Transactional 동작 원리

구분 일반 코드 테스트 코드 트랜잭션 시작 메서드 호출 시 테스트 시작 시 성공 시 커밋 롤백 예외 발생 시 롤백 롤백 목적 데이터 영속성 보장 테스트 격리 및 데이터 정리
강제 커밋하기
-
데이터베이스에 데이터가 실제로 저장되는지 확인하고 싶을 때 사용
-
1 2 3 4 5
@Commit // 또는 @Rollback(value = false) @Transactional @SpringBootTest class ItemRepositoryTest { }
임베디드 데이터베이스
임베디드 모드란?

- 별도의 DB 서버 설치나 실행이 불필요함
- JVM 안에서 메모리 모드로 동작하며 애플리케이션 종료 시 자동으로 데이터가 삭제됨
- 테스트 환경 구성이 매우 간단함
직접 설정하는 방법
-
1 2 3 4 5 6 7 8 9 10 11
@Bean @Profile("test") public DataSource dataSource() { log.info("메모리 데이터베이스 초기화"); DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; }
jdbc:h2:mem- 메모리 모드로 동작
db- 데이터베이스 이름
DB_CLOSE_DELAY=-1- 임베디드 모드에서는 데이터베이스 커넥션 연결이 모두 끊어지면 데이터베이스도 종료되는데, 이를 방지하고 애플리케이션 종료 시까지 유지함
- 기본 형식
jdbc:h2:mem:{database_name};{options}
테이블 자동 생성
- src/test/resources/schema.sql 파일 생성
-
스프링 부트는 시작 시 이 파일을 읽어 테이블을 자동으로 생성해줌

-
데이터 초기화 스크립트 (선택사항)
- src/test/resources/data.sql
schema.sql실행 후 데이터를 초기화하고 싶을 때 사용 가능함
1 2 3
-- 테스트 데이터 초기화 INSERT INTO item (item_name, price, quantity) VALUES ('testItem1', 10000, 10); INSERT INTO item (item_name, price, quantity) VALUES ('testItem2', 20000, 20);
스프링 부트 자동 설정
최종 권장 설정
-
ItemServiceApplication.java
-
DataSource빈 정의를 제거하고 스프링 부트의 자동 설정을 활용함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@Slf4j @Import(JdbcTemplateV3Config.class) @SpringBootApplication(scanBasePackages = "hello.itemservice.web") public class ItemServiceApplication { public static void main(String[] args) { SpringApplication.run(ItemServiceApplication.class, args); } @Bean @Profile("local") public TestDataInit testDataInit(ItemRepository itemRepository) { return new TestDataInit(itemRepository); } // DataSource 빈 정의 제거 (스프링 부트가 자동으로 임베디드 DB 설정) }
-
-
test/resources/application.properties
1 2 3 4 5 6 7 8 9 10
# 프로파일 설정 spring.profiles.active=test # DataSource 설정 제거 (스프링 부트가 자동으로 임베디드 H2 설정) # spring.datasource.url=jdbc:h2:tcp://localhost/~/testcase # spring.datasource.username=sa # 로깅 설정만 유지 logging.level.org.springframework.jdbc=debug logging.level.org.springframework.test.context.transaction=trace
-
ItemRepositoryTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
@Transactional // 자동 롤백 @SpringBootTest // 스프링 컨테이너 로드 class ItemRepositoryTest { @Autowired ItemRepository itemRepository; @Test void save() { // given Item item = new Item("itemA", 10000, 10); // when Item savedItem = itemRepository.save(item); // then Item findItem = itemRepository.findById(savedItem.getId()).get(); assertThat(findItem).isEqualTo(savedItem); // 자동 롤백 확인 } @Test void updateItem() { // given Item item = new Item("itemA", 10000, 10); Item savedItem = itemRepository.save(item); Long itemId = savedItem.getId(); // when ItemUpdateDto updateParam = new ItemUpdateDto("itemB", 20000, 30); itemRepository.update(itemId, updateParam); // then Item findItem = itemRepository.findById(itemId).get(); assertThat(findItem.getItemName()).isEqualTo(updateParam.getItemName()); assertThat(findItem.getPrice()).isEqualTo(updateParam.getPrice()); assertThat(findItem.getQuantity()).isEqualTo(updateParam.getQuantity()); } }
연습 문제
-
테스트 시 실제 DB 사용 중 발생하는 주요 문제는 무엇일까요?
a. 이전 테스트 데이터 잔여
- 테스트 시 이전 테스트나 서버 데이터가 DB에 남아있어 결과에 영향을 줌
- 이는 테스트 간 격리가 부족하기 때문임
-
테스트 후 데이터 잔여 문제를 해결하기 위해 트랜잭션을 사용하는 주된 이유는 무엇일까요?
a. 변경사항을 테스트 후 자동 롤백
- 트랜잭션 사용은 테스트 시작 전 상태로 되돌리는(롤백) 기능 덕분임
- 테스트가 끝난 후 변경사항을 취소해서 다음 테스트에 영향을 주지 않음
-
Spring 테스트에서
@Transactional어노테이션 사용의 주요 장점은 무엇일까요?a. 테스트 후 데이터 변경 자동 롤백
@Transactional을 테스트 메서드나 클래스에 붙이면, Spring이 각 테스트 후 자동으로 DB 변경을 되돌려줌- 수동 cleanup보다 훨씬 편리함
-
테스트 환경에서 H2와 같은 임베디드 DB를 사용하는 주된 이점은 무엇일까요?
a. 외부 DB 서버 설정 불필요 및 빠른 시작
- 임베디드 DB는 별도의 DB 서버 설치나 설정 없이 테스트 코드와 함께 실행됨
- JVM 종료 시 데이터가 사라져 테스트 환경 관리가 쉬움
-
Spring Boot가 테스트 환경에서 임베디드 DB 설정을 간소화하는 방법은 무엇인가요?
a. 별도 설정 없이 기본 임베디드 DB 자동 생성
- test 리소스의
application.properties등에서 DB 설정을 비워두면, Spring Boot가 기본값으로 H2 임베디드 DB를 생성해서 데이터소스로 등록해줌
- test 리소스의
요약 정리
- 좋은 테스트는 다른 테스트에 영향을 주지 않는 격리성, 언제 실행해도 같은 결과를 내는 반복성, 외부 환경에 의존하지 않는 독립성을 반드시 지켜야 함
- 임베디드 H2 데이터베이스를 사용하면 별도의 외부 DB 설치 없이 메모리 상에서 테스트를 수행하고 종료 시 데이터가 휘발되므로 테스트 환경 구축이 매우 간편함
- 스프링 부트는 테스트 환경(
test프로필)에서 별도 설정이 없으면 자동으로 임베디드 DB를 구성하고schema.sql을 실행하여 테이블을 초기화해주는 편리한 기능을 제공함 @Transactional을 테스트 코드에 적용하면, 각 테스트 메서드 실행 후 자동으로 트랜잭션을 롤백하여 DB 데이터를 초기 상태로 복구해주므로 데이터 격리 문제를 완벽하게 해결함- 따라서
src/test/resources에 별도의 설정 파일과 스키마 파일을 관리하고,@Transactional과 임베디드 DB를 조합하여 안전하고 반복 가능한 테스트 환경을 구축하는 것이 권장됨