2025-04-08T19:56:24
This commit is contained in:
74
docs/test/01_스프링 부트 테스트.md
Normal file
74
docs/test/01_스프링 부트 테스트.md
Normal file
@@ -0,0 +1,74 @@
|
||||
아래는 **1장. 스프링 부트 테스트 개요**에 대한 글입니다. 이 장은 책의 도입부로서 독자가 스프링 부트 테스트의 중요성과 기본 개념을 이해할 수 있도록 자연스럽고 명확하게 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 1장. 스프링 부트 테스트 개요
|
||||
|
||||
스프링 부트는 현대적인 자바 애플리케이션 개발을 위한 강력한 프레임워크로, 빠른 설정과 생산성을 높이는 다양한 기능을 제공합니다. 하지만 애플리케이션이 복잡해질수록 코드의 안정성과 품질을 보장하는 일이 점점 더 중요해집니다. 여기서 테스트가 핵심적인 역할을 합니다. 이 장에서는 스프링 부트에서의 테스트가 왜 중요한지, 어떤 도구와 환경을 제공하는지, 그리고 이를 시작하기 위한 기초를 다룹니다.
|
||||
|
||||
### 1.1 테스트의 중요성과 스프링 부트에서의 역할
|
||||
|
||||
소프트웨어 개발에서 테스트는 단순히 버그를 찾는 도구가 아닙니다. 테스트는 코드가 의도한 대로 동작하는지 확인하고, 리팩토링이나 기능 추가 시 기존 로직이 깨지지 않도록 보호하며, 개발자에게 자신감을 심어주는 안전망입니다. 스프링 부트는 애플리케이션 개발 속도를 높이는 데 초점이 맞춰져 있지만, 이는 테스트 없이는 지속 가능하지 않습니다. 예를 들어, 컨트롤러가 REST API를 제대로 처리하는지, 서비스 계층이 비즈니스 로직을 정확히 수행하는지, 데이터베이스 쿼리가 예상대로 동작하는지를 확인하려면 체계적인 테스트가 필수적입니다.
|
||||
|
||||
스프링 부트는 이러한 테스트를 쉽게 작성할 수 있도록 설계되었습니다. 내장된 테스트 지원과 자동 설정 덕분에 개발자는 최소한의 노력으로 단위 테스트부터 통합 테스트까지 구현할 수 있습니다. 테스트가 잘 작성된 프로젝트는 유지보수성과 확장성이 높아지며, 팀 간 협업에서도 신뢰를 더해줍니다.
|
||||
|
||||
### 1.2 스프링 부트 테스트 프레임워크 소개
|
||||
|
||||
스프링 부트는 테스트를 위해 풍부한 도구와 라이브러리를 제공합니다. 기본적으로 `spring-boot-starter-test` 의존성을 추가하면 JUnit, AssertJ, Mockito, Spring Test 등 강력한 테스트 도구가 함께 포함됩니다. 이들은 각각의 역할을 수행하며 테스트 작성을 단순화합니다.
|
||||
|
||||
- **JUnit**: 자바 개발의 표준 테스트 프레임워크로, 테스트 케이스를 정의하고 실행하는 데 사용됩니다.
|
||||
- **Spring Test**: 스프링 컨텍스트를 로드하고 애플리케이션 환경을 시뮬레이션해 통합 테스트를 지원합니다.
|
||||
- **Mockito**: 의존성을 모킹(mock)하여 단위 테스트에서 외부 요소를 배제하고 핵심 로직만 검증할 수 있게 합니다.
|
||||
- **AssertJ**: 가독성 높은 검증 코드를 작성할 수 있는 유연한 라이브러리입니다.
|
||||
|
||||
이 외에도 스프링 부트는 REST API 테스트를 위한 `TestRestTemplate`이나 `WebTestClient`, 인메모리 데이터베이스(H2) 지원 등 실무에서 바로 활용 가능한 기능을 제공합니다. 이 책에서는 이러한 도구들을 어떻게 사용하는지 하나씩 살펴볼 것입니다.
|
||||
|
||||
### 1.3 테스트 환경 설정 기초
|
||||
|
||||
스프링 부트에서 테스트를 시작하려면 먼저 환경을 설정해야 합니다. 가장 간단한 방법은 Maven이나 Gradle 프로젝트에 `spring-boot-starter-test` 의존성을 추가하는 것입니다. 예를 들어, Maven의 경우 `pom.xml`에 다음과 같이 추가하면 됩니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
의존성이 추가되면 기본적인 테스트 클래스를 작성할 수 있습니다. 스프링 부트는 `@SpringBootTest`와 같은 애너테이션을 통해 테스트 환경을 손쉽게 구성할 수 있게 해줍니다. 예를 들어, 간단한 테스트 코드는 다음과 같습니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class MyApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
// 스프링 컨텍스트가 제대로 로드되는지 확인
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
이 코드는 애플리케이션 컨텍스트가 문제없이 로드되는지 확인하는 기본 테스트입니다. 이처럼 스프링 부트는 최소한의 설정으로 테스트를 시작할 수 있는 기반을 제공합니다.
|
||||
|
||||
### 1.4 JUnit과 스프링 부트의 통합
|
||||
|
||||
JUnit은 스프링 부트 테스트의 핵심 엔진입니다. 최신 버전인 JUnit 5는 모듈화된 구조와 강력한 기능을 제공하며, 스프링 부트와 자연스럽게 통합됩니다. JUnit 5는 크게 세 가지 모듈로 나뉩니다:
|
||||
|
||||
- **JUnit Platform**: 테스트 실행을 위한 기반 플랫폼.
|
||||
- **JUnit Jupiter**: 테스트 작성과 새로운 기능을 제공하는 엔진.
|
||||
- **JUnit Vintage**: JUnit 4와의 호환성을 지원.
|
||||
|
||||
스프링 부트는 기본적으로 JUnit Jupiter를 사용하며, `@Test`, `@BeforeEach`, `@AfterEach`와 같은 애너테이션으로 테스트 생명주기를 관리합니다. 또한, 스프링 부트의 `@SpringBootTest`와 결합하면 애플리케이션 컨텍스트를 활용한 통합 테스트도 쉽게 작성할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
이 장에서는 스프링 부트 테스트의 중요성과 기본적인 도구, 설정 방법을 살펴보았습니다. 테스트는 단순한 선택이 아니라, 안정적이고 유지보수 가능한 애플리케이션을 위한 필수 요소입니다. 다음 장부터는 단위 테스트 작성법을 시작으로 구체적인 예제와 함께 테스트 기술을 깊이 파고들어 보겠습니다. 스프링 부트의 테스트 기능을 익히며, 여러분의 코드에 자신감을 더해보세요!
|
||||
|
||||
---
|
||||
|
||||
이 글은 독자에게 친근하게 다가가면서도 기술적인 내용을 명확히 전달하는 데 초점을 맞췄습니다. 추가로 보완하거나 수정하고 싶은 부분이 있다면 말씀해 주세요!
|
||||
158
docs/test/02_단위 테스트.md
Normal file
158
docs/test/02_단위 테스트.md
Normal file
@@ -0,0 +1,158 @@
|
||||
아래는 **2장. 단위 테스트 작성**에 대한 글입니다. 이 장은 스프링 부트에서 단위 테스트의 개념을 설명하고, 실습 가능한 예제와 함께 구체적인 작성 방법을 다룹니다. 초보자와 중급 개발자 모두 이해할 수 있도록 단계적으로 접근했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 2장. 단위 테스트 작성
|
||||
|
||||
단위 테스트는 애플리케이션의 개별 구성 요소가 독립적으로 올바르게 동작하는지 확인하는 과정입니다. 스프링 부트에서는 단위 테스트를 통해 컨트롤러, 서비스, 리포지토리와 같은 각 계층의 로직을 검증할 수 있습니다. 이 장에서는 단위 테스트의 정의와 중요성, 스프링 부트에서의 설정 방법, 그리고 실습 예제를 다룹니다.
|
||||
|
||||
### 2.1 단위 테스트란 무엇인가
|
||||
|
||||
단위 테스트(Unit Test)는 코드의 가장 작은 단위—보통 메서드나 클래스—를 독립적으로 테스트하는 것을 의미합니다. 외부 의존성(데이터베이스, 외부 API 등)에 의존하지 않고, 해당 단위가 기대한 대로 동작하는지 확인하는 데 초점이 맞춰져 있습니다. 단위 테스트의 주요 장점은 다음과 같습니다:
|
||||
|
||||
- **빠른 피드백**: 실행 속도가 빠르며, 문제를 조기에 발견할 수 있습니다.
|
||||
- **리팩토링 안전성**: 코드 변경 시 기존 기능이 깨지지 않음을 보장합니다.
|
||||
- **문서화**: 테스트 코드는 코드의 사용법과 의도를 보여주는 살아있는 문서 역할을 합니다.
|
||||
|
||||
스프링 부트에서는 단위 테스트를 작성할 때 의존성 주입이나 스프링 컨텍스트를 최소화하고, 필요 시 모킹(mock)을 활용해 독립성을 유지합니다.
|
||||
|
||||
### 2.2 스프링 부트에서 단위 테스트 설정
|
||||
|
||||
스프링 부트에서 단위 테스트를 시작하려면 기본적으로 `spring-boot-starter-test` 의존성이 필요합니다(1장에서 설정 방법 참고). 단위 테스트는 스프링 컨텍스트를 로드하지 않으므로 `@SpringBootTest` 대신 JUnit의 기본 애너테이션만 사용하거나, 필요한 경우 특정 빈을 모킹합니다.
|
||||
|
||||
예를 들어, 간단한 서비스 클래스를 테스트한다고 가정해 봅시다. 먼저 테스트 클래스를 다음과 같이 설정합니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class MyServiceTest {
|
||||
|
||||
@Test
|
||||
void simpleTest() {
|
||||
MyService service = new MyService();
|
||||
String result = service.sayHello("World");
|
||||
assertThat(result).isEqualTo("Hello, World!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
위 코드는 스프링 없이 순수 자바 객체를 테스트하는 예제입니다. 하지만 스프링 부트 애플리케이션에서는 의존성이 있는 경우가 많으므로, 이를 모킹하는 방법을 배워야 합니다.
|
||||
|
||||
### 2.3 Mockito를 활용한 모킹
|
||||
|
||||
실제 애플리케이션에서는 서비스가 리포지토리나 외부 API에 의존하는 경우가 많습니다. 이런 의존성을 제거하고 단위 테스트를 독립적으로 실행하려면 **Mockito**를 사용합니다. Mockito는 가짜 객체(mock)를 만들어 의존성을 대체하며, 특정 동작을 시뮬레이션할 수 있게 해줍니다.
|
||||
|
||||
예를 들어, `UserService`가 `UserRepository`에 의존한다고 가정해 봅시다:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class UserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public String getUserName(Long id) {
|
||||
User user = userRepository.findById(id);
|
||||
return user != null ? user.getName() : "Unknown";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
이를 테스트하려면 Mockito를 사용해 `UserRepository`를 모킹합니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class) // Mockito를 JUnit 5와 통합
|
||||
class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
@Test
|
||||
void getUserName_withExistingUser_returnsName() {
|
||||
// 모킹 설정: ID가 1인 경우 "Alice"라는 사용자를 반환
|
||||
when(userRepository.findById(1L)).thenReturn(new User(1L, "Alice"));
|
||||
|
||||
String result = userService.getUserName(1L);
|
||||
assertThat(result).isEqualTo("Alice");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUserName_withNonExistingUser_returnsUnknown() {
|
||||
// 모킹 설정: ID가 2인 경우 null 반환
|
||||
when(userRepository.findById(2L)).thenReturn(null);
|
||||
|
||||
String result = userService.getUserName(2L);
|
||||
assertThat(result).isEqualTo("Unknown");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`@Mock`으로 가짜 객체를 만들고, `@InjectMocks`로 이를 주입받는 `UserService`를 테스트합니다. `when().thenReturn()`을 사용해 모킹된 객체의 동작을 정의합니다.
|
||||
|
||||
### 2.4 컨트롤러, 서비스, 리포지토리 단위 테스트 예제
|
||||
|
||||
이제 각 계층별로 단위 테스트를 작성하는 방법을 살펴보겠습니다.
|
||||
|
||||
#### 컨트롤러 단위 테스트
|
||||
컨트롤러는 HTTP 요청을 처리하므로, `@WebMvcTest`를 사용해 MVC 계층만 로드하고 의존성을 모킹합니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
|
||||
@WebMvcTest(UserController.class)
|
||||
class UserControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private UserService userService;
|
||||
|
||||
@Test
|
||||
void getUserName_returnsName() throws Exception {
|
||||
when(userService.getUserName(1L)).thenReturn("Alice");
|
||||
|
||||
mockMvc.perform(get("/users/1/name"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("Alice"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 서비스 단위 테스트
|
||||
위의 `UserServiceTest` 예제를 참고하세요. 서비스는 비즈니스 로직을 테스트하며, 의존성을 모킹해 독립적으로 검증합니다.
|
||||
|
||||
#### 리포지토리 단위 테스트
|
||||
리포지토리는 데이터베이스와 직접 상호작용하므로, 단위 테스트에서는 구현체 대신 인터페이스를 모킹하거나, 간단한 메모리 기반 구현을 사용할 수 있습니다. 통합 테스트에서 실제 DB를 다루는 경우가 많으므로, 여기서는 간단히 생략하고 3장에서 다룹니다.
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
단위 테스트는 스프링 부트 애플리케이션의 각 부분을 독립적으로 검증하며, 빠르고 신뢰할 수 있는 피드백을 제공합니다. Mockito와 같은 도구를 활용하면 복잡한 의존성도 쉽게 관리할 수 있습니다. 다음 장에서는 단위 테스트를 넘어, 애플리케이션 전체를 통합적으로 테스트하는 방법을 배워보겠습니다. 단위 테스트를 마스터하면 더 큰 테스트 세계로의 첫걸음을 내딛게 될 것입니다!
|
||||
|
||||
---
|
||||
|
||||
이 글은 실습 중심으로 작성되었으며, 코드 예제를 통해 실질적인 이해를 돕고자 했습니다. 추가 설명이나 수정이 필요하면 말씀해 주세요!
|
||||
193
docs/test/03_통합 테스트.md
Normal file
193
docs/test/03_통합 테스트.md
Normal file
@@ -0,0 +1,193 @@
|
||||
아래는 **3장. 통합 테스트**에 대한 글입니다. 이 장은 스프링 부트에서 통합 테스트의 개념을 설명하고, 실무에서 자주 사용하는 설정과 예제를 다룹니다. 단위 테스트와의 차이점을 강조하며 실습 중심으로 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 3장. 통합 테스트
|
||||
|
||||
단위 테스트가 개별 구성 요소를 독립적으로 검증한다면, 통합 테스트는 애플리케이션의 여러 부분이 함께 잘 작동하는지 확인하는 과정입니다. 스프링 부트에서는 컨트롤러, 서비스, 리포지토리가 데이터베이스나 외부 시스템과 상호작용하며 기대한 결과를 내는지 테스트할 수 있습니다. 이 장에서는 통합 테스트의 필요성, 설정 방법, 그리고 실습 예제를 다룹니다.
|
||||
|
||||
### 3.1 통합 테스트의 개념과 필요성
|
||||
|
||||
통합 테스트(Integration Test)는 애플리케이션의 여러 모듈 또는 계층이 상호작용할 때 발생할 수 있는 문제를 발견하는 데 목적이 있습니다. 단위 테스트가 모킹으로 의존성을 제거한다면, 통합 테스트는 실제 의존성을 포함해 전체 흐름을 검증합니다. 예를 들어, REST API 요청이 컨트롤러를 거쳐 서비스와 리포지토리를 호출하고, 데이터베이스에서 데이터를 가져오는 과정을 테스트한다고 생각해 봅시다. 이 과정에서 발생할 수 있는 설정 오류, 데이터 불일치, 예외 처리를 확인할 수 있습니다.
|
||||
|
||||
통합 테스트가 필요한 이유는 다음과 같습니다:
|
||||
- **현실적인 동작 확인**: 실제 환경과 유사한 조건에서 동작을 검증합니다.
|
||||
- **의존성 간 문제 탐지**: 모킹으로는 발견하기 어려운 상호작용 문제를 찾습니다.
|
||||
- **배포 전 신뢰성 확보**: 단위 테스트만으로는 놓칠 수 있는 통합된 시스템의 안정성을 보장합니다.
|
||||
|
||||
스프링 부트는 통합 테스트를 쉽게 작성할 수 있도록 강력한 도구를 제공하며, 이를 활용하는 방법을 배워보겠습니다.
|
||||
|
||||
### 3.2 @SpringBootTest 애너테이션 이해
|
||||
|
||||
스프링 부트에서 통합 테스트를 작성할 때 가장 중요한 애너테이션은 `@SpringBootTest`입니다. 이 애너테이션은 스프링 애플리케이션 컨텍스트를 로드하며, 실제 운영 환경과 유사한 설정으로 테스트를 실행합니다. 기본 사용법은 다음과 같습니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class MyApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
// 애플리케이션 컨텍스트가 로드되는지 확인
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`@SpringBootTest`는 다양한 옵션을 제공합니다:
|
||||
- `webEnvironment`: 웹 애플리케이션 환경을 설정합니다.
|
||||
- `MOCK`: 모킹된 웹 환경(기본값).
|
||||
- `RANDOM_PORT`: 실제 서버를 띄우고 랜덤 포트 사용.
|
||||
- `DEFINED_PORT`: 지정된 포트 사용.
|
||||
- `classes`: 특정 클래스만 로드해 테스트 속도를 높일 수 있습니다.
|
||||
|
||||
예를 들어, 웹 환경에서 테스트하려면 다음과 같이 설정합니다:
|
||||
|
||||
```java
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class MyWebIntegrationTests {
|
||||
// 테스트 코드
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 테스트용 데이터베이스 설정 (H2, Testcontainers)
|
||||
|
||||
통합 테스트에서는 데이터베이스와의 상호작용을 검증해야 하므로, 테스트용 데이터베이스 설정이 중요합니다. 스프링 부트는 이를 위해 두 가지 인기 있는 방법을 지원합니다: **H2 인메모리 데이터베이스**와 **Testcontainers**.
|
||||
|
||||
#### H2 인메모리 데이터베이스
|
||||
H2는 가볍고 빠른 인메모리 데이터베이스로, 설정이 간단합니다. `pom.xml`에 의존성을 추가합니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
그리고 `application-test.properties`에 설정을 추가합니다:
|
||||
|
||||
```properties
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
```
|
||||
|
||||
#### Testcontainers
|
||||
Testcontainers는 Docker를 사용해 실제 데이터베이스(예: MySQL, PostgreSQL)를 테스트 환경에서 실행합니다. 설정이 조금 복잡하지만, 운영 환경과 더 가까운 테스트를 제공합니다. 의존성을 추가합니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.19.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<version>1.19.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
테스트 클래스에서 사용 예제:
|
||||
|
||||
```java
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
class MySqlIntegrationTest {
|
||||
|
||||
@Container
|
||||
private static final MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
|
||||
.withDatabaseName("test")
|
||||
.withUsername("test")
|
||||
.withPassword("test");
|
||||
|
||||
@DynamicPropertySource
|
||||
static void mysqlProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add("spring.datasource.url", mysql::getJdbcUrl);
|
||||
registry.add("spring.datasource.username", mysql::getUsername);
|
||||
registry.add("spring.datasource.password", mysql::getPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithRealDatabase() {
|
||||
// 테스트 코드
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 REST API 통합 테스트 실습
|
||||
|
||||
이제 실제 REST API를 호출하는 통합 테스트를 작성해 봅시다. `UserController`가 있다고 가정하고, 이를 테스트합니다:
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/name")
|
||||
public String getUserName(@PathVariable Long id) {
|
||||
return userService.getUserName(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
통합 테스트 코드:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class UserControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Test
|
||||
void getUserName_returnsName() {
|
||||
// 테스트 데이터 준비
|
||||
User user = new User(1L, "Alice");
|
||||
userRepository.save(user);
|
||||
|
||||
// API 호출
|
||||
ResponseEntity<String> response = restTemplate.getForEntity("/users/1/name", String.class);
|
||||
|
||||
// 결과 검증
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isEqualTo("Alice");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`TestRestTemplate`은 실제 HTTP 요청을 보내고 응답을 검증하는 데 유용합니다. `@Transactional`을 추가하면 테스트 후 데이터가 롤백되어 깔끔하게 유지됩니다.
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
통합 테스트는 스프링 부트 애플리케이션의 전체 흐름을 검증하며, 단위 테스트로는 확인할 수 없는 현실적인 문제를 발견할 수 있습니다. `@SpringBootTest`, H2, Testcontainers를 활용하면 다양한 환경에서 테스트를 실행할 수 있습니다. 다음 장에서는 테스트를 더 효율적으로 작성하고 관리하는 유틸리티와 도구를 살펴보겠습니다. 통합 테스트를 통해 여러분의 애플리케이션에 더 큰 신뢰를 더해보세요!
|
||||
|
||||
---
|
||||
|
||||
이 글은 실습 예제와 함께 통합 테스트의 핵심을 다루었으며, 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!
|
||||
201
docs/test/04_테스트 유틸리티와 도구.md
Normal file
201
docs/test/04_테스트 유틸리티와 도구.md
Normal file
@@ -0,0 +1,201 @@
|
||||
아래는 **4장. 테스트 유틸리티와 도구**에 대한 글입니다. 이 장은 스프링 부트 테스트를 더 효율적이고 가독성 있게 작성할 수 있도록 돕는 유틸리티와 도구를 소개하며, 실습 예제를 포함해 실무에서의 활용성을 강조했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 4장. 테스트 유틸리티와 도구
|
||||
|
||||
테스트 코드는 단순히 동작을 검증하는 것을 넘어, 가독성 있고 유지보수하기 쉬워야 합니다. 스프링 부트에서는 이를 지원하는 다양한 유틸리티와 도구가 제공됩니다. 이 장에서는 AssertJ와 Hamcrest로 검증을 강화하고, 스프링 REST Docs로 API 문서를 생성하며, 테스트 코드 리팩토링 팁을 다룹니다.
|
||||
|
||||
### 4.1 AssertJ로 깔끔한 검증 작성
|
||||
|
||||
AssertJ는 JUnit의 기본 `assert` 메서드보다 더 유연하고 가독성 높은 검증을 제공하는 라이브러리입니다. `spring-boot-starter-test`에 기본 포함되어 있어 별도 설정 없이 바로 사용할 수 있습니다. AssertJ의 장점은 메서드 체이닝으로 자연스러운 문장을 만들고, 풍부한 검증 옵션을 제공한다는 점입니다.
|
||||
|
||||
예를 들어, 기본 JUnit 검증과 AssertJ를 비교해 봅시다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AssertJExampleTest {
|
||||
|
||||
@Test
|
||||
void basicJUnitAssertion() {
|
||||
String result = "Hello, World!";
|
||||
assertEquals("Hello, World!", result); // JUnit 기본 방식
|
||||
}
|
||||
|
||||
@Test
|
||||
void assertJAssertion() {
|
||||
String result = "Hello, World!";
|
||||
assertThat(result)
|
||||
.isNotNull()
|
||||
.startsWith("Hello")
|
||||
.contains("World")
|
||||
.hasLength(13); // AssertJ 방식
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
AssertJ는 리스트, 맵, 객체 등 복잡한 데이터 구조도 쉽게 검증할 수 있습니다:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void assertJListTest() {
|
||||
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
|
||||
assertThat(names)
|
||||
.hasSize(3)
|
||||
.containsExactly("Alice", "Bob", "Charlie")
|
||||
.doesNotContain("David");
|
||||
}
|
||||
```
|
||||
|
||||
AssertJ를 사용하면 실패 메시지도 더 자세히 제공되어 디버깅이 쉬워집니다.
|
||||
|
||||
### 4.2 Hamcrest를 활용한 유연한 매칭
|
||||
|
||||
Hamcrest는 테스트에서 조건을 더 유연하고 읽기 쉽게 표현할 수 있는 매처(matcher)를 제공합니다. AssertJ와 비슷한 역할을 하지만, 문법적으로 다르며 특정 상황에서 더 직관적일 수 있습니다. `spring-boot-starter-test`에도 포함되어 있습니다.
|
||||
|
||||
Hamcrest의 기본 사용법은 다음과 같습니다:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
class HamcrestExampleTest {
|
||||
|
||||
@Test
|
||||
void hamcrestBasicTest() {
|
||||
String result = "Hello, World!";
|
||||
assertThat(result, is("Hello, World!")); // 기본 매칭
|
||||
assertThat(result, startsWith("Hello"));
|
||||
assertThat(result, containsString("World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hamcrestCollectionTest() {
|
||||
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
|
||||
assertThat(numbers, hasSize(4));
|
||||
assertThat(numbers, hasItem(greaterThan(2)));
|
||||
assertThat(numbers, everyItem(lessThan(5)));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hamcrest는 특히 컬렉션이나 복잡한 조건을 테스트할 때 유용하며, AssertJ와 함께 사용해도 무방합니다. 상황에 따라 두 도구를 혼합해 가독성을 높일 수 있습니다.
|
||||
|
||||
### 4.3 스프링 REST Docs로 API 문서화
|
||||
|
||||
스프링 REST Docs는 테스트를 기반으로 REST API 문서를 자동 생성하는 도구입니다. 기존 Swagger와 달리 테스트가 실패하면 문서도 갱신되지 않으므로, 항상 정확한 문서를 유지할 수 있습니다. 설정은 약간 복잡하지만, 품질 높은 문서를 위해 투자할 가치가 있습니다.
|
||||
|
||||
의존성을 추가합니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
테스트 예제:
|
||||
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(UserController.class)
|
||||
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
class UserControllerRestDocsTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
void getUserName_documentation() throws Exception {
|
||||
mockMvc.perform(get("/users/1/name"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("user-get-name",
|
||||
responseFields(
|
||||
fieldWithPath("name").description("The name of the user")
|
||||
)));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
위 코드는 `target/snippets` 폴더에 문서 조각을 생성하며, 이를 Asciidoctor로 통합해 HTML 문서로 변환할 수 있습니다. 예를 들어, `pom.xml`에 빌드 플러그인을 추가해 문서를 생성합니다:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirectory>src/docs/asciidoc</sourceDirectory>
|
||||
<attributes>
|
||||
<snippets>${project.build.directory}/snippets</snippets>
|
||||
</attributes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
```
|
||||
|
||||
### 4.4 테스트 코드 리팩토링 팁
|
||||
|
||||
좋은 테스트 코드는 명확하고 중복이 적어야 합니다. 다음은 리팩토링 팁입니다:
|
||||
|
||||
- **공통 설정 분리**: `@BeforeEach`나 별도의 유틸리티 클래스를 활용해 반복 코드를 줄입니다.
|
||||
```java
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userRepository.save(new User(1L, "Alice"));
|
||||
}
|
||||
```
|
||||
- **테스트 메서드 이름 명확히**: `getUserName_withExistingUser_returnsName`처럼 의도를 드러내게 작성합니다.
|
||||
- **작은 테스트 유지**: 한 테스트가 너무 많은 것을 검증하지 않도록 단일 책임 원칙을 따릅니다.
|
||||
- **커스텀 Matcher/Assertion 사용**: AssertJ나 Hamcrest로 자주 사용하는 검증 로직을 재사용 가능하게 만듭니다.
|
||||
|
||||
예를 들어, 커스텀 AssertJ assertion:
|
||||
```java
|
||||
public class UserAssertions {
|
||||
public static AbstractStringAssert<?> assertThatUserName(String actual) {
|
||||
return assertThat(actual).isNotNull().isNotEmpty();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
사용:
|
||||
```java
|
||||
@Test
|
||||
void testUserName() {
|
||||
String name = userService.getUserName(1L);
|
||||
UserAssertions.assertThatUserName(name).isEqualTo("Alice");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
AssertJ와 Hamcrest는 테스트 검증을 풍부하게 하고, 스프링 REST Docs는 API 문서화를 혁신하며, 리팩토링은 테스트 코드의 품질을 높입니다. 이러한 도구와 기법을 활용하면 테스트가 단순한 검증을 넘어 팀과 프로젝트에 실질적인 가치를 더할 수 있습니다. 다음 장에서는 테스트 데이터 관리 전략을 다루며, 통합 테스트를 더 깊이 탐구해 보겠습니다!
|
||||
|
||||
---
|
||||
|
||||
이 글은 도구별 특징과 실습 예제를 중심으로 작성되었으며, 실무에서의 활용성을 높였습니다. 추가 요청이 있다면 말씀해 주세요!
|
||||
207
docs/test/05_테스트와 데이터 관리.md
Normal file
207
docs/test/05_테스트와 데이터 관리.md
Normal 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로 일관성을 유지할 수 있습니다. 실제 데이터베이스 연동은 운영 환경과의 차이를 줄이는 데 유용합니다. 다음 장에서는 성능과 부하 테스트를 다루며, 애플리케이션의 한계를 시험해 보겠습니다!
|
||||
|
||||
---
|
||||
|
||||
이 글은 데이터 관리의 다양한 접근법을 다루며, 실습 예제를 통해 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!
|
||||
135
docs/test/06_성능 및 부하 테스트.md
Normal file
135
docs/test/06_성능 및 부하 테스트.md
Normal file
@@ -0,0 +1,135 @@
|
||||
아래는 **6장. 성능 및 부하 테스트**에 대한 글입니다. 이 장은 스프링 부트 애플리케이션의 성능과 부하를 테스트하는 방법을 다루며, 실무에서 자주 사용하는 도구와 분석 방법을 소개합니다. 실습 예제를 포함해 실질적인 이해를 돕고자 했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 6장. 성능 및 부하 테스트
|
||||
|
||||
애플리케이션의 기능이 올바르게 동작하는지를 확인하는 단위 및 통합 테스트를 넘어, 실제 사용자 트래픽을 감당할 수 있는지 확인하는 성능 및 부하 테스트는 필수적입니다. 스프링 부트 애플리케이션의 성능 병목 지점을 찾고, 확장성을 검증하는 방법을 이 장에서 다룹니다. JMeter와 Gatling을 활용한 실습과 결과 분석까지 포함했습니다.
|
||||
|
||||
### 6.1 스프링 부트 애플리케이션 성능 테스트 개요
|
||||
|
||||
성능 테스트는 애플리케이션이 특정 조건에서 얼마나 잘 동작하는지 측정합니다. 부하 테스트는 그 하위 범주로, 높은 트래픽이나 동시 사용자 요청을 시뮬레이션해 시스템의 한계를 확인합니다. 주요 목표는 다음과 같습니다:
|
||||
- **응답 시간**: 요청 처리 속도가 적절한지.
|
||||
- **처리량(Throughput)**: 초당 처리할 수 있는 요청 수.
|
||||
- **안정성**: 부하 증가 시 시스템이崩溃하지 않는지.
|
||||
|
||||
스프링 부트는 내장 톰캣 서버와 자동 설정으로 빠르게 실행되지만, 데이터베이스 연결, 캐싱, 비동기 처리 등에서 병목이 발생할 수 있습니다. 이를 테스트로 사전에 파악해야 합니다.
|
||||
|
||||
### 6.2 JMeter를 활용한 부하 테스트
|
||||
|
||||
Apache JMeter는 오픈소스 부하 테스트 도구로, HTTP 요청을 시뮬레이션하고 결과를 분석하는 데 유용합니다. 설치 후 간단한 테스트를 설정해 봅시다.
|
||||
|
||||
#### JMeter 설정
|
||||
1. [JMeter 공식 사이트](https://jmeter.apache.org/)에서 다운로드 후 실행.
|
||||
2. 테스트 계획(Test Plan) 생성:
|
||||
- **스레드 그룹(Thread Group)**: 동시 사용자 수(예: 100명), 반복 횟수 설정.
|
||||
- **HTTP 요청(HTTP Request)**: 대상 URL(예: `http://localhost:8080/users/1/name`) 입력.
|
||||
- **리스너(Listener)**: 결과 트리(View Results Tree), 요약 리포트(Summary Report) 추가.
|
||||
|
||||
#### 예제 테스트
|
||||
`UserController`의 `/users/1/name` 엔드포인트를 테스트한다고 가정:
|
||||
- 스레드 수: 100
|
||||
- 반복 횟수: 10
|
||||
- 요청: GET `/users/1/name`
|
||||
|
||||
JMeter에서 테스트 실행 후 결과:
|
||||
- **평균 응답 시간**: 50ms
|
||||
- **처리량**: 200 req/s
|
||||
- **에러율**: 0%
|
||||
|
||||
#### 분석
|
||||
응답 시간이 길거나 에러가 발생하면 데이터베이스 쿼리 최적화, 캐싱 추가 등 개선이 필요합니다. JMeter는 CSV로 결과를 내보내 상세 분석도 가능합니다.
|
||||
|
||||
### 6.3 Gatling으로 시나리오 기반 테스트
|
||||
|
||||
Gatling은 스칼라 기반의 고성능 부하 테스트 도구로, 시나리오를 코드로 작성해 복잡한 사용자 행동을 시뮬레이션할 수 있습니다. JMeter보다 가볍고, HTML 리포트가 직관적입니다.
|
||||
|
||||
#### Gatling 설정
|
||||
의존성 추가 (Maven 프로젝트로 설정):
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.gatling.highcharts</groupId>
|
||||
<artifactId>gatling-charts-highcharts</artifactId>
|
||||
<version>3.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 테스트 시나리오 작성
|
||||
`src/test/scala`에 시나리오 작성:
|
||||
|
||||
```scala
|
||||
import io.gatling.core.Predef._
|
||||
import io.gatling.http.Predef._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class UserApiSimulation extends Simulation {
|
||||
val httpProtocol = http
|
||||
.baseUrl("http://localhost:8080")
|
||||
.acceptHeader("application/json")
|
||||
|
||||
val scn = scenario("User API Load Test")
|
||||
.exec(http("Get User Name")
|
||||
.get("/users/1/name"))
|
||||
.pause(1) // 1초 대기
|
||||
|
||||
setUp(
|
||||
scn.inject(
|
||||
rampUsers(100).during(10.seconds) // 10초 동안 100명 증가
|
||||
)
|
||||
).protocols(httpProtocol)
|
||||
}
|
||||
```
|
||||
|
||||
#### 실행 및 결과
|
||||
`mvn gatling:test`로 실행하면 HTML 리포트가 `target/gatling`에 생성됩니다. 리포트는 응답 시간 분포, 요청별 통계 등을 시각화합니다.
|
||||
|
||||
### 6.4 성능 테스트 결과 분석
|
||||
|
||||
성능 테스트 결과를 분석하려면 다음 지표를 확인합니다:
|
||||
- **응답 시간 분포**: 90번째 백분위수(90% 요청이 이 시간 내 처리됨)가 중요한 기준.
|
||||
- **에러율**: 1% 이상이면 문제 원인을 조사.
|
||||
- **리소스 사용량**: CPU, 메모리, 디스크 I/O를 모니터링(예: `top`, Prometheus).
|
||||
|
||||
#### 예제 분석
|
||||
JMeter 결과:
|
||||
- 평균 응답 시간: 50ms
|
||||
- 90% 응답 시간: 70ms
|
||||
- 처리량: 200 req/s
|
||||
|
||||
Gatling 결과:
|
||||
- 최대 응답 시간: 120ms
|
||||
- 실패 요청: 0건
|
||||
|
||||
응답 시간이 100ms를 넘거나 실패가 발생하면 다음 조치를 고려합니다:
|
||||
- **쿼리 최적화**: 인덱스 추가, 불필요한 조인 제거.
|
||||
- **캐싱**: Redis나 Spring Cache 도입.
|
||||
- **스케일링**: 서버 인스턴스 추가.
|
||||
|
||||
#### 모니터링 도구 연동
|
||||
Spring Boot Actuator를 활성화해 성능 지표를 수집할 수 있습니다:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
`application.properties`:
|
||||
```properties
|
||||
management.endpoints.web.exposure.include=health,metrics
|
||||
```
|
||||
|
||||
`/actuator/metrics` 엔드포인트로 HTTP 요청 수, 응답 시간 등을 확인하며 테스트와 연계합니다.
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
성능 및 부하 테스트는 스프링 부트 애플리케이션이 실전에서 안정적으로 동작할 수 있는지 확인하는 마지막 관문입니다. JMeter로 간단한 부하를 시뮬레이션하고, Gatling으로 복잡한 시나리오를 테스트하며, 결과를 분석해 병목을 개선할 수 있습니다. 다음 장에서는 CI/CD 파이프라인에 테스트를 통합해 개발 프로세스를 자동화하는 방법을 다루겠습니다. 성능 테스트로 여러분의 애플리케이션을 한 단계 업그레이드해 보세요!
|
||||
|
||||
---
|
||||
|
||||
이 글은 성능 테스트의 핵심 개념과 도구 사용법을 다루며, 실습과 분석을 통해 실무 적용 가능성을 높였습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!
|
||||
137
docs/test/09_테스트 피라미드와 실무 적용.md
Normal file
137
docs/test/09_테스트 피라미드와 실무 적용.md
Normal file
@@ -0,0 +1,137 @@
|
||||
아래는 **9장. 테스트 피라미드와 실무 적용**에 대한 글입니다. 이 장은 테스트 피라미드라는 이론적 프레임워크를 기반으로 스프링 부트 프로젝트에서 실무적으로 테스트를 적용하는 방법을 다룹니다. 실습 예제와 실무 팁을 포함해 실질적인 가치를 제공하도록 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 9장. 테스트 피라미드와 실무 적용
|
||||
|
||||
테스트는 단순히 코드를 검증하는 도구를 넘어, 프로젝트의 품질과 유지보수성을 높이는 전략입니다. 테스트 피라미드는 이를 체계적으로 설계하는 데 유용한 개념으로, 스프링 부트 프로젝트에서 어떻게 적용할 수 있는지 이 장에서 다룹니다. 실무에서의 전략, 흔한 실수, 그리고 성공적인 테스트 문화를 구축하는 방법까지 살펴보겠습니다.
|
||||
|
||||
### 9.1 테스트 피라미드 이해
|
||||
|
||||
테스트 피라미드는 테스트의 종류와 비율을 계층적으로 표현한 모델입니다. 아래에서 위로 갈수록 테스트 범위는 넓어지지만, 수와 실행 비용은 줄어듭니다. 주요 계층은 다음과 같습니다:
|
||||
|
||||
- **단위 테스트(Unit Tests)**: 가장 아래층으로, 개별 메서드나 클래스를 독립적으로 검증. 빠르고 수가 많음.
|
||||
- **통합 테스트(Integration Tests)**: 중간층으로, 여러 모듈이나 계층 간 상호작용을 테스트. 단위 테스트보다 느림.
|
||||
- **엔드투엔드 테스트(E2E Tests)**: 최상층으로, 사용자 관점에서 전체 시스템을 검증. 가장 느리고 수가 적음.
|
||||
|
||||
스프링 부트에서:
|
||||
- 단위 테스트: 서비스 로직, 유틸리티 메서드 (예: `UserService.getUserName()`).
|
||||
- 통합 테스트: 컨트롤러와 리포지토리 연동 (예: REST API 호출).
|
||||
- E2E 테스트: 브라우저나 클라이언트를 통한 전체 워크플로우.
|
||||
|
||||
피라미드의 핵심은 **단위 테스트를 기반으로 하되, 필요한 만큼 상위 테스트를 보완**하는 것입니다. 단위 테스트가 70-80%, 통합 테스트가 15-25%, E2E 테스트가 5-10% 정도가 일반적인 비율입니다.
|
||||
|
||||
### 9.2 실무에서의 테스트 전략 수립
|
||||
|
||||
스프링 부트 프로젝트에서 테스트 전략을 수립하려면 다음 단계를 따릅니다:
|
||||
|
||||
1. **프로젝트 요구사항 분석**:
|
||||
- 빠른 피드백이 중요한가? → 단위 테스트 강화.
|
||||
- 외부 시스템 연동이 많나? → 통합 테스트 필요.
|
||||
- UI 중심인가? → E2E 테스트 고려.
|
||||
|
||||
2. **테스트 범위 정의**:
|
||||
- 핵심 비즈니스 로직은 단위 테스트로 100% 커버.
|
||||
- API 엔드포인트는 통합 테스트로 검증.
|
||||
- 주요 사용자 시나리오는 E2E로 확인.
|
||||
|
||||
3. **도구 선택**:
|
||||
- 단위: JUnit, Mockito, AssertJ.
|
||||
- 통합: `@SpringBootTest`, Testcontainers.
|
||||
- E2E: Selenium, Cypress (웹 애플리케이션용).
|
||||
|
||||
#### 예제 전략
|
||||
간단한 사용자 관리 API 프로젝트:
|
||||
- **단위 테스트**: `UserService`의 `createUser()`, `getUserName()` 메서드.
|
||||
- **통합 테스트**: `/users` 엔드포인트 호출 및 DB 저장 확인.
|
||||
- **E2E 테스트**: 사용자 등록 후 이름 조회 시나리오.
|
||||
|
||||
```java
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class UserApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
void createAndGetUser() {
|
||||
// 통합 테스트: 사용자 생성
|
||||
ResponseEntity<String> createResponse = restTemplate.postForEntity("/users", new User(null, "Alice"), String.class);
|
||||
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||
|
||||
// 통합 테스트: 사용자 조회
|
||||
ResponseEntity<String> getResponse = restTemplate.getForEntity("/users/1/name", String.class);
|
||||
assertThat(getResponse.getBody()).isEqualTo("Alice");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 흔한 테스트 실수와 해결 방법
|
||||
|
||||
실무에서 자주 발생하는 실수와 대처법을 정리했습니다:
|
||||
|
||||
- **실수 1: 모든 것을 통합 테스트로 작성**
|
||||
- 문제: 느리고 유지보수가 어려움.
|
||||
- 해결: 단위 테스트로 분리 가능한 로직을 옮기고, 통합 테스트는 핵심 흐름에 집중.
|
||||
|
||||
- **실수 2: 모킹 과다 사용**
|
||||
- 문제: 실제 동작과 달라 신뢰성 하락.
|
||||
- 해결: 모킹은 외부 의존성에만 사용하고, 내부 로직은 실제 구현으로 테스트.
|
||||
|
||||
- **실수 3: 테스트 데이터 관리 부족**
|
||||
- 문제: 테스트 간 간섭으로 실패.
|
||||
- 해결: `@Transactional` 사용하거나, 각 테스트마다 고유 데이터 생성.
|
||||
|
||||
- **실수 4: 커버리지에 집착**
|
||||
- 문제: 의미 없는 테스트 증가.
|
||||
- 해결: 핵심 로직의 품질에 집중하고, 커버리지는 참고 지표로만 활용.
|
||||
|
||||
#### 예제: 실수 수정
|
||||
```java
|
||||
// 잘못된 예: 모든 것을 통합 테스트로
|
||||
@SpringBootTest
|
||||
class BadTest {
|
||||
@Test
|
||||
void testEverything() {
|
||||
// 너무 많은 검증
|
||||
}
|
||||
}
|
||||
|
||||
// 개선된 예: 단위 테스트로 분리
|
||||
class UserServiceTest {
|
||||
@Mock
|
||||
private UserRepository repository;
|
||||
|
||||
@InjectMocks
|
||||
private UserService service;
|
||||
|
||||
@Test
|
||||
void getUserName() {
|
||||
when(repository.findById(1L)).thenReturn(new User(1L, "Alice"));
|
||||
assertThat(service.getUserName(1L)).isEqualTo("Alice");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 성공적인 테스트 문화 구축
|
||||
|
||||
테스트를 프로젝트의 핵심 요소로 만들려면 팀 차원의 노력이 필요합니다:
|
||||
- **교육과 공유**: 신입 개발자에게 테스트 작성법을 가르치고, 코드 리뷰에서 테스트를 점검.
|
||||
- **자동화**: CI/CD에 테스트를 통합해 빠른 피드백 제공 (7장 참고).
|
||||
- **책임 분담**: 모든 팀원이 테스트 작성에 참여하도록 장려.
|
||||
- **측정과 개선**: 커버리지와 실패율을 모니터링하며 지속적으로 개선.
|
||||
|
||||
#### 실무 팁
|
||||
- **테스트 주도 개발(TDD)**: 요구사항을 테스트로 먼저 작성 후 구현.
|
||||
- **페어 프로그래밍**: 테스트 코드를 함께 작성하며 품질 향상.
|
||||
- **리팩토링 시간 확보**: 테스트가 없으면 리팩토링이 두려워지므로, 초기에 투자.
|
||||
|
||||
---
|
||||
|
||||
### 마무리
|
||||
|
||||
테스트 피라미드는 스프링 부트 프로젝트에서 테스트를 체계적으로 설계하는 가이드입니다. 단위 테스트로 기반을 다지고, 통합 테스트로 상호작용을 검증하며, E2E 테스트로 사용자 경험을 보장합니다. 실무에서 흔한 실수를 피하고, 팀 전체가 테스트 문화를 받아들이면 안정적이고 확장 가능한 애플리케이션을 만들 수 있습니다. 다음 장에서는 실제 사례를 통해 배운 교훈을 공유하며 이 책을 마무리하겠습니다!
|
||||
|
||||
---
|
||||
|
||||
이 글은 테스트 피라미드의 이론과 실무 적용을 균형 있게 다루며, 실습과 팁으로 실질적인 도움을 주고자 했습니다. 추가 요청이나 수정 사항이 있다면 말씀해 주세요!
|
||||
51
docs/test/11_애너테이션.md
Normal file
51
docs/test/11_애너테이션.md
Normal file
@@ -0,0 +1,51 @@
|
||||
아래는 스프링 부트 테스트에서 자주 사용되는 애너테이션들을 **JUnit**, **Spring Test**, **Mockito**로 구분하고, 표 형식으로 정리한 내용입니다. 각 애너테이션의 역할과 설명을 간결하게 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
### JUnit 관련 애너테이션
|
||||
| 애너테이션 | 설명 |
|
||||
|-----------------------|----------------------------------------------------------------------|
|
||||
| `@Test` | 테스트 메서드를 정의. 별도 설정 없이 실행 가능한 기본 단위 테스트. |
|
||||
| `@BeforeEach` | 각 테스트 메서드 실행 전에 실행되는 설정 메서드를 지정. |
|
||||
| `@AfterEach` | 각 테스트 메서드 실행 후에 실행되는 정리 메서드를 지정. |
|
||||
| `@BeforeAll` | 모든 테스트 실행 전에 한 번 실행되는 클래스 수준 설정 메서드를 지정. (static 필요) |
|
||||
| `@AfterAll` | 모든 테스트 실행 후에 한 번 실행되는 클래스 수준 정리 메서드를 지정. (static 필요) |
|
||||
| `@Disabled` | 특정 테스트 메서드나 클래스를 비활성화하여 실행에서 제외. |
|
||||
| `@DisplayName` | 테스트 메서드에 사용자 친화적인 이름을 지정해 가독성 향상. |
|
||||
| `@ParameterizedTest` | 동일한 테스트를 여러 데이터로 반복 실행. (예: 값 소스와 함께 사용) |
|
||||
| `@Nested` | 테스트 클래스 내에 계층적 구조를 만들어 관련 테스트를 그룹화. |
|
||||
|
||||
---
|
||||
|
||||
### Spring Test 관련 애너테이션
|
||||
| 애너테이션 | 설명 |
|
||||
|-------------------------|----------------------------------------------------------------------------------------|
|
||||
| `@SpringBootTest` | 스프링 부트 애플리케이션 컨텍스트를 로드하여 통합 테스트를 실행. (webEnvironment 설정 가능) |
|
||||
| `@WebMvcTest` | MVC 계층(컨트롤러)만 로드해 단위 테스트. 서비스나 리포지토리는 모킹 필요. |
|
||||
| `@DataJpaTest` | JPA 관련 테스트를 위해 리포지토리와 데이터베이스 설정만 로드. (H2 기본 사용) |
|
||||
| `@AutoConfigureMockMvc`| MockMvc를 자동 설정해 컨트롤러 테스트에 사용. |
|
||||
| `@Transactional` | 테스트 메서드 실행 후 트랜잭션을 롤백하여 데이터베이스 상태를 초기화. |
|
||||
| `@Rollback` | `@Transactional`과 함께 사용해 롤백 여부를 명시적으로 설정. (기본: true) |
|
||||
| `@Testcontainers` | Testcontainers를 사용해 Docker 기반 데이터베이스 등을 테스트 환경에 통합. |
|
||||
| `@DynamicPropertySource`| 테스트에서 동적으로 프로퍼티(예: 데이터베이스 URL)를 설정. (Testcontainers와 자주 사용) |
|
||||
| `@AutoConfigureRestDocs`| 스프링 REST Docs를 자동 설정해 API 문서 생성을 지원. |
|
||||
|
||||
---
|
||||
|
||||
### Mockito 관련 애너테이션
|
||||
| 애너테이션 | 설명 |
|
||||
|------------------------|----------------------------------------------------------------------------------------|
|
||||
| `@Mock` | 모킹된 가짜 객체를 생성. 외부 의존성을 대체할 때 사용. |
|
||||
| `@InjectMocks` | 모킹된 객체를 실제 객체에 주입해 테스트 대상 객체를 초기화. |
|
||||
| `@Spy` | 실제 객체를 감싸 일부 메서드만 모킹 가능. 전체 동작을 유지하면서 특정 동작만 변경. |
|
||||
| `@MockBean` | 스프링 컨텍스트에 모킹된 빈을 등록. `@SpringBootTest`나 `@WebMvcTest`에서 사용. |
|
||||
| `@ExtendWith(MockitoExtension.class)` | JUnit 5와 Mockito를 통합해 `@Mock`, `@InjectMocks` 등을 활성화. |
|
||||
|
||||
---
|
||||
|
||||
### 추가 참고
|
||||
- **JUnit**: 테스트 실행의 기본 프레임워크로, 단위 테스트와 통합 테스트 모두에서 사용됩니다.
|
||||
- **Spring Test**: 스프링 부트의 컨텍스트와 통합된 테스트를 지원하며, 계층별 테스트에 특화된 애너테이션을 제공합니다.
|
||||
- **Mockito**: 의존성을 모킹해 단위 테스트를 독립적으로 실행하거나, 통합 테스트에서 특정 빈을 대체하는 데 유용합니다.
|
||||
|
||||
이 표는 책의 **부록 A. 자주 사용하는 테스트 애너테이션 정리**에 포함시키기에 적합합니다. 필요하면 특정 애너테이션에 대한 예제나 더 자세한 설명을 추가할 수 있으니 말씀해 주세요!
|
||||
74
docs/test/README.md
Normal file
74
docs/test/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
스프링 부트에서의 테스트에 관한 책의 목차를 작성해 드리겠습니다. 이 목차는 초보자부터 중급 개발자까지 유용하게 사용할 수 있도록 구성했으며, 실무에서 자주 다루는 주제를 포함했습니다. 아래는 제안하는 목차입니다:
|
||||
|
||||
---
|
||||
|
||||
### 목차
|
||||
|
||||
#### 1장. 스프링 부트 테스트 개요
|
||||
- 1.1 테스트의 중요성과 스프링 부트에서의 역할
|
||||
- 1.2 스프링 부트 테스트 프레임워크 소개
|
||||
- 1.3 테스트 환경 설정 기초
|
||||
- 1.4 JUnit과 스프링 부트의 통합
|
||||
|
||||
#### 2장. 단위 테스트 작성
|
||||
- 2.1 단위 테스트란 무엇인가
|
||||
- 2.2 스프링 부트에서 단위 테스트 설정
|
||||
- 2.3 Mockito를 활용한 모킹
|
||||
- 2.4 컨트롤러, 서비스, 리포지토리 단위 테스트 예제
|
||||
|
||||
#### 3장. 통합 테스트
|
||||
- 3.1 통합 테스트의 개념과 필요성
|
||||
- 3.2 @SpringBootTest 애너테이션 이해
|
||||
- 3.3 테스트용 데이터베이스 설정 (H2, Testcontainers)
|
||||
- 3.4 REST API 통합 테스트 실습
|
||||
|
||||
#### 4장. 테스트 유틸리티와 도구
|
||||
- 4.1 AssertJ로 깔끔한 검증 작성
|
||||
- 4.2 Hamcrest를 활용한 유연한 매칭
|
||||
- 4.3 스프링 REST Docs로 API 문서화
|
||||
- 4.4 테스트 코드 리팩토링 팁
|
||||
|
||||
#### 5장. 테스트와 데이터 관리
|
||||
- 5.1 테스트 데이터 생성 전략
|
||||
- 5.2 @Transactional과 데이터 롤백
|
||||
- 5.3 Flyway 또는 Liquibase를 활용한 테스트 데이터 마이그레이션
|
||||
- 5.4 실제 데이터베이스와의 연동 테스트
|
||||
|
||||
#### 6장. 성능 및 부하 테스트
|
||||
- 6.1 스프링 부트 애플리케이션 성능 테스트 개요
|
||||
- 6.2 JMeter를 활용한 부하 테스트
|
||||
- 6.3 Gatling으로 시나리오 기반 테스트
|
||||
- 6.4 성능 테스트 결과 분석
|
||||
|
||||
#### 7장. 테스트와 CI/CD 통합
|
||||
- 7.1 CI/CD 파이프라인에서 테스트 실행
|
||||
- 7.2 GitHub Actions로 테스트 자동화
|
||||
- 7.3 Jenkins와 스프링 부트 테스트 연동
|
||||
- 7.4 테스트 커버리지 측정과 리포팅
|
||||
|
||||
#### 8장. 고급 테스트 기법
|
||||
- 8.1 비동기 코드 테스트 (@Async, CompletableFuture)
|
||||
- 8.2 스프링 시큐리티와 함께하는 보안 테스트
|
||||
- 8.3 웹소켓 테스트
|
||||
- 8.4 스프링 배치 테스트
|
||||
|
||||
#### 9장. 테스트 피라미드와 실무 적용
|
||||
- 9.1 테스트 피라미드 이해
|
||||
- 9.2 실무에서의 테스트 전략 수립
|
||||
- 9.3 흔한 테스트 실수와 해결 방법
|
||||
- 9.4 성공적인 테스트 문화 구축
|
||||
|
||||
#### 10장. 사례 연구 및 모범 사례
|
||||
- 10.1 실제 프로젝트에서의 테스트 사례
|
||||
- 10.2 오픈소스 프로젝트의 테스트 분석
|
||||
- 10.3 모범 사례와 교훈
|
||||
- 10.4 추가 학습 리소스
|
||||
|
||||
#### 부록
|
||||
- A. 자주 사용하는 테스트 애너테이션 정리
|
||||
- B. 테스트 관련 라이브러리 설치 가이드
|
||||
- C. 샘플 프로젝트 GitHub 링크
|
||||
|
||||
---
|
||||
|
||||
이 목차는 스프링 부트 테스트의 기초부터 고급 주제까지 다루며, 실무에서 바로 적용 가능한 내용을 중심으로 설계되었습니다. 특정 주제에 더 깊이 들어가고 싶거나 다른 섹션을 추가하고 싶다면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user