먼저 로컬에 pnpm
패키지 매니저가 설치되지 않았을 경우, npm 을 통해 설치해준다.
npm install -g pnpm
아래의 명령어를 실행하여 vite 를 통해 typescript + react
프로젝트를 생성한다.
pnpm create vite <프로젝트명> -- --template react-ts
생성 단계에서 위와 같은 선택창이 나올 경우, Typescript + SWC
를 추천한다.
Typescript + SWC 를 추천하는 이유? SWC란?
참조 자료: https://shape-coding.tistory.com/entry/SWC-SWC%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80
정상적으로 생성되었다면 위와 같은 문구가 출력될 것이다.
그러면, pnpm dev
명령어를 실행해보자.
위와 같이 정상적으로 실행되는 것을 확인할 수 있다.
{
"compilerOptions": {
"target": "ES2020", // ECMAScript 타겟 버전을 최신으로 설정
"lib": ["ES2020", "DOM", "DOM.Iterable"], // DOM과 ES 표준 라이브러리를 포함
"useDefineForClassFields": true, // 클래스 필드의 동작을 최신 사양에 맞게 설정
"module": "ESNext", // 최신 ESMAScript 모듈 시스템 사용
"skipLibCheck": true, // 라이브러리의 타입 검사를 생략하여 빌드 속도 향상
"baseUrl": "./", // 기본 경로 설정
/* Bundler mode */
"moduleResolution": "Node", // Node.js 모듈 해석 방식 사용
"resolveJsonModule": true, // JSON 파일을 import 할 수 있도록 설정
"isolatedModules": true, // Vite 와 호환되도록 각 파일을 독립된 모듈로 취급
"esModuleInterop": true, // CommonJS 와 호환성 유지
"allowImportingTsExtensions": true, // ES 모듈을 CommonJS 방식으로 import 가능
"moduleDetection": "force",
"noEmit": true, // Vite 가 빌드를 처리하므로 Typescript 에서 파일을 생성하지 않음
"jsx": "react-jsx", // React 17+ 를 사용하기 위한 설정
/* Linting */
"strict": true, // 엄격한 타입 검사 활성화
"forceConsistentCasingInFileNames": true, // 파일명 대소문자 일관성 검사
"noUnusedLocals": true, // 사용되지 않은 변수 검사
"noUnusedParameters": true, // 사용되지 않은 파라미터들 검사
"noFallthroughCasesInSwitch": true,
"types": ["vite/client", "jest", "@testing-library/jest-dom"] // Vite, Jest, RTL 타입 추가
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
$ pnpm install --save-dev jest @types/jest jest-environment-jsdom @testing-library/jest-dom @testing-library/react @testing/library/jest-dom @testing-library/user-event ts-jest
// jest.setup.ts
import '@testing-library/jest-dom';
import '@jest/globals';
// jest.config.ts
import type { Config } from 'jest';
const config: Config = {
collectCoverage: true, // 취합 커버리지 가능
coverageDirectory: 'coverage',
preset: 'ts-jest',
testEnvironment: 'jsdom',
testMatch: [
'**/__tests__/**/*.?([mc])[jt]s?(x)',
'**/?(*.)+(spec|test).?([mc])[jt]s?(x)',
],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
],
coveragePathIgnorePatterns: ['/node_modules', '.*/config.*'],
coverageThreshold: {
global: {
branches: 50, // 조건문 ( if, switch 등)의 각 분기가 테스트된 비율
functions: 50, // 전체 함수 중 테스트된 함수들의 비율
lines: 50, // 전체 코드 라인 중 테스트된 라인의 비율
statements: 50, // 모든 종류의 문장 테스트 비율. (조건문, 일반 코드라인 포함)
},
'src/shared': {
branches: 60,
functions: 60,
lines: 60,
statements: -3, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
'src/pages': {
branches: 60,
functions: 60,
lines: 60,
statements: -1, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
'src/app': {
branches: 80,
functions: 80,
lines: 80,
statements: 90, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
'src/features': {
branches: 60,
functions: 70,
lines: 70,
statements: -3, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
'src/entities': {
branches: 60,
functions: 70,
lines: 70,
statements: -3, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
'src/widgets': {
branches: 50,
functions: 80,
lines: 68,
statements: -3, // 적용되지 않은 명령문이 3개 이상인 경우, jest 실패
},
},
transform: {
// Use babel-jest to transpile tests with the next/babel preset
'^.+.(js|jsx)$': 'babel-jest',
'^.+.(ts|tsx)$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.app.json' }],
},
transformIgnorePatterns: ['^.+\\.module\\.(css|sass|scss)$', '/dist'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/dist/'],
moduleNameMapper: {
'\\.(jpg|png|gif)$': '<rootDir>/src', // 이미지 파일 경로
'\\.svg$': '<rootDir>/svgMockup.ts',
'\\.(css|less|scss)$': 'identity-obj-proxy',
'lodash-es': 'lodash', // lodash es6 문법 오류 해결
// jest 테스트 코드에서 ts alias paths 사용 하기 위한 설정
'@src/(.*)$': '<rootDir>/src/$1',
'@app/(.*)$': '<rootDir>/src/app/$1',
'@entities/(.*)$': '<rootDir>/src/entities/$1',
'@features/(.*)$': '<rootDir>/src/features/$1',
'@mocks/(.*)$': '<rootDir>/src/mocks/$1',
'@pages/(.*)$': '<rootDir>/src/pages/$1',
'@shared/(.*)$': '<rootDir>/src/shared/$1',
'@store/(.*)$': '<rootDir>/src/store/$1',
'@styles/(.*)$': '<rootDir>/src/styles/$1',
'@@types/(.*)$': '<rootDir>/src/types/$1',
'@widgets/(.*)$': '<rootDir>/src/widgets/$1',
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleDirectories: ['node_modules', 'src'],
verbose: true,
};
export default config;
// src/App.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, test } from '@jest/globals';
import App from './App';
describe('App', () => {
test('renders Vite and React logos', () => {
render(<App />);
const viteLogo = screen.getByAltText('Vite logo');
const reactLogo = screen.getByAltText('React logo');
expect(viteLogo).toBeInTheDocument();
expect(reactLogo).toBeInTheDocument();
});
test('renders title', () => {
render(<App />);
const titleElement = screen.getByText('Vite + React');
expect(titleElement).toBeInTheDocument();
});
test('increments count when button is clicked', () => {
render(<App />);
const button = screen.getByText('count is 0');
fireEvent.click(button);
expect(screen.getByText('count is 1')).toBeInTheDocument();
});
test('renders instruction text', () => {
render(<App />);
const instructionText = screen.getByText(/Edit/i);
expect(instructionText).toBeInTheDocument();
});
});
Coverage data ... was not found
는 아직 해당 경로에 대한 폴더를 생성하지 않아 발생하는 것입니다.기능 분할 설계 (FSD; Feature-Sliced Design)
를 기반으로 생성하였습니다. ( 참조 링크 )