2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
아래는 **5장. 테스트와 데이터 관리**에 대한 글입니다. 이 장은 스프링 부트에서 테스트 데이터를 효과적으로 생성하고 관리하는 방법을 다루며, 실무에서 흔히 사용하는 전략과 도구를 소개합니다. 실습 예제를 포함해 실질적인 이해를 돕고자 했습니다.
---
## 5장. 테스트와 데이터 관리
통합 테스트에서 데이터베이스와의 상호작용을 검증하려면 테스트 데이터의 생성과 관리가 필수적입니다. 데이터가 없거나 일관성이 부족하면 테스트 결과가 신뢰를 잃을 수 있습니다. 이 장에서는 테스트 데이터를 생성하는 전략, 트랜잭션 관리, 데이터 마이그레이션 도구 활용, 그리고 실제 데이터베이스와의 연동 테스트 방법을 살펴봅니다.
### 5.1 테스트 데이터 생성 전략
테스트 데이터는 테스트의 성공 여부를 좌우합니다. 효과적인 데이터 생성 전략은 다음과 같습니다:
- **수동 생성**: 테스트 코드 내에서 직접 데이터를 삽입합니다. 간단한 경우에 적합합니다.
```java
@Test
void testUserRetrieval() {
User user = new User(1L, "Alice");
userRepository.save(user);
// 검증 로직
}
```
- **픽스처(Fixture)**: 공통 데이터를 별도의 클래스나 파일로 관리합니다.
```java
class TestFixtures {
static User createDefaultUser() {
return new User(1L, "Alice");
}
}
```
- **랜덤 데이터 생성**: 라이브러리(예: JavaFaker)를 사용해 다양한 데이터를 생성합니다.
```xml
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
```
```java
@Test
void testWithRandomData() {
Faker faker = new Faker();
User user = new User(null, faker.name().fullName());
userRepository.save(user);
// 검증 로직
}
```
각 전략은 테스트의 목적과 복잡도에 따라 선택됩니다. 단순한 테스트에는 수동 생성이, 복잡한 시나리오에는 픽스처나 랜덤 데이터가 유용합니다.
### 5.2 @Transactional과 데이터 롤백
스프링 부트에서 통합 테스트를 실행할 때 데이터베이스 상태를 초기화하는 것은 중요합니다. `@Transactional` 애너테이션은 테스트 메서드 실행 후 데이터베이스 트랜잭션을 롤백해 원래 상태로 되돌립니다. 이를 사용하면 테스트 간 간섭을 방지할 수 있습니다.
예제:
```java
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 설정
의존성 추가:
```xml
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
```
`src/test/resources/db/migration`에 SQL 파일 작성 (예: `V1__init_users.sql`):
```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`에 설정:
```properties
spring.flyway.locations=classpath:db/migration
```
테스트에서 Flyway가 자동으로 스키마와 데이터를 적용합니다.
#### Liquibase 설정
의존성 추가:
```xml
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
```
`src/test/resources/db/changelog/db.changelog-master.yaml`:
```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`):
```properties
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=testuser
spring.datasource.password=testpass
spring.jpa.hibernate.ddl-auto=update
```
테스트 코드:
```java
@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로 일관성을 유지할 수 있습니다. 실제 데이터베이스 연동은 운영 환경과의 차이를 줄이는 데 유용합니다. 다음 장에서는 성능과 부하 테스트를 다루며, 애플리케이션의 한계를 시험해 보겠습니다!
---
이 글은 데이터 관리의 다양한 접근법을 다루며, 실습 예제를 통해 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!