[8팀 박창준] Chapter 3-1. 프론트엔드 테스트 코드 🧦 #44
Open
ckdwns9121 wants to merge 43 commits intohanghae-plus:hardfrom
Open
[8팀 박창준] Chapter 3-1. 프론트엔드 테스트 코드 🧦 #44ckdwns9121 wants to merge 43 commits intohanghae-plus:hardfrom
ckdwns9121 wants to merge 43 commits intohanghae-plus:hardfrom
Conversation
…rce @typescript-eslint/no-unused-vars
…t for improved UI structure
…verlapDialog mock implementation
…ts to ensure stability with various props
… in scheduling scenarios
|
이번주도 고생하셨습니다~ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HARD
7주차 과제 체크포인트
기본과제
질문
기존 핸들러는 정적 JSON만 돌려줘서, 생성/수정 후에
fetch를 다시 해도 최신 상태가 안 나왔고, 전역 상태를 공유해서 병렬 실행 시 데이터가 섞일 여지가 있었습니다.그래서 클로저 기반
createMockHandlers팩토리를 넣었습니다. 테스트마다createMockHandlers(seed)()를 호출해 독립적인mockEvents를 갖도록 했고, 그 결과POST/PUT/DELETE가 실제mockEvents를 업데이트하고GET은 항상 그 시점의 최신 상태를 반환하며이 패턴 덕분에 테스트 A/B 각각 완전히 분리된 인스턴스를 쉽게 만들 수 있었습니다.
아래는 최종 코드입니다.
현재
createMockHandlers함수는 단순히 핸들러 배열을 바로 반환하지 않고 대신 “핸들러 팩토리”를 반환하도록 설계되어 있습니다.그래서 처음 한 번 호출할 때는, 전달받은 초기 상태(
seed이벤트 배열)를 클로저에 담아두는 공장 같은 함수를 만들어내고, 두 번째 호출할 때 그 공장이 실제로 동작해서 핸들러 배열을 반환하게 만들었습니다.이렇게 두 단계로 나눈 이유는 테스트 환경에서 여러 개의 독립적인 인스턴스를 쉽게 만들어내기 위해서인데요. 예를 들어 테스트 A와 테스트 B가 동시에 실행될 때, 각각
createMockHandlers([])()를 호출하면 서로 다른 메모리 공간을 가진 핸들러 인스턴스를 생성하게 되고, 덕분에 데이터 오염 없이 병렬로 안전하게 동작할 수 있게 되었습니다.또한 여기서
structuredClone()를 사용했는데, 깊은 복사로 초기 데이터를 복제해 두 테스트가 같은 참조를 공유하지 않게 좀더 안전하게 막기 위해서 사용했습니다. 얕은 복사였다면 객체 참조가 이어져서 한쪽 변경이 다른 쪽에 새어 나올 수 있을거라 생각했어요. (하지만 지금 생각해보니 이미 클로저 환경이라 크게 상관없을 것 같습니다.)그리고 setupTest.ts 파일에서 테스트가 끝나면 이전 테스트의 핸들러를 모두 제거하여 독립성을 확보했습니다.
심화 과제
과제 셀프회고
기술적 성장
원래 저는 테스트코드를 작성해본 경험이 거의 없었습니다. 백엔드에서 로직 검증할 때. 몇 번 짰었는데 프론트에서 테스트코드는 처음이였어요. 단순히 테스트가 필요하다는것 만 알고있었지 실제로 어떻게 작성하고 어떤 원리로 동작하는지 알지 못했습니다.
이번 과제를 통해서 테스트코드를 작성하는 기본 흐름을 배우면서 이게 실제로 필요한 테스트일까? 혹은 어떤 테스트가 꼭 필요할까? 라는 고민을 자연스럽게 해볼 수있어서 좋았습니다.
그리고
act,waitFor같은 도구가 비동기 로직과 UI 업데이트를 제어하는 역할을 한다는 것을 알게 되었고, 이를 활용해 실제 시나리오 기반의 테스트를 작성해보니 너무 재미있었습니다. 하지만 이부분은 여전히 헷갈리네요. act는 React의 상태관리를 래핑해주는 함수로만 알고있고 waitFor은 비동기나 useEffect처리처럼 바로 나타나지 않는 결과를 처리할때 쓰는거로 공부했는데 어떨때 적절하게 쓸지 아직 감은 못잡았습니다.또한 테스트 환경에서는 시간에 대한 모킹이 되게 중요하다고 느꼈는데
vi.useFakeTimers,vi.setSystemTime을 사용해 시간을 고정하거나 조작하면서 테스트 환경에서도 실제 시간에 의존하지 않고 안정적으로 검증하는 방법을 배웠습니다.그리고 MSW를 사용해 서버 없이도 네트워크 요청을 흉내 내고 독립적인 테스트 환경을 구성하는 방법을 배웠습니다. 특히 전역 상태를 그대로 두면 병렬 실행 시 데이터 오염 문제가 생기므로 클로저 기반 상태 관리와 테스트 격리의 중요성도 체감했습니다.
무엇보다 단순히 테스트를 많이 작성하는 것이 아니라 어떤 것이 의미가 있고 어떤것이 불필요할지 구분하는 감각을 조금이나마 얻을 수 있게된것 같습니다.
코드 품질
학습 효과 분석
1. 불필요한 테스트를 과감히 제거하기
처음에는 알림시간이 0인건 즉시 알림이 발생하는줄 알고 “알림 시간이 0이면 즉시 알림이 발생한다.”와 같은 테스트를 작성했습니다. 하지만 해당 테스트는 실패했고 getUpcomingEvents를 확인해보니 알림시간이 0인 부분에 대해서는 따로 예외를 던지거나 처리하고 있는 로직이 없었습니다.
그래서 아래와 같은 테스트를 추가했더니 통과가 되었습니다.
하지만 곰곰히 생각해보니 이 테스트는 오히려 잘못된 동작을 올바른 결과로 착각하게 만들 위험이 있다고 생각했습니다.
이 경험을 통해 모든 케이스를 억지로 테스트하는 것보다, 의미 없는 테스트는 과감히 제거하는 것이 낫다는 점을 배웠습니다.
다음으로는 알림 메시지 생성 로직을 검증하기 위해 여러 케이스를 추가했습니다.
2. 다양한 케이스 검증
여기서는 단순히 기능을 확인하는 것을 넘어서, 실제 사용자가 마주할 수 있는 다양한 상황을 검증해보았습니다.
&, 괄호 등)이런 테스트를 추가하면서, “테스트는 코드만 보는 게 아니라, 실제 사용자 경험을 뒷받침해야 한다”는 점을 다시 느꼈습니다.
3. 여러 이벤트 동시 처리
실제 서비스에서는 여러 이벤트의 알림이 동시에 도래하는 경우도 있습니다. 이를 검증하기 위해 다음과 같은 테스트를 작성했습니다.
이 테스트는 동시성 상황에서도 함수가 안정적으로 동작하는지 확인해주는 장치라고 생각했습니다. 실제로 알림 시스템을 운영한다고 생각하면, 꼭 필요한 검증이라는 생각이 들었어요
4. 경계값 테스트: 월을 넘어가는 주간 뷰
마지막으로 경계 조건을 검증하는 테스트도 추가했습니다.
달력 기반 UI에서 가장 자주 문제가 되는 부분은 월 말 ~ 월 초에 걸쳐 있는 부분입니다. 이 테스트를 통해 사용자가 실제로 볼 때 문제가 없도록 보장할 수 있었고, 이 부분만 테스트 해보면 적은 수의 테스트로 다른 케이스들의 안정성을 확보할 수 있었습니다.
이번 학습을 통해 크게 네 가지를 배웠습니다.
과제 피드백
추가적으로 커버리지를 측정해봤습니다.

전체 커버리지 요약
테스트 실행 결과
100% 커버리지 달성 파일
fetchHolidays.tsdateUtils.tseventUtils.tseventOverlap.tsnotificationUtils.tstimeValidation.ts그중에서도 utils 함수들은 다양한 케이스를 꼼꼼하게 테스트하기 위해 극단값·에러 케이스까지 고려해서 테스트를 짰습니다. 단순히 숫자 달성에 그치지 않고, 실제로 오류가 발생하기 쉬운 부분들을 세밀하게 커버했다는 점에서 의미가 있다고 생각합니다.
특히 dateUtils는 윤년/월 경계/시작일·종료일 같은 예외 케이스까지 테스트했고, eventOverlap은 겹치는 구간·겹치지 않는 구간·완전히 포함되는 경우 등 분기별로 테스트했습니다. 이런 과정을 통해 단순히 커버리지를 높인 게 아니라 코드 로직의 안정성에 대한 신뢰도 확보할 수 있었습니다.
다만 제가 심화과제때 작성한 컴포넌트에 대한 테스트는 커버리지가 낮았는데 컴포넌트에 대한 테스트들을 작성해본 경험이 많이 없어서 어떻게 하면 의미있는 테스트를 작성할지 좀더 고민을 해서 채워나가야할 것 같습니다
리뷰 받고 싶은 내용
Q1. 통합 테스트의 DOM 탐색 관련
MUI 같은 외부 UI 라이브러리를 쓰다 보면 포털, 비동기 렌더링 같은 특성 때문에 예상치 못한 DOM 구조가 생겨서 테스트 작성이 복잡해지는 경우가 있습니다. 특히 UI 라이브러리를 MUI에서 Antd나 shadcn 같은 다른 걸로 바꾸면 테스트 코드도 크게 수정해야 할 것 같은데요. 실제 실무에서는 이런 DOM 의존적인 테스트에 시간을 많이 쓰는 편인가요? 아니면 테스트 전략을 단순화하거나, UI 라이브러리에 덜 의존하는 방식으로 접근하는 경우가 더 일반적인가요?
Q2. TanStack Query 테스트 질문
현업에서 TanStack Query를 많이 사용하는데, 이런 데이터 페칭 라이브러리를 사용하는 컴포넌트를 테스트할 때 테스트가 복잡하거나 관리하기 번거로운 편인지 궁금합니다.
특히, 캐싱, 비동기 상태, 쿼리 무효화 같은 기능 때문에 테스트 작성이 어려울 것 같은데 이런부분은 어떻게 학습하고 대응하는지 궁금합니다..
Q3. CI에서 간헐적으로 실패하는 테스트가 있습니다.
1주차에서도 그랬고 이번 과제에서도 그랬는데 로컬에서 돌리면 잘 통과하는 테스트들이 CI환경에서는 실패하는 경우가 종종 있더라구요. 이걸 플래키 테스트라고 부르던데 현업에서 이를 잘못된 테스트로 보고 반드시 리팩터링하는지, 아니면 리트라이 등 다른 방식으로 대응하는지 궁금합니다.