워밍업 클럽 4기 BE - 2주차 발자국
2주차 미션 회고
배운 내용을 토대로 실제 코드에 적용하면서 정말 많은 것을 배우고 느꼈다. 리팩토링에는 정답이 없지만 오답은 있을 수 있다는 것을 깨달았고, 앞으로 개발할 때 클린코드의 진정한 의미에 대해 생각하면서 코드를 작성해야겠다.
2주차 강의 회고
클린코드 강의를 마무리하고 본격적인 테스트 코드 강의에 들어가기 전 워밍업 느낌의 1~5장 챕터라고 생각됐다.
테스트의 개념부터 기본적인 테스트 코드 작성법까지 어찌보면 가장 중요한 챕터일지도 모른다는 생각이 들었다.
최대한 꼼꼼히 공부하면서 첫 시작부터 기반을 다진다는 마음가짐으로 강의를 들었다.
2주차 학습 내용 요약
테스트가 필요한 이유
테스트 코드를 작성하지 않는다면,
변화가 생기는 매순간마다 발생할 수 있는 모든 케이스를 고려해야 한다.
변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.
빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.
잘못된 테스트 코드를 작성한다면,
프로덕션 코드의 안정성을 제공하기 힘들어진다.
테스트 코드 자체가 유지보수하기 어려운 짐이 된다.
잘못된 검증이 이루어질 가능성이 생긴다.
올바른 테스트 코드를 작성한다면,
자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있다.
소프트웨어의 빠른 변화를 지원한다.
팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.
가까이 보면 느리지만 멀리 보면 가장 빠르다.
소프트웨어 테스트 종류
✅기능 테스트
단위 테스트: 하나의 함수나 메서드 단위 동작 검증 (로직 중심)
통합 테스트: 여러 모듈이 함께 동작하는지 확인 (인터페이스, 데이터 흐름 검증)
시스템 테스트: 전체 시스템이 요구사항대로 동작하는지 확인
E2E 테스트: 사용자 흐름을 처음부터 끝까지 시뮬레이션하여 기능 동작 여부 확인
회귀 테스트: 기존 기능이 변경 없이 잘 동작하는지 반복 확인
유효성 검사 테스트: 입력값 유효성, 필수 값 누락 등 사용자의 잘못된 입력 처리 검증
조건부 분기 테스트: if/else, switch 등 분기문이 정상적으로 작동하는지 확인
✅비기능 테스트
동시성 테스트: 여러 스레드/사용자가 동시에 접근할 때의 일관성, 충돌 여부 확인
성능 테스트: 시스템이 응답 속도나 처리량 요구사항을 충족하는지 확인
부하 테스트: 많은 사용자나 트래픽이 몰릴 때 성능 저하 없이 동작하는지 확인
스트레스 테스트: 시스템 한계를 넘는 부하에서도 장애 없이 복구 가능한지 확인
단위 테스트 시 검증해야 할 주요 케이스
정상 케이스
기대한 입력값이 주어졌을 때 올바른 결과가 나오는지 확인
ex)
input = 5
→output = 25
경계 값 테스트
허용되는 최소/최대값 근처의 입력을 검증
ex)
min = 1
,max = 100
→0
,1
,100
,101
예외/에러 케이스
잘못된 입력이 들어왔을 때 예외가 잘 처리되는지 확인
ex)
input = null
→IllegalArgumentException
발생
빈 값/Null 처리
null
, 빈 리스트, 빈 문자열 등 비정상적이지만 흔한 값 검증ex)
""
,null
,[]
입력 처리 확인
중복/경합 조건
동일 입력을 여러 번 넣었을 때 문제가 없는지 확인
ex) 중복 ID 처리, 중복 요청 무시 등
상태 기반 테스트
특정 상태 변경 전/후 동작이 예상대로 수행되는지 검증
ex) 로그인 전/후 메뉴 접근 가능 여부 등
시간 관련 테스트
시간에 따라 달라지는 로직의 동작 확인
ex) 쿠폰 유효 기간, 예약 처리 등
성능 경계 테스트
데이터량이 많을 때 정상 처리 여부
ex) 리스트 10,000건 처리 가능 여부 등
테스트하기 어려운 영역
✅관측할 때마다 다른 값에 의존하는 코드 (현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등)
public class Order {
public String generateOrderId() {
return "ORD-" + UUID.randomUUID(); // 매번 다른 값
}
public boolean isTodayOrder(LocalDate orderDate) {
return orderDate.equals(LocalDate.now()); // 현재 날짜 의존
}
}
✅외부 세계에 영향을 주는 코드 (표준 출력, 메시지 발송, 데이터베이스 기록 등)
public class Notifier {
public void sendNotification(String message) {
System.out.println("Sending message: " + message); // 표준 출력
}
}
public class UserRepository {
public void save(User user) {
// 실제 DB에 저장하는 코드 (예: JDBC, JPA)
entityManager.persist(user); // 외부 세계에 영속성 부여
}
}
TDD (Test Driven Development)
테스트를 먼저 작성하고, 그 테스트를 통과하는 최소한의 코드를 구현한 후, 리팩토링하는 개발 방식이다.
// Step 1 (Red): 실패하는 테스트 작성
@Test
void add_shouldReturnSum() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3)); // 아직 add 메서드 없음 → 실패
}
// Step 2 (Green): 테스트 통과하는 최소한의 코드 작성
public int add(int a, int b) {
return a + b;
}
// Step 3 (Refactor): 코드 리팩토링 (필요시 구조 개선 등)
🔴Red: 실패하는 테스트 작성 (아직 기능이 없으므로 실패)
✅Green: 테스트를 통과할 최소한의 코드 작성
🧹Refactor: 중복 제거 및 코드 정리 (기능은 그대로)
BDD (Behavior Driven Development)
사용자 관점의 시나리오(행동)를 중심으로 기능을 정의하고 테스트하는 접근 방식이다.
Feature: 로그인 기능
Scenario: 올바른 아이디와 비밀번호 입력 시 로그인 성공
**Given** 사용자가 로그인 페이지에 접속했을 때
**When** 아이디와 비밀번호를 올바르게 입력하면
**Then** 로그인에 성공하고 홈 화면으로 이동한다
Given: 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)
When: 시나리오 행동 진행
Then: 시나리오에 대한 결과 명시, 검증
테스트 제대로 설명하기
- 음료 1개 추가 **테스트** (bad) → ~테스트 문장 지양
- 음료를 1개 **추가할 수 있다.** (not bad) → 명사의 나열보단 문장으로
- 음료를 1개 **추가하면 주문 목록에 담긴다.** (good) → 테스트 행위에 대한 결과까지
- 특정 시간 이전에 주문을 생성하면 **실패한다.** (bad) -> 테스트 현상 중점 기술
- **영업 시간** 이전에는 주문을 생성할 수 없다. (good) -> 도메인 용어 사용
~테스트 문장 지양하기
명사의 나열보단 문장으로 기술하기
테스트 행위에 대한 결과까지 기술하기
도메인 용어를 사용하여 한층 추상화된 내용 담기
테스트의 현상을 중점으로 기술하지 말기
댓글을 작성해보세요.