
이번에 캘린더 애플리케이션을 구현하면서 TDD(Test-Driven Development)를 처음 제대로 경험해봤다. 그동안 테스트는 '개발이 끝난 후에 하는 것'이라고 생각했는데, 이번 과제를 통해 테스트가 단순히 버그를 찾는 도구가 아니라 설계를 개선하는 방법론이라는 것을 깨달았다.
프로젝트 시작 전, 팀원들과 함께 어떤 테스트 전략을 사용할지 논의했다. 처음에는 각자 다른 접근법을 제안했다.
토론 과정에서 흥미로웠던 점은, 같은 애플리케이션을 보면서도 각자 다른 관점으로 접근했다는 것이다. 나는 처음에 '작은 규모 = 단위 테스트'라는 단순한 공식을 생각했는데, 다른 팀원들의 의견을 들으면서 애플리케이션의 특성을 더 깊이 생각하게 되었다.
결국 테스트 트로피 전략으로 합의했는데, 그 이유는:
이 합의 과정에서 배운 것은, 테스트 전략은 정답이 있는 게 아니라 프로젝트의 맥락에 따라 달라진다는 점이었다.
이론으로만 알고 있던 TDD 사이클을 실제로 경험해보니, 생각보다 어려웠다. 특히 '빨간불'을 보는 것에 익숙해지는 게 쉽지 않았다.
// 먼저 실패하는 테스트부터 작성
test('종료일을 지정하지 않았을 때 반복 시작일이 10월 30일 이후이면 시작 날짜에 대한 일정 하나만 생성', () => {
// Given: 10월 30일 이후 시작 날짜
// When: 반복 일정 생성
// Then: 하나의 일정만 생성되어야 함
});
처음에는 "이런 것도 테스트해야 하나?"라는 생각이 들었는데, 막상 구현하면서 이런 엣지 케이스들이 실제로 버그를 만들어낸다는 것을 깨달았다.
가장 놀라웠던 점은 테스트가 설계를 더 좋게 만든다는 것이었다. 테스트하기 어려운 코드는 보통 결합도가 높거나 책임이 명확하지 않은 코드였다. 테스트를 먼저 작성하니까 자연스럽게 다음과 같은 질문들을 하게 되었다:
백엔드 없이 테스트하는 방법이 처음에는 막막했다. MSW(Mock Service Worker)를 도입하면서 실제 API와 유사한 환경을 구축할 수 있었다.
// handlers.ts에서 반복 일정 API 모킹
export const handlers = [
http.post('/api/events/repeat', ({ request }) => {
// 실제 API 응답과 동일한 구조로 모킹
})
];
이 과정에서 모킹의 철학에 대해 많이 고민했다. 너무 현실과 다르면 테스트의 의미가 없고, 너무 복잡하면 유지보수가 어려워지니까.
React의 비동기 상태 변화를 테스트하는 것이 가장 어려웠다. waitFor, act 같은 유틸리티들을 사용하면서 비동기 테스트의 어려움을 체감했다.
test('반복 생성된 일정들과 이미 존재하는 일정이 겹칠 때 경고 표시', async () => {
// 비동기 상태 변화를 기다려야 하는 복잡함
await waitFor(() => {
expect(screen.getByText(/일정이 겹칩니다/)).toBeInTheDocument();
});
});
특히 타임아웃 이슈로 몇 번 고생했는데, 이는 근본적인 성능 문제일 수도 있고 내 컴퓨터 환경의 문제일 수도 있었다. 결국 임시방편으로 timeout 값을 늘렸지만, 실무에서는 이런 식으로 해결하면 안 되겠다는 생각이 들었다.
이번 과제에서 가장 아쉬웠던 점은 E2E 테스트를 경험하지 못한 것이다. 단위 테스트와 통합 테스트에만 집중하다 보니, 실제 사용자가 경험하는 전체 플로우를 검증하는 E2E 테스트를 놓쳤다.
특히 캘린더 애플리케이션 같은 경우, 사용자가 "일정 생성 → 캘린더에서 확인 → 수정 → 삭제"하는 전체 여정을 테스트하는 것이 정말 중요한데, 이 부분을 놓친 게 아쉽다.
심화과제 평가에서도 이 부분이 지적되었는데, 이론적으로만 알고 있던 E2E 테스트의 중요성을 실제로 느끼게 되었다. Cypress나 Playwright 같은 도구를 사용해서 실제 브라우저 환경에서의 테스트를 경험해보지 못한 것이 정말 아쉽다.
또한 UI 컴포넌트의 시각적 변화를 감지하는 시각적 회귀 테스트도 고려하지 못했다. 캘린더 UI는 시각적 요소가 중요한데, 코드 변경으로 인한 UI 깨짐을 자동으로 감지하는 테스트가 있었다면 더 완전한 테스트 전략이 되었을 텐데.
앞서 언급한 timeout 이슈도 근본적으로 해결하지 못한 아쉬움이 있다. 테스트가 느리면 개발자 경험이 나빠지고, 결국 TDD 사이클 자체가 지연되니까. 실무에서는 이런 성능 문제를 어떻게 해결하는지 더 알아보고 싶다.
코치님의 답변을 들으면서 현실을 알게 되었다. 대부분의 회사에서 완전한 TDD 도입은 쉽지 않다는 것, 특히 프론트엔드에서는 더욱 어렵다는 것을 알게 되었다.
하지만 그렇다고 해서 TDD를 배운 것이 의미 없다고 생각하지는 않는다. 설계를 개선하는 사고방식을 배웠고, 이는 TDD를 완전히 적용하지 않더라도 코드 품질을 높이는 데 도움이 될 것 같다.
실무에서 혼자 개발하는 경우가 많은 프론트엔드지만, 테스트 코드가 있으면 코드 리뷰나 인수인계 과정에서 의도를 파악하기 쉬워진다는 점을 깨달았다. 특히 복잡한 비즈니스 로직의 경우, 테스트 코드가 일종의 명세서 역할을 한다는 것을 체감했다.
첫 TDD 경험이었지만, 많은 것을 배우고 느낄 수 있었다. 특히 팀원들과 함께 테스트 전략을 논의하고 합의하는 과정에서, 혼자서는 생각하지 못했을 관점들을 배울 수 있어서 좋았다.
아직 E2E 테스트나 시각적 회귀 테스트 같은 부분들이 아쉽지만, 이것들은 앞으로의 학습 목표로 삼고 계속 공부해나가려고 한다.
무엇보다 "테스트는 개발을 더 즐겁게 만든다"는 것을 깨달았다. 안전하게 리팩토링할 수 있다는 자신감, 요구사항 변경에 대한 두려움 감소 등... 이런 것들이 개발자의 삶을 더 나아지게 만든다고 생각한다.
다음에는 E2E 테스트까지 포함한 완전한 테스트 전략을 구현해보고 싶다!
잘읽었습니다 테스트가 설계를 더 좋게 만든다는 말이 인상깊었어요