들어가며

앞서 Controller → Service → Domain → Repository 계층의 데이터 흐름과 각 계층에서 발생할 수 있는 문제를 다루었습니다. 이번 3부에서는 효율적인 테스트 코드 작성 방법테스트를 통한 유지보수 전략에 대해 이야기하겠습니다.

왜 테스트 코드가 중요한가요?


1. 계층별 테스트 작성 전략

테스트 코드는 일반적으로 다음과 같은 범주로 나눌 수 있습니다:

  1. 단위 테스트(Unit Test): 특정 메서드나 클래스의 동작을 독립적으로 검증.
  2. 통합 테스트(Integration Test): 여러 계층이 함께 동작할 때의 흐름을 검증.
  3. 엔드 투 엔드 테스트(End-to-End Test): 실제 사용자 시나리오를 기반으로 전체 시스템을 검증.

각 계층별로 어떤 테스트를 작성해야 하는지 살펴보겠습니다.

1.1 Controller 테스트

Controller 테스트 예제 (MockMvc 사용):

@WebMvcTest(PostController.class)
public class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PostService postService;

    @Test
    void savePost_shouldReturnCreatedPost() throws Exception {
        // 가짜 데이터 설정
        PostDto mockPost = new PostDto(1L, "제목", "내용");
        when(postService.savePost(any(PostDto.class))).thenReturn(mockPost);

        // 요청 실행 및 결과 검증
        mockMvc.perform(post("/posts")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"title\": \"제목\", \"content\": \"내용\"}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1L))
                .andExpect(jsonPath("$.title").value("제목"))
                .andExpect(jsonPath("$.content").value("내용"));
    }
}

1.2 Service 테스트

Service 테스트 예제 (Mock Repository 사용):

java

코드 복사

@ExtendWith(MockitoExtension.class) public class PostServiceTest { @Mock private PostRepository postRepository; @InjectMocks private PostService postService; @Test void savePost_shouldSaveAndReturnPost() { // Mock 데이터 설정 Post post = new Post("제목", "내용"); when(postRepository.save(any(Post.class))).thenReturn(post); // 테스트 실행 PostDto result = postService.savePost(new PostDto(null, "제목", "내용")); // 결과 검증 assertEquals("제목", result.getTitle()); assertEquals("내용", result.getContent()); verify(postRepository, times(1)).save(any(Post.class)); } }

1.3 Domain 테스트

Domain 테스트 예제:

@ExtendWith(MockitoExtension.class)
public class PostServiceTest {

    @Mock
    private PostRepository postRepository;

    @InjectMocks
    private PostService postService;

    @Test
    void savePost_shouldSaveAndReturnPost() {
        // Mock 데이터 설정
        Post post = new Post("제목", "내용");
        when(postRepository.save(any(Post.class))).thenReturn(post);

        // 테스트 실행
        PostDto result = postService.savePost(new PostDto(null, "제목", "내용"));

        // 결과 검증
        assertEquals("제목", result.getTitle());
        assertEquals("내용", result.getContent());
        verify(postRepository, times(1)).save(any(Post.class));
    }
}

1.4 Repository 테스트

Repository 테스트 예제 (H2 데이터베이스 사용):

@DataJpaTest
public class PostRepositoryTest {

    @Autowired
    private PostRepository postRepository;

    @Test
    void saveAndFindPost_shouldWorkCorrectly() {
        // 데이터 저장
        Post post = new Post("제목", "내용");
        Post savedPost = postRepository.save(post);

        // 데이터 조회
        Post foundPost = postRepository.findById(savedPost.getId()).orElse(null);

        // 검증
        assertNotNull(foundPost);
        assertEquals("제목", foundPost.getTitle());
        assertEquals("내용", foundPost.getContent());
    }
}

2. 효율적인 테스트 전략

2.1 테스트 우선순위

모든 계층에서 테스트를 작성하려면 많은 리소스가 필요하므로, 중요도가 높은 테스트에 우선순위를 둡니다.

  1. 최우선

    • Service 계층: 비즈니스 로직의 중심이므로 반드시 검증해야 합니다.
    • Repository 계층: 데이터 저장/조회 문제가 없도록 통합 테스트를 작성합니다.
  2. 다음 우선

    • Controller 계층: 요청과 응답이 예상대로 동작하는지 MockMvc로 테스트.
  3. 선택적

    • Domain 계층: 간단한 도메인 객체는 테스트를 생략할 수 있지만, 비즈니스 규칙이 복잡한 경우 반드시 검증합니다.

2.2 Mock과 실제 객체의 적절한 사용

2.3 테스트 환경 자동화


3. 유지보수 전략

3.1 주기적인 테스트 코드 리뷰

3.2 CI/CD 파이프라인에 테스트 통합

3.3 테스트 커버리지 측정

3.4 테스트를 기반으로 리팩토링


3부 마무리

이 글에서는 계층별 테스트 작성 방법효율적인 유지보수 전략에 대해 알아보았습니다. 테스트 코드는 코드의 품질과 안정성을 유지하는 데 중요한 역할을 하며, 이를 기반으로 코드를 지속적으로 개선할 수 있습니다.

다음 단계에서는 이 계층형 아키텍처를 바탕으로 실제 서비스에 어떻게 적용할 수 있는지에 대해 더 구체적인 사례를 다뤄보겠습니다.