지식 정리 📝

Autowini WEB 통합 프로젝트 Vitest 테스트 시스템 도입

엄성준 2025. 10. 17. 15:11
728x90

web-www-front는 오토위니(AutoWini)에서 기존 JSP 기반의 레거시 페이지를 React로 분리 및 개선하기 위해 만들어진 통합 프로젝트입니다.

 

현재까지 분리된 주요 페이지는 다음과 같습니다.

  • 필터 검색 페이지
  • 통합 검색 페이지
  • 찜 목록 페이지 (마이위니)
  • 운송 정보 제공 페이지
  • 파츠위니 통합 검색 페이지
  • 파츠위니 필터 검색 페이지

이 프로젝트를 진행하면서 “테스트 코드를 도입할 필요가 있을까?”라는 질문을 스스로에게 던지고 있었습니다.


🤔  테스트 코드, 정말 필요한 걸까?

최근 여러 기업들의 채용 공고를 보면 "테스트 코드 작성 경험"이나 "TDD 경험자 우대"라는 문구를 자주 볼 수 있습니다.
저 역시 Toss의 es-toolkit과 NaverPay의 hidash 라이브러리에 테스트 코드 및 벤치마크 코드 기여를 한 경험이 있지만, 정작 사내 프로젝트에는 테스트를 도입하지 않고 있었습니다.

 

이유는 "기능 개발하고 유지보수할 시간도 부족한데, 테스트 코드까지 작성하라고?", "테스트 안 짜도 완벽하게 개발하면 되는 거 아닌가?"라는 생각을 갖고 있었습니다.

 

하지만 최근에 파츠위니 필터 검색 페이지를 마이그레이션 후 QA 과정에서 시간이 남았던 김에, 테스트 코드를 실제 프로젝트에 도입해 보기로 했습니다.


그리고 그 과정에서 몇 가지 고민이 있었습니다.


💡 1.  어떤 테스트 프레임워크를 사용할까?

가장 먼저 고민한 건 테스트 도구 선택이었습니다. JestVitest 사이에서 고민했지만, 이전 오픈소스 기여 경험 덕분에 더 익숙하고, 무엇보다 Vite 환경과의 호환성이 뛰어난 Vitest를 선택했습니다.

 

 

📁 2.  테스트 파일의 위치 구조는 어떻게 할까?

테스트 파일을 각 파일 옆에 둘 것인지, 아니면 별도의 tests 폴더에서 일괄 관리할 것인지 고민했습니다.

 

현재 프로젝트 구조는 다음과 같습니다.

src/
 ┣ domains/
 ┃ ┣ PartFilterSearch/
 ┃ ┃ ┣ api/
 ┃ ┃ ┣ components/
 ┃ ┃ ┣ utils/
 

이런 구조에서는 각 도메인 하위 폴더에 __tests__ 디렉토리를 만들어 관리하는 것이 효율적이라고 판단했습니다.

 

이 방식의 장점은 다음과 같습니다.

  • 관련 코드와 테스트가 함께 존재하여 유지보수 시 가독성 향상
  • import 경로가 짧고 구조가 단순
  • 특정 도메인을 삭제할 때 테스트도 함께 정리 가능

📂 즉, 프로젝트 구조의 장점을 그대로 이어가는 테스트 구조”를 유지하고자 했습니다.

 

 

🧪 3.  모든 예외 케이스까지 테스트해야 할까?

테스트를 작성할 때 가장 많이 고민한 부분은 "어디까지 테스트해야 할까?"였습니다.

 

예를 들어 아래와 같은 전역 유틸 함수가 있습니다.

/**
 * @description 숫자형 가격 데이터를 천 단위로 콤마(,)를 포함한 문자열로 반환
 * @param price 숫자형 가격 데이터
 * @returns {string} 천 단위로 콤마(,)가 포함된 가격 문자열
 */
export const formatPrice = (price: number | null): string => {
  if (price === null) return '*,***';
  return price.toLocaleString();
};

 

이 함수는 API에서 받아온 price 값을 포맷팅 하는 역할을 합니다. 그런데 Infinity, -Infinity, NaN 같은 값은 API에서 들어올 가능성이 없기 때문에, 테스트 케이스에 포함시키지 않았습니다. 터무니없는 값까지 검증하는 것은 테스트 효율을 떨어뜨릴 수 있고, 오히려 불필요한 테스트는 가시적으로 좋지 않다고 생각했습니다.


⚙️ 실제 작성한 테스트 코드 예시

아래는 기존 전역 유틸 함수에 대해 작성한 테스트 코드 일부입니다.

import { describe, expect, test } from 'vitest';

import { formatPrice, formatUpperToFirstLetter } from '../formatUtils';

describe('formatPrice', () => {
  test('숫자를 천 단위로 콤마(,)를 포함한 문자열로 반환한다', () => {
    expect(formatPrice(0)).toBe('0');
    expect(formatPrice(1234)).toBe('1,234');
    expect(formatPrice(1234567)).toBe('1,234,567');
  });
  test('값이 null일 경우 *,*** 문자열을 반환한다', () => {
    expect(formatPrice(null)).toBe('*,***');
  });

 

짧고 단순하지만, 테스트 코드를 작성하면서 몇 가지 흥미로운 점을 발견했습니다.

 

예를 들어, 기존에는 !price 조건을 사용해 '*,***'를 반환하고 있었는데, 이 경우 price가 0일 때도 잘못된 결과를 반환하는 예외 상황이 있었습니다.

 

테스트를 작성하는 과정에서 이런 로직상의 허점을 발견할 수 있었고, 테스트 코드가 단순히 검증 도구가 아니라, 코드 품질을 개선하는 도구라는 걸 실감했습니다. 또한 이 과정을 통해 함수의 JsDoc 주석 추가, 명확한 반환 타입 지정, 조건문 보완 등의 부수적인 개선도 함께 이루어졌습니다.

 

실제 작성한 테스트 코드 이미지


🔍  마무리하며

이번 경험을 통해 느낀 점은, 테스트 코드는 “시간 낭비”가 아니라 개발 효율을 높여주는 든든한 안전망이라는 것입니다.

 

물론 모든 함수나 컴포넌트에 테스트를 작성할 필요는 없다고 생각합니다. 하지만 리팩토링 가능성이 높거나 로직이 복잡한 부분에는 테스트를 작성해야 한다는 생각이 생겼습니다.

 

또한 테스트 코드를 효율적으로 작성하려면 자연스럽게 단일 책임 원칙(SRP)을 지키게 되고, 이는 곧 테스트하기 쉽고 유지보수하기 좋은 코드로 이어진다고 생각합니다.

 

앞으로도 테스트 케이스를 작성하며, 운영 환경에서 발생할 수 있는 예외 상황에 미리 대비할 수 있는 코드를 작성하기 위해 노력할 계획입니다.