초보 개발자와 실무 프로 개발자를 가르는 가장 거대하고 뚜렷한 경계선은 바로 '테스트 코드(Test Code)'의 존재 여부입니다. 학원 프로젝트 시절에는 기능을 구현하고 서버를 켠 뒤, 포스트맨(Postman)으로 API를 한 번 호출해 보고 성공이 뜨면 "우와! 다 됐다!" 하고 환호하며 코딩을 끝냈습니다. 하지만 6개월 뒤, A 기능을 살짝 수정했는데 전혀 상관없어 보이던 B 기능과 C 기능이 연쇄적으로 펑펑 터지기 시작합니다. 어디서부터 손대야 할지 몰라 밤을 새우는 '레거시 코드의 늪'에 빠지게 되죠. 만약 수백 개의 기능이 "나는 지금 정상적으로 작동하고 있어!"라고 매번 자동 검증해 주는 시스템이 있었다면 어땠을까요? 코드가 코드를 검증하고, 내 로직이 100% 옳다는 것을 수학적으로 증명해 내는 마법이 바로 테스트 코드입니다.
이번 포스팅에서는 "백엔드 개발의 진정한 자존심이자 생명줄!" JUnit 5와 Mockito를 활용한 단위 테스트(Unit Test) 작성 비법에 대하여 다뤄보겠습니다.

🛡️ 1. Given-When-Then 패턴: 명확한 테스트의 3단계 뼈대
테스트 코드를 처음 짤 때 가장 막막한 것은 "도대체 어떻게 시작해야 하지?"입니다. 전 세계 개발자들이 사랑하는 가장 명확하고 아름다운 BDD(Behavior-Driven Development) 테스트 작성 템플릿이 있습니다. 무조건 이 3단계 주석을 깔아두고 시작하세요.

- Given (준비): 테스트를 위한 상황(데이터, 가짜 객체)을 세팅합니다. "어떤 데이터가 주어졌을 때"
- When (실행): 우리가 실제로 테스트하고 검증하고 싶은 그 핵심 '메서드'를 실행합니다. "무언가를 하면"
- Then (검증): 실행 결과가 내가 예상한 값(Expected)과 일치하는지 단언(Assert)합니다. "이런 결과가 나와야 한다!"
🎭 2. Mockito의 마법: 외부 통신을 싹 다 끊고 고립시켜라!
가장 중요한 단위 테스트(Unit Test)의 철학은 "오직 내 코드(Service 비즈니스 로직)만 순수하게 검증한다"는 것입니다. 회원가입 로직을 테스트하는데 실제 MySQL 데이터베이스까지 가서 쿼리가 잘 박히는지 확인하면, 그건 통합 테스트(Integration Test)지 단위 테스트가 아닙니다. DB가 꺼져있으면 테스트가 실패해버리는, 외부 환경에 의존적인 쓰레기(?) 테스트가 됩니다.
DB 연동, S3 업로드 등 나머지 외부 의존성들을 전부 '가짜 장난감(Mock)'으로 흉내 내어버리고 순수 로직만 검증하게 해주는 미친 라이브러리가 바로 Mockito입니다.
💻 3. 완벽한 비즈니스 로직 테스트 실전 코드 (Service Test)
UserService의 '회원가입(Signup)' 로직을 테스트해 봅시다. 여기에는 "이메일 중복이면 예외(Exception)가 터져야 한다!"라는 핵심 비즈니스 룰이 있습니다.
// Spring Boot 껍데기(톰캣)를 안 띄우고 순수 Java 코드로만 번개처럼 빠르게 돕니다!
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@InjectMocks // 진짜로 테스트할 대상 (얘한테 가짜들을 쑤셔 넣음)
private UserService userService;
@Mock // 가짜(Mock) 객체 만들기 (진짜 DB 안 찌름!)
private UserRepository userRepository;
@Test
@DisplayName("회원가입 실패: 중복된 이메일이면 예외가 터져야 한다")
void signup_fail_duplicate_email() {
// [1] Given (준비)
String duplicateEmail = "test@gmail.com";
UserSignupDto dto = new UserSignupDto(duplicateEmail, "1234");
// Mockito의 마법: "만약 가짜 Repository한테 저 이메일로 중복검사 물어보면, 무조건 true(중복)라고 대답해 줘!" 라고 미리 세팅(조작)함
given(userRepository.existsByEmail(duplicateEmail)).willReturn(true);
// [2] When & Then (실행 및 검증)
// 저 DTO를 넣고 가입 메서드를 돌리면(When), IllegalArgumentException 에러가 터져야만 한다!(Then)고 단언(Assert)
assertThrows(IllegalArgumentException.class, () -> {
userService.registerUser(dto);
});
// 부가 검증: 가입 로직이 터졌으니, DB 저장(save) 메서드는 단 한 번도 호출되지 않았음을 맹세해라!
verify(userRepository, never()).save(any());
}
}
이 테스트 코드는 서버나 DB를 켜지 않아도 마우스 클릭 0.05초 만에 초록색 [PASSED] 불을 띄우며 "네 비즈니스 로직은 예외 처리를 완벽하게 해내고 있어!"라고 증명해 줍니다. 이런 테스트 코드가 500개 쌓여있다면? 여러분은 두 다리 쭉 뻗고 숙면을 취하며, 거침없이 코드 리팩토링을 감행할 수 있는 불사신의 심장을 가지게 됩니다.
🎯 4. 마무리 및 다음 단계
지금까지 코드 변경에 대한 공포심을 지워버리고 완벽한 TDD(테스트 주도 개발)의 초석을 다지기 위한, Given-When-Then 구조와 Mockito 가짜 객체를 활용한 초고속 Service 단위 테스트 작성 가이드에 대해 다루어 보았습니다. 이제 여러분의 프로젝트는 '버그 제로(Bug-Zero)'를 향한 든든한 방어선을 구축했습니다.
테스트도 다 통과했고, 이제 이 완벽한 .jar 파일을 서버(AWS EC2)에 던져놓고 라이브(Live) 운영을 시작했습니다. 그런데 며칠 뒤 서버가 갑자기 느려지더니 메모리가 터질 것 같습니다! "대체 서버 안에서 무슨 일이 벌어지고 있는 거지? DB 통신은 잘 되나? 메모리는 얼마나 남았지?" 이런 궁금증을 해소하기 위해 서버 컴퓨터에 들어가서 top 명령어를 치는 원시적인 행위는 그만두어야 합니다. 이어지는 21단계 포스팅에서는 "서버의 심장 박동과 디스크 상태, 그리고 수많은 지표를 실시간 API로 뱉어낸다!" 스프링 부트 운영의 절대적 필수품, Spring Boot Actuator 모니터링과 헬스 체크(Health Check) 세팅법에 대해 아주 뼈 때리게 파헤쳐 보겠습니다!
'Framework > Spring Boot' 카테고리의 다른 글
| [Spring Boot] Spring Cloud Config: 50개의 서버 설정을 단 한 곳에서 통제하라 (0) | 2026.07.27 |
|---|---|
| [Spring Boot] Actuator 모니터링: 서버의 심장 박동을 실시간으로 감시하라 (0) | 2026.07.27 |
| [Spring Boot] 파일 업로드와 AWS S3 연동: 서버 디스크의 한계를 클라우드로 돌파하라 (0) | 2026.07.27 |
| [Spring Boot] 비동기(@Async)와 스케줄링(@Scheduled): 무거운 작업을 뒤로 빼라 (0) | 2026.07.27 |
| [Spring Boot] @Transactional의 함정: 롤백(Rollback)과 전파 속성의 모든 것 (0) | 2026.07.27 |