Files
spring-boot-examples/docs/test/05_테스트와 데이터 관리.md
2025-04-08 19:56:24 +09:00

7.3 KiB

아래는 5장. 테스트와 데이터 관리에 대한 글입니다. 이 장은 스프링 부트에서 테스트 데이터를 효과적으로 생성하고 관리하는 방법을 다루며, 실무에서 흔히 사용하는 전략과 도구를 소개합니다. 실습 예제를 포함해 실질적인 이해를 돕고자 했습니다.


5장. 테스트와 데이터 관리

통합 테스트에서 데이터베이스와의 상호작용을 검증하려면 테스트 데이터의 생성과 관리가 필수적입니다. 데이터가 없거나 일관성이 부족하면 테스트 결과가 신뢰를 잃을 수 있습니다. 이 장에서는 테스트 데이터를 생성하는 전략, 트랜잭션 관리, 데이터 마이그레이션 도구 활용, 그리고 실제 데이터베이스와의 연동 테스트 방법을 살펴봅니다.

5.1 테스트 데이터 생성 전략

테스트 데이터는 테스트의 성공 여부를 좌우합니다. 효과적인 데이터 생성 전략은 다음과 같습니다:

  • 수동 생성: 테스트 코드 내에서 직접 데이터를 삽입합니다. 간단한 경우에 적합합니다.
    @Test
    void testUserRetrieval() {
        User user = new User(1L, "Alice");
        userRepository.save(user);
        // 검증 로직
    }
    
  • 픽스처(Fixture): 공통 데이터를 별도의 클래스나 파일로 관리합니다.
    class TestFixtures {
        static User createDefaultUser() {
            return new User(1L, "Alice");
        }
    }
    
  • 랜덤 데이터 생성: 라이브러리(예: JavaFaker)를 사용해 다양한 데이터를 생성합니다.
    <dependency>
        <groupId>com.github.javafaker</groupId>
        <artifactId>javafaker</artifactId>
        <version>1.0.2</version>
        <scope>test</scope>
    </dependency>
    
    @Test
    void testWithRandomData() {
        Faker faker = new Faker();
        User user = new User(null, faker.name().fullName());
        userRepository.save(user);
        // 검증 로직
    }
    

각 전략은 테스트의 목적과 복잡도에 따라 선택됩니다. 단순한 테스트에는 수동 생성이, 복잡한 시나리오에는 픽스처나 랜덤 데이터가 유용합니다.

5.2 @Transactional과 데이터 롤백

스프링 부트에서 통합 테스트를 실행할 때 데이터베이스 상태를 초기화하는 것은 중요합니다. @Transactional 애너테이션은 테스트 메서드 실행 후 데이터베이스 트랜잭션을 롤백해 원래 상태로 되돌립니다. 이를 사용하면 테스트 간 간섭을 방지할 수 있습니다.

예제:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@Transactional
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void saveUser_rollsBackAfterTest() {
        User user = new User(1L, "Alice");
        userRepository.save(user);

        assertThat(userRepository.findById(1L)).isPresent();
        // 테스트 종료 후 롤백됨
    }
}

테스트가 끝나면 데이터가 자동으로 삭제되므로, 다음 테스트에 영향을 주지 않습니다. 단, 실제 데이터베이스에 반영하고 싶다면 @Rollback(false)를 추가할 수 있습니다.

5.3 Flyway 또는 Liquibase를 활용한 테스트 데이터 마이그레이션

운영 환경에서 데이터베이스 스키마와 초기 데이터를 관리하기 위해 Flyway나 Liquibase를 사용하는 경우, 테스트에서도 이를 활용할 수 있습니다. 이를 통해 테스트 환경을 운영과 동일하게 유지할 수 있습니다.

Flyway 설정

의존성 추가:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

src/test/resources/db/migration에 SQL 파일 작성 (예: V1__init_users.sql):

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);

INSERT INTO users (id, name) VALUES (1, 'Alice');
INSERT INTO users (id, name) VALUES (2, 'Bob');

application-test.properties에 설정:

spring.flyway.locations=classpath:db/migration

테스트에서 Flyway가 자동으로 스키마와 데이터를 적용합니다.

Liquibase 설정

의존성 추가:

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
</dependency>

src/test/resources/db/changelog/db.changelog-master.yaml:

databaseChangeLog:
  - changeSet:
      id: 1
      author: test
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: bigint
                  constraints:
                    primaryKey: true
              - column:
                  name: name
                  type: varchar(255)
                  constraints:
                    nullable: false
        - insert:
            tableName: users
            columns:
              - column:
                  name: id
                  value: 1
              - column:
                  name: name
                  value: Alice

Flyway와 Liquibase는 테스트 데이터의 일관성을 보장하며, 복잡한 스키마 변경도 관리할 수 있습니다.

5.4 실제 데이터베이스와의 연동 테스트

H2나 Testcontainers 대신 실제 데이터베이스(예: MySQL, PostgreSQL)를 사용한 테스트가 필요할 때도 있습니다. 이는 운영 환경과 최대한 가까운 조건에서 테스트를 실행하고자 할 때 유용합니다.

설정 예제 (application-test.properties):

spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=testuser
spring.datasource.password=testpass
spring.jpa.hibernate.ddl-auto=update

테스트 코드:

@SpringBootTest
class RealDatabaseIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    @Transactional
    void testWithRealDatabase() {
        User user = new User(1L, "Alice");
        userRepository.save(user);

        User found = userRepository.findById(1L).orElse(null);
        assertThat(found).isNotNull();
        assertThat(found.getName()).isEqualTo("Alice");
    }
}

실제 데이터베이스 테스트는 속도가 느릴 수 있으므로, CI/CD 파이프라인에서는 별도의 단계로 분리하거나 제한적으로 사용하는 것이 좋습니다.


마무리

테스트 데이터 관리는 통합 테스트의 신뢰성과 효율성을 높이는 핵심입니다. 수동 생성, 픽스처, 랜덤 데이터 생성으로 유연성을 확보하고, @Transactional로 데이터 상태를 관리하며, Flyway나 Liquibase로 일관성을 유지할 수 있습니다. 실제 데이터베이스 연동은 운영 환경과의 차이를 줄이는 데 유용합니다. 다음 장에서는 성능과 부하 테스트를 다루며, 애플리케이션의 한계를 시험해 보겠습니다!


이 글은 데이터 관리의 다양한 접근법을 다루며, 실습 예제를 통해 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!