[7주차 회고] 테스트 코드

신희원·2025년 9월 17일
0
post-thumbnail

배운 내용 정리 📚

1. 테스트 환경 구성에 대한 고민과 이해

이번 과제를 진행하면서 "왜 이런 설정들이 필요한가?"라는 근본적인 질문부터 시작했다.

Mock에 대한 고민과 깨달음
처음에는 "왜 실제 함수를 쓰지 않고 가짜를 만드는거지?"라는 의문이 들었다. 하지만 실제로 사용해보니:

  • toastFn: 실제 토스트를 표시하면 테스트 환경에서 UI가 복잡해지고, 정작 확인하고 싶은 건 "호출되었는지"이지 "실제 토스트가 보이는지"가 아니라는걸 깨달았다
  • enqueueSnackbarFn: 함수 호출을 추적하고 기록하는 것만으로도 충분히 기능을 검증할 수 있다는 점
  • notistack mock: 실제 라이브러리 의존성 없이도 테스트가 가능하다는 것이 얼마나 큰 장점인지 체감했다

Provider 사용에 대한 의문과 해답
통합 테스트에서 ChakraProvider로 컴포넌트를 감싸는 부분을 보고 "이게 정말 의미가 있을까?"라고 의문을 가졌다. 하지만 실제로는:

  • ThemeProvider, SnackbarProvider, CssBaseline 등이 단순한 스타일링이 아니라 컴포넌트의 핵심 기능 작동에 필요하다는걸 알게 되었다
  • Provider 없이는 렌더링은 되지만 기능이 제대로 작동하지 않을 수 있다는 점이 충격적이었다
  • "겉보기에는 괜찮아 보이지만 실제로는 망가진 상태"를 방지하는 중요한 역할을 한다는걸 깨달았다

handlersUtils 활용에서 느낀 효율성
각 이벤트 생성/수정/삭제를 위한 mock API 설정을 보면서 "이미 짜여져 있는 함수를 가져다 쓰는거였지만 미디움에서 편리하게 사용했다"고 생각했다. 실제 API 호출은 너무 많은 리소스를 사용한다고 생각이 들었고, 가짜 서버와 데이터를 만들어 사용하는 것이 얼마나 효율적인지 몸소 체험할 수 있었다.

시간 고정에 대한 이해
vi.setSystemTime을 보고 "왜 이 시간을 설정해주는 걸까?"라는 궁금증에서 시작했다. 고정된 시간 설정이 날짜/시간 의존 로직의 예측 가능성과 재현성을 보장한다는걸 이해하고 나니, 테스트의 안정성이 얼마나 중요한지 깨달았다.

2. 비동기 테스트에서 겪은 시행착오와 이해

actwaitFor를 처음 접했을 때 "언제 뭘 써야 하는거지?"라는 혼란이 컸다.

초기 고민들

  • "act와 waitFor이 둘 다 비동기 처리를 하는 것 같은데 차이점이 뭐지?"
  • "언제는 act를 쓰고 언제는 waitFor을 써야 하는거야?"
  • "타이머 관련 테스트에서 실제로 시간이 흘러야 하나, 아니면 즉시 처리할 수 있나?"

실제 경험을 통한 깨달음

  • act는 동기/비동기 모두 사용 가능하며, React의 상태 업데이트를 즉시 처리한다는걸 체험했다
  • waitFor은 항상 비동기이며, 실제 시간이 흘러야 하는 상황에서 사용한다는걸 이해했다
// 동기 act - "이거 정말 즉시 처리되나?" 의심하며 사용했는데 실제로 즉시 처리됨!
act(() => {
   vi.advanceTimersByTime(1000);
});

// waitFor - "정말 2초를 기다려야 하나?" 걱정했는데 필요한 경우였음
await waitFor(() => {
    expect(screen.getByText('1초 지남')).toBeInTheDocument();
}, { timeout: 2000 });

3. 데이터 로딩 방식에 대한 고민과 선택

두 가지 데이터 로딩 방식을 보고 "어떤게 더 좋은 방법일까?"라는 고민이 들었다.

1번 방법에 대한 의문

await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
});

"이게 왜 작동하는거지? setTimeout을 0으로 설정하는게 무슨 의미가 있을까?"라는 의문에서 시작했다.

2번 방법의 명확성

await act(async () => {
    await result.current.fetchEvents();
});

"이게 더 직관적이고 명확한 것 같은데, 2번 방법을 택한게 맞을까?"라고 고민했다.

코치님 피드백을 통한 이해

  • 1번은 "다음 이벤트 루프로 넘어가며 pending Promise들을 기다리는" 해킹 기법이라는걸 알게 되었다
  • 2번이 의도가 더 명확하고 일반적으로 선호된다는 확신을 얻을 수 있었다
  • "뭔가 비동기적으로 실행될 것 같은데 정확히 언제 실행될지 모를 때" 1번을 사용한다는 기준을 이해했다

깨달은 점

1. AI에 대한 의존도 조절의 중요성

몇 시간 동안 MUI Icons 모킹 문제로 삽질했던 경험이 가장 큰 깨달음을 줬다. AI에게 계속 에러를 고쳐달라고 했지만, 답이 수렴되지 않고 오히려 더 복잡해지기만 했다.

결국 직접 구글링해서 찾은 해결책은 단순했다:

// 기존
import { LocationCity } from "@mui/icons-material";
// 수정
import LocationCity from "@mui/icons-material/LocationCity";

코치님의 팁처럼, AI를 사용했을 때 답이 수렴되지 않는다면 AI가 방향성을 제대로 짚지 못한 징조로 받아들이고, 직접 문제 해결에 나서야 한다는 것을 체감했다.

2. 의미있는 테스트의 기준

코치님의 피드백을 통해 테스트 코드 작성의 기준을 명확히 할 수 있었다:

  1. 기획 관점에서의 요구사항: 기획서에 해당하는 요구사항이 있다면 뻔해 보여도 테스트해야 함
  2. 실제 발생 가능성: 사용자로 인해 실제로 발생할 수 있는 상황인가?
  3. 사용자 영향도: 이 테스트가 실패하면 사용자에게 문제가 되는가?

경계값 테스트의 경우, 달력 Form을 사용한다면 큰 문제가 없을 수 있지만, 코드로 입력하는 엑셀 같은 환경이라면 반드시 검증해야 한다는 관점이 인상적이었다.

3. 통합 테스트의 재미

처음엔 막막했던 통합 테스트가 오히려 단위 테스트보다 재미있었다. 상태를 기반으로 기획서와 코딩을 맞춰가는 과정에서 전체적인 플로우를 이해할 수 있었고, 실제 사용자 관점에서 기능을 검증하는 느낌이 들었다.

4. Git 명령어의 위험성

git reset --hard HEAD~1 명령어로 커밋하지 않은 파일들이 모두 날아간 경험... 정말 뼈아픈 교훈이었다. 스테이징에 올려두지 않은 것이 화근이었지만, 덕분에 git 명령어의 위험성과 중요성을 체감할 수 있었다.

아쉬운 점과 다음 목표

아쉬운 점

  1. Hard 레벨 도전 실패: Git 실수로 인해 Hard 레벨을 완주하지 못한 것이 아쉽다
  2. AI 의존도: 초기에 AI에만 의존하려 했던 점, 직접 문제 해결을 시도하지 않았던 점
  3. 기본기 부족: MUI Import 방식 같은 기본적인 부분에서 막혔던 점

다음 목표

  1. 문제 해결 프로세스 개선: AI 사용 시 답이 수렴되지 않으면 즉시 직접 조사로 전환
  2. Git 사용 숙련도 향상: 위험한 명령어 사용 전 충분한 확인, 정기적인 커밋 습관화
  3. 테스트 설계 능력 향상: 요구사항 기반의 의미있는 테스트 케이스 설계 능력 기르기
  4. Hard 레벨 재도전: 다음 기회에는 반드시 Hard 레벨까지 완주하기

마무리

이번 과제는 단순히 테스트 코드 작성법을 배우는 것을 넘어서, 문제 해결 접근법, 도구 사용의 균형, 그리고 실패로부터 배우는 것의 중요성을 깨닫게 해준 의미있는 경험이었다.

"내가 웃는게 웃는게 아니야.. ㅠ"라고 해주신 코치님의 따뜻한 피드백처럼, 실패와 삽질의 경험도 결국 성장의 밑거름이 된다는 것을 느꼈다. 테스트 코드에 대한 심리적 장벽은 확실히 허물어졌고, 다음 과제에서는 더 체계적이고 효율적인 접근을 해보고 싶다.

profile
프론트엔드 공부하는 개발자입니다.

4개의 댓글

comment-user-thumbnail
2025년 9월 17일

화이팅입니다!

1개의 답글
comment-user-thumbnail
2025년 9월 17일

홧팅

1개의 답글