
React나 다른 프런트엔드 프레임워크 로 인터페이스를 개발하다 보면 , "내 컴퓨터에서는 잘 작동한다"는 말만 믿고 개발하는 것은 매우 위험하다는 것을 곧 깨닫게 될 것입니다. 컴포넌트의 작은 변경, 간단한 리팩토링, 또는 업데이트된 의존성만으로도 애플리케이션의 일부가 제대로 작동하지 않게 될 수 있는데, Jest를 사용한 탄탄한 단위 테스트 시스템이 없다면 이러한 문제를 알아차리지 못할 가능성이 큽니다.
최신 자바스크립트 생태계는 자동화된 테스트를 핵심 요소로 삼고 있습니다. Jest 와 같은 도구를 사용하면 테스트 전문가가 아니더라도 컴포넌트, 함수, 훅에 대한 테스트를 손쉽게 작성할 수 있습니다 . 핵심은 편안한 환경을 구축하고, 테스트 작성 방법을 확실히 이해하며, 테스트 결과와 코드 커버리지를 해석하여 테스트되지 않은 부분을 파악하는 것입니다.
프런트엔드 프로젝트에서 유닛 테스트를 하는 이유는 무엇일까요?
유닛 테스트는 코드의 특정 부분 (함수, 컴포넌트, 훅, 유틸리티 등)을 검증하는 간단한 테스트입니다. 특히 프런트엔드 개발에서 유용한데, 인터페이스가 자주 변경되고, 상태 로직, 사용자 이벤트, 비동기 호출 등이 많기 때문입니다. 안전장치가 없다면 모든 변경 사항이 위험 부담이 따릅니다.
단위 테스트의 가장 확실한 장점 중 하나는 오류를 조기에 발견할 수 있다는 것입니다 . 사용자가 이미 실제 운영 환경에 배포한 후에 버그를 발견하는 대신, 변경 사항을 저장하고 테스트 스위트를 실행하는 즉시 버그를 감지할 수 있으므로 버그의 발생 주기를 더 잘 이해할 수 있습니다 . 이는 팀의 시간, 비용, 그리고 많은 골칫거리를 줄여줍니다.
또 다른 중요한 장점은 테스트가 살아있는 문서 역할을 한다는 것입니다 . 컴포넌트나 함수에 대한 테스트가 어떻게 작성되었는지 살펴보면 해당 컴포넌트나 함수가 어떻게 사용되어야 하는지, 어떤 입력값을 받아야 하는지, 그리고 어떤 결과를 반환해야 하는지 명확하게 알 수 있습니다. 특히 대규모 프로젝트에서는 이러한 점이 신입 팀원들에게 매우 유용합니다.
JavaScript와 React 환경에서 테스트를 작성하는 것은 코드 모듈화를 더욱 효과적으로 만드는 데 도움이 됩니다 . 각 부분을 독립적으로 테스트하려면 해당 부분이 잘 격리되어 있어야 하고, 명확한 의존성과 정의된 책임이 있어야 합니다. 이는 중장기적으로 더욱 유지보수하기 쉬운 프런트엔드를 만드는 데 기여합니다.
Jest란 무엇이며, 프런트엔드 개발에서 왜 그렇게 많이 사용되는 걸까요?
Jest는 원래 Facebook에서 개발한 JavaScript 테스트 프레임워크로 , React와 함께 사용하기에 매우 적합하게 설계되었지만, 모든 클라이언트 측 또는 서버 측 JavaScript 또는 TypeScript 프로젝트에서 완벽하게 사용할 수 있습니다.
이 도구의 가장 큰 장점 중 하나는 "설정이 필요 없는" 철학 입니다 . 많은 프로젝트에서 단순히 설치하고 package.json 파일에 스크립트를 추가하는 것만으로 복잡한 설정 파일에 얽매이지 않고 테스트를 실행할 수 있습니다. 이러한 특징 덕분에 이미 다양한 도구가 사용되고 있는 프런트엔드 환경에서 특히 매력적입니다.
Jest는 프런트엔드 프로젝트에 필요한 주요 기능들을 기본적으로 통합하고 있습니다 . 빠른 테스트 실행, 변경 사항 감지 시 테스트를 다시 실행하는 watch 모드, 매우 편리한 비동기 코드 지원, 의존성을 시뮬레이션하는 mock 및 spie 기능, 그리고 추가적인 외부 도구 없이 코드 커버리지 보고서를 생성하는 기능 등이 포함됩니다.
React를 사용하는 프런트엔드 프로젝트에서 Jest는 거의 항상 React Testing Library 와 함께 사용됩니다 . 이 라이브러리를 사용하면 컴포넌트의 내부 구현에 지나치게 얽매이지 않고 동작 및 렌더링을 통해 쉽게 테스트할 수 있습니다. 이 두 가지 도구를 함께 사용하면 일반적인 프런트엔드 테스트 요구 사항을 거의 모두 충족할 수 있습니다.
프런트엔드 프로젝트에 Jest 및 React 테스트 라이브러리 설치하기
Jest를 시작하는 첫 번째 단계는 프로젝트에 개발 종속성으로 추가하는 것입니다. npm을 사용하는 경우 일반적인 명령어는 다음과 같습니다.
npm install --save-dev jest
Yarn을 사용하여 작업하는 것을 선호한다면 다음 명령으로 설치할 수 있습니다.
yarn add --dev jest
React 기반 프로젝트에서는 React Testing Library를 설치하는 것이 매우 일반적입니다. 이 라이브러리 는 여러 패키지로 구성되어 있는데, 컴포넌트 테스트를 위한 기본 패키지인 `@testing-library/react`, DOM에 대한 추가 매처를 위한 `@testing-library/jest-dom`, 그리고 복잡한 사용자 상호작용을 시뮬레이션하기 위한 `@testing-library/user-event` 등이 있습니다.
일반적인 React 설치 과정은 다음과 같습니다.
npm install –save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
이러한 종속성이 설정되면 Jest를 주요 테스트 실행 엔진으로 사용하여 구성 요소, 이벤트 및 사용자에게 표시되는 결과에 초점을 맞춘 단위 테스트를 작성할 수 있는 환경이 마련됩니다.
package.json의 기본 Jest 설정
Jest를 설치한 후에는 프로젝트에서 테스트를 실행하는 방법을 알려줘야 합니다 . 일반적으로는 package.json 파일에 스크립트를 추가하여 터미널에서 간단한 명령어로 실행할 수 있도록 합니다.
최소한의 구성 예시는 다음과 같습니다.
{ «스크립트»: { «테스트»: «농담» } }
이 스크립트를 사용하면 다음 명령만 실행하면 프로젝트의 모든 테스트를 실행할 수 있습니다.
npm 테스트
또는 실을 사용하는 경우 다음과 같이 하세요 :
실 테스트
Jest는 파일 이름을 기반으로 테스트 파일을 자동으로 감지합니다 . 기본적으로 폴더 트리에서 .test.js 또는 .spec.js 접미사가 붙은 파일을 찾으므로, 이러한 규칙을 따르는 한 경로를 수동으로 지정할 필요가 없습니다.
파일 규칙: .test.js 확장자 및 테스트 구조
Jest가 별도의 설정 없이 테스트를 인식하도록 하려면 파일 이름 에 `.test.js` 확장자(TypeScript를 사용하는 경우 `.test.ts`)를 사용하는 것이 좋습니다 . 예를 들어, `Button.jsx` 컴포넌트가 있다면 해당 컴포넌트의 테스트 파일은 `Button.test.js`와 같은 이름으로 저장하는 것이 일반적이며, 같은 디렉터리에 저장하거나 별도의 `tests` 폴더에 저장할 수 있습니다.
이 관례에는 두 가지 분명한 장점이 있습니다.
- 한편으로, Jest는 실행할 파일을 자동으로 찾아줍니다.
- 반면에 프로젝트에 새로 참여하는 사람은 누구나 어떤 파일에 실제 운영 코드가 포함되어 있고 어떤 파일에 테스트 코드가 포함되어 있는지 즉시 알 수 있습니다.
테스트는 `test` 함수 또는 그 별칭인 `it` 함수를 사용하여 정의합니다 . 첫 번째 인수는 테스트 대상에 대한 텍스트 설명이고, 두 번째 인수는 테스트 로직을 실행하는 함수입니다. 어설션은 `expect` 함수와 다양한 매처를 통해 이 함수 내에서 사용됩니다.
React 컴포넌트의 구조는 비슷하지만 , 순수 함수를 테스트하는 대신 React Testing Library를 사용하여 컴포넌트를 렌더링하고, 화면의 요소(텍스트, 역할, 레이블 등)를 검색하여 사용자 상호 작용을 시뮬레이션할 때 해당 요소가 예상대로 표시되거나 반응하는지 확인합니다.
Jest를 사용하여 첫 번째 단위 테스트를 작성하세요
이 모든 것을 쉽게 이해하기 위해 두 값을 더하는 간단한 함수를 생각해 보세요 . sum.js라는 파일에 `function sum(a, b) { return a + b; }`와 같이 함수를 정의하고 내보냅니다. 그런 다음 sum.test.js 파일에서 해당 함수를 가져와서 어떤 결과가 나와야 하는지 명확하게 설명하는 테스트 코드를 작성합니다.
테스트 본문은 함수 실행 및 결과 유효성 검사로 제한됩니다 . 예를 들어 sum(1, 2)를 호출하고 expect를 사용하여 값이 정확히 3이어야 함을 지정합니다. 만약 함수가 버그나 예기치 않은 변경으로 인해 더 이상 해당 결과를 반환하지 않는다면, Jest는 테스트를 실패로 표시합니다.
이러한 유형의 테스트는 아무리 간단해 보여도 유닛 테스트의 기본입니다 . 각 함수 또는 논리적 단위에는 다양한 시나리오에서 어떻게 동작해야 하는지를 설명하는 하나 이상의 테스트가 있으므로, 테스트 스위트를 실행하는 즉시 예상 동작과의 차이를 확인할 수 있습니다.
프런트엔드 컴포넌트에서도 접근 방식은 마찬가지로 간단합니다 . 컴포넌트를 렌더링하고, 표시되는 내용을 확인하고, 클릭이나 입력 필드 입력과 같은 이벤트를 시뮬레이션하고, 상태와 결과 DOM이 애플리케이션의 기능적 설계와 일치하는지 검증하면 됩니다.
프로젝트가 성장함에 따라, 예외적인 경우, 비정형적인 입력, 오류 및 덜 명확한 상황을 포괄하는 테스트를 추가하여 애플리케이션의 신뢰성을 강화하고 새로운 기능을 도입할 때 발생하는 회귀를 방지할 수 있습니다.
Jest 매처: 결과를 확인하는 다양한 방법
Jest에서 어설션의 핵심은 `expect` 함수 이며 , 이 함수는 다양한 매처와 연결되어 수신된 값에 대한 요구 사항을 지정합니다. 테스트 대상에 따라 적절한 매처를 선택해야 합니다. 다음은 가장 실용적인 매처들입니다.
- 장차 ~ 가 되는. 이 코드는 엄격한 동등성을 검사합니다. 자바스크립트에서 엄격한 동등성이란 동일한 값과 동일한 유형을 의미하며, 숫자, 문자열 또는 불리언과 같이 정확히 일치하는 값을 기대하는 경우에 매우 유용합니다. 예를 들어, 3을 기대한다면 "3"이 출력되어서는 안 됩니다.
- 같음객체나 배열을 다룰 때 유용합니다. 이 매처는 객체의 구조와 내용을 비교하여 내부 참조가 다르더라도 함수가 올바른 속성과 값을 가진 객체를 반환하는지 확인할 수 있도록 합니다.
- 아니. 어떤 상황에서든 특정 결과가 발생하지 않도록 해야 할 경우, 부정 연산자 `not`을 사용할 수 있습니다. 예를 들어, `expect(value).not.toBe(0)`은 해당 숫자가 0이 아니어야 함을 명확히 하고, `expect(array).not.toEqual([])`은 빈 배열이 예상되지 않음을 나타냅니다.
이러한 기본 사항 외에도 Jest는 함수가 오류를 발생시키는지, 배열에 특정 요소가 포함되어 있는지, 문자열이 정규 표현식과 일치하는지, 또는 React의 경우 jest-dom을 사용하여 DOM 요소가 보이는지, 비활성화 되었는지, 특정 텍스트를 가지고 있는지 등을 확인하는 등 다양한 매처를 제공합니다.
Jest에서의 비동기 테스트: 프로미스, async/await 및 콜백
최신 프런트엔드는 HTTP 요청, 타이머, 상태 업데이트를 유발하는 사용자 상호 작용 등과 같은 비동기 작업 으로 가득 차 있습니다 . 따라서 Jest는 비동기 테스트를 편리하게 처리할 수 있는 여러 가지 방법을 제공합니다.
비동기 로직을 테스트하는 가장 깔끔하고 일반적인 방법은 `async` 또는 `await`를 사용하는 것 입니다 . 테스트 함수를 비동기 함수로 선언하고, 테스트 대상 프로미스가 해결될 때까지 기다린 다음, 수신된 결과에 대해 `expect`를 사용하여 일반적인 검증을 수행합니다.
예를 들어, 프로미스를 반환하는 fetchData 함수를 만들고, fetchData를 호출하고 프로미스가 해결될 때까지 기다린 후 반환된 데이터가 특정 텍스트인지 특정 구조를 가진 객체인지 등 예상과 일치하는지 확인하는 비동기 테스트를 작성할 수 있습니다.
Jest는 async/await 없이도 프로미스를 직접 지원하며 , 테스트에서 프로미스 자체를 반환하여 프레임워크가 작업 완료 시점을 알 수 있도록 합니다. 또한, 이전 버전이나 매우 특정한 경우에는 `done` 매개변수를 가진 콜백을 사용하여 테스트 종료를 나타낼 수도 있습니다.
React Testing Library에서 비동기 테스트는 종종 `wait`와 `findBy` 또는 `waitFor`를 결합하여 사용합니다 . 이를 통해 요청이나 상태 변경 후 DOM이 업데이트될 때까지 기다린 후 관련 검증을 수행할 수 있습니다.
Jest에서 모킹하기: 모듈, 함수 및 종속성 시뮬레이션
단위 테스트의 기본 원칙은 테스트 대상 단위를 격리하는 것 입니다 . 즉, 함수나 구성 요소가 외부 서비스(API, 타사 라이브러리, 용량이 큰 모듈 등)에 의존하는 경우, 테스트 중에 실제로 실행하는 대신 해당 동작을 시뮬레이션해야 합니다.
Jest는 모의 객체를 통해 이러한 격리를 용이하게 합니다 . jest.fn을 사용하면 호출 횟수, 인수, 반환 값 등을 기록할 수 있는 모의 함수를 생성할 수 있습니다. 이는 서비스의 실제 코드를 수정하지 않고도 내부 상호 작용을 테스트하는 데 매우 유용합니다.
한 단계 더 나아가야 할 때는 jest.mock을 사용하여 전체 모듈을 대체할 수 있습니다 . 특정 파일을 가져올 때 Jest가 제어된 값을 반환하는 모의 구현을 사용하도록 지정하여, 예를 들어 테스트 스위트가 실행될 때마다 실제 HTTP 요청을 보내는 것을 방지할 수 있습니다.
React 컴포넌트에서 모의 객체는 종종 사용자 정의 훅, 데이터 서비스 또는 로컬 스토리지, 분석 등을 처리하는 모듈을 시뮬레이션하는 데 사용되어 테스트가 외부 종속성이 아닌 컴포넌트 자체의 동작에 집중되도록 합니다.
모킹을 올바르게 사용하면 테스트 실행 속도가 크게 향상되고 실제 서비스와 상호 작용해서는 재현하기 어려운 오류 시나리오, 이상한 서버 응답 또는 비정상적인 상태를 쉽게 재현할 수 있습니다.
블록을 사용하여 테스트를 구성하고 그룹화하는 방법을 설명합니다.
테스트 스위트가 커짐에 따라 여러 파일에 흩어져 있는 수백 개의 테스트 사이에서 길을 잃지 않으려면 최소한의 체계적인 관리가 필요합니다 . Jest는 관련 테스트를 그룹화하는 자연스러운 방법으로 블록 기능을 제공합니다.
`describe`를 사용하면 "산술 연산" 또는 "헤더 컴포넌트 동작"과 같이 동일한 컨텍스트 아래에 여러 테스트를 묶을 수 있습니다 . 블록 내의 각 테스트는 특정 사례를 설명하지만, 전체적으로는 해당 코드 부분에 대한 일관된 이야기처럼 읽힙니다.
이러한 구성 방식은 읽기와 디버깅 모두에 유용합니다 . 문제가 발생했을 때 전체 프로젝트를 살펴보지 않고도 테스트 스위트를 쉽게 찾고 시스템의 어느 부분에 영향을 미치는지 파악할 수 있습니다.
또한, Jest의 라이프사이클 훅(예: beforeEach 또는 afterEach)과 매우 잘 결합되어 동일한 블록 내에서 모든 테스트에 대한 데이터를 준비하거나 공유 상태를 정리할 수 있으므로 각 개별 테스트에서 초기화 로직을 중복하는 것을 방지할 수 있습니다.
복잡한 프런트엔드 프로젝트에서는 각 컴포넌트에 대한 설명 파일을 갖는 것이 일반적이며 , 필요한 경우 다양한 모드, 속성 또는 상호 작용 흐름에 대한 내부 설명으로 세분화할 수 있습니다. 이렇게 하면 테스트 파일이 해당 컴포넌트에 기대되는 모든 것을 명확하게 보여주는 지도가 됩니다.
워크플로우에서 npm 테스트 및 실행 규율을 활용하기
기본 설정이 완료되면 `npm test` 명령어가 매일 사용하는 필수 도구가 됩니다 . 변경 사항을 커밋하기 전에 이 명령어를 실행하는 것은 파일을 저장하거나 린터를 실행하는 것처럼 거의 자동으로 이루어질 것입니다.
많은 팀들이 테스트가 실패하면 커밋하지 않는다는 암묵적인 규칙을 따릅니다 . 이러한 관행은 메인 프로젝트 브랜치가 손상되는 것을 방지하고 저장소에 추가되는 모든 병합 또는 풀 리퀘스트에 대해 최소한의 품질을 보장합니다.
Jest는 개발에 매우 유용한 대화형 모드 도 제공합니다 . `npm test`를 감시 모드로 실행하면 프레임워크는 사용자가 수정하는 파일과 관련된 테스트만 다시 실행하므로 새로운 기능을 개발하거나 버그를 수정할 때 테스트 및 수정 주기를 크게 단축할 수 있습니다.
Jest를 GitHub Actions 와 같은 지속적 통합(CI) 시스템에 통합하면 전체 과정이 완료됩니다 . 누군가 원격 저장소에 코드를 푸시할 때마다 CI 서버는 `npm test`를 실행하고, 테스트 스위트가 실패하면 통합을 차단하여 오류가 공유 환경으로 유입되는 것을 방지합니다.
코드 커버리지: 실제로 테스트된 양을 측정하는 것
단순히 테스트 코드를 작성하는 것만으로는 충분하지 않습니다. 테스트가 코드를 어느 정도까지 커버하는지 파악하는 것도 중요합니다 . 이를 위해 Jest는 코드 커버리지 보고 기능을 제공하여 테스트 중에 실행된 코드 라인, 함수 및 분기를 보여줍니다.
이러한 보고서를 생성하는 것은 `test` 명령에 `--coverage` 플래그를 추가하는 것만큼 간단합니다 . 예를 들어, `package.json` 파일에서 `test` 스크립트를 "test": `jest --coverage`로 구성하거나, 자세한 보고서를 원할 때마다 `npm test --coverage`를 실행하면 됩니다.
결과에는 파일별 및 전체 코드 커버리지 비율이 포함됩니다 . 어떤 파일의 코드 커버리지가 높은지, 어떤 파일은 테스트 과정에서 거의 사용되지 않는지 확인할 수 있으므로, 추가적인 테스트 작성에 투자할 가치가 있는 부분을 판단하는 데 도움이 됩니다.
하지만 100% 코드 커버리지가 오류가 전혀 없다는 것을 보장하는 것은 아니라는 점을 기억해야 합니다 . 모든 코드 라인을 부담이 적은 테스트로 실행하는 것이 가능하기 때문에, 어설션의 품질은 커버리지 자체의 수치만큼이나, 어쩌면 그 이상으로 중요합니다.
코드 커버리지를 대략적인 지표로 활용하고, 코드 리뷰 및 상식을 함께 사용하면 테스트 노력과 프로젝트 안정성에 대한 실질적인 이점 사이의 균형을 유지하는 좋은 방법입니다.
이러한 모든 점을 고려할 때, 프런트엔드 프로젝트에서 Jest와 React Testing Library 같은 도구를 사용하는 것은 매우 논리적인 선택입니다 . 각 컴포넌트와 함수가 의도한 대로 작동하는지 확인할 수 있고, `npm test`로 쉽게 테스트를 실행할 수 있으며, `--coverage`로 코드 커버리지를 모니터링할 수 있고, 이미 잘 작동하는 부분을 망가뜨릴 염려 없이 제품을 발전시켜 나갈 수 있는 견고한 코드베이스를 유지할 수 있기 때문입니다.



