TypeScript 프로젝트를 시작할 때 린트와 포맷터는 보통 “있으면 좋은 것”으로 취급됩니다. 규모가 작을 때는 없어도 크게 불편하지 않습니다.
그런데 팀 단위로 개발이 시작되면 상황이 달라집니다. 코드 스타일이 미묘하게 달라지고, 리뷰에서 의미 없는 코멘트가 반복됩니다. 또, 타입 관련 실수를 린트 단계에서 걸러낼 수 있는데도 그걸 놓치는 경우가 생깁니다.
ESLint + Prettier + TypeScript 조합은 결국 도구 셋업 문제가 아니라, 팀이 “어디까지 자동화하고, 어디까지는 사람이 판단할지”를 정하는 문제로 흘러갑니다.
역할을 먼저 나누는 편이 덜 흔들린다
셋을 같이 쓰면 먼저 역할부터 분리하는 편이 안정적입니다.
- ESLint: 코드 품질, 잠재 버그, 팀 규칙
- Prettier: 포맷(공백, 줄바꿈, 따옴표 등) 자동 정리
- TypeScript: 타입 체크(컴파일러가 담당)
이 역할이 섞이면, 포맷 문제로 ESLint가 경고를 내고, 규칙 충돌로 저장할 때마다 파일이 흔들리는 일이 생깁니다.
기본 설치 조합
TypeScript 기반에서 흔히 쓰는 조합은 다음입니다.
npm i -D eslint prettier typescript
npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm i -D eslint-config-prettier eslint-plugin-prettier
eslint-config-prettier는 “포맷과 충돌하는 ESLint 규칙을 꺼주는 역할”이고, eslint-plugin-prettier는 “Prettier 결과를 ESLint 규칙처럼 실행”하는 방식입니다. 둘 다 쓸 수도 있고, 팀에 따라서는 config-prettier만 쓰고 Prettier는 별도 실행으로 분리하기도 합니다.
ESLint 설정 예시
아래는 서버/백엔드 기준으로 많이 쓰는 형태입니다. 실제 프로젝트에서는 규칙이 더 많아지지만, 구조는 이 정도에서 시작하는 경우가 많습니다.
// .eslintrc.cjs
module.exports = {
root: true,
env: {
node: true,
es2022: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'prettier',
],
rules: {
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
},
};
project 옵션을 켜면 타입 정보를 기반으로 하는 규칙들이 활성화됩니다. 실제 실수(놓친 await, 잘못된 Promise 사용 등)를 잡아주는 대신, lint 실행이 느려지고 tsconfig 범위를 잘 관리해야 합니다. 모노레포에서는 이 지점에서 설정이 꼬이는 경우가 많습니다.
Prettier 설정 예시
Prettier는 팀에서 합의할 요소만 최소로 정해두는 편이 유지가 쉽습니다. 규칙을 많이 늘리면 오히려 스타일 논쟁이 다시 시작되는 경우가 많습니다.
// .prettierrc.cjs
module.exports = {
singleQuote: true,
trailingComma: 'all',
printWidth: 100,
};
이 정도로 시작하면, 대부분의 포맷 차이는 자동으로 정리됩니다.
저장 시 자동 포맷과 린트
팀 단위에서는 “누군가는 포맷을 적용하고 누군가는 안 하는” 상황이 흔합니다. 이 상태가 지속되면 PR마다 포맷 변경이 섞입니다.
보통은 다음 둘 중 하나로 수렴합니다.
- 에디터 저장 시 Prettier만 자동 적용, ESLint는 별도 실행
- 에디터 저장 시 ESLint fix로 정리, Prettier는 ESLint를 통해 실행
어느 쪽이든 중요한 건 한 가지입니다. 팀이 같은 흐름을 쓰도록 맞추는 쪽이 비용이 적습니다.
package.json 스크립트 예시
CI까지 고려하면 스크립트는 분리해 두는 편이 관리가 쉽습니다.
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier . --check",
"format:fix": "prettier . --write",
"typecheck": "tsc --noEmit"
}
}
빌드가 통과해도 typecheck가 실패하는 상황을 막으려면, CI에서는 lint + format + typecheck를 별도로 돌리는 편이 안전합니다. 여기서 무엇을 필수로 할지는 팀과 환경에 따라 달라질 수 있습니다.
자주 겪는 설정 충돌
운영 코드에서 자주 겪는 문제는 비슷합니다.
- ESLint와 Prettier가 같은 규칙을 서로 다르게 강제한다
- parserOptions.project를 켠 뒤 모노레포에서 파일이 tsconfig 범위 밖으로 빠진다
- lint는 통과하는데 타입 체크는 실패한다
이런 문제는 도구 자체보다 tsconfig 범위와 eslint 실행 경로가 어긋나서 생기는 경우가 많습니다. 파일이 어디 tsconfig에 속하는지부터 확인하는 편이 빠릅니다.
규칙을 어디까지 강제할지
규칙을 많이 넣으면 코드 품질이 올라갈 것 같지만, 초반부터 너무 강하게 걸면 팀의 피로도가 먼저 올라갑니다.
대체로 오래 남는 구성은 다음과 같습니다.
- 포맷은 Prettier로 일관되게 자동화
- 버그 가능성이 높은 규칙은 ESLint에서 error로 강제
- 논쟁이 생기는 취향 영역은 Prettier에 맡기고 ESLint에서는 빼기
규칙은 기술 선택이라기보다 팀 합의의 결과물이 되는 경우가 많습니다.
ESLint + Prettier + TypeScript 조합은 도구를 많이 붙이는 일이 아니라, 각 도구가 책임질 범위를 정확히 나누는 일에 가깝습니다.
포맷은 자동화하고, 타입 체크는 컴파일러에 맡기고, ESLint는 실제 버그와 유지보수 비용을 줄이는 쪽에 집중시키는 편이 프로젝트가 커졌을 때 흔들리지 않습니다.
구성은 팀과 환경에 따라 달라질 수 있습니다. 다만 한 번 정한 기준이 PR과 CI에 반영되도록 만들어두면, 리뷰는 스타일이 아니라 로직에 집중하게 됩니다.
'개발 > Typescript' 카테고리의 다른 글
| [TYPESCRIPT] Vitest + TypeScript 통합: 빠르게 돌리되 흔들리지 않게 만드는 구성 (0) | 2026.02.27 |
|---|---|
| [TYPESCRIPT] Jest + TypeScript 테스트 환경을 운영 기준으로 잡는 구성 (0) | 2026.02.26 |
| [TYPESCRIPT] Babel과 TypeScript를 같이 쓰는 빌드 전략: 역할 분리와 실무 기준 (0) | 2026.02.24 |
| [TYPESCRIPT] ts-node-dev, swc, esbuild를 같이 써보고 나서 보이는 차이 (0) | 2026.02.23 |
| [TYPESCRIPT] 모노레포 환경에서 TypeScript를 관리하다 보면 보이는 경계들 (0) | 2026.02.22 |
