| name | ts-dev |
| description | 모던 TypeScript 개발 컨벤션을 강제하는 스킬. pnpm(패키지 관리), Biome(린터/포매터), tsc(타입 체커), Vitest(테스트), TypeScript 5.x + Node 22+ 문법을 사용한다. TypeScript 파일 생성, 프로젝트 초기화, package.json/tsconfig.json 수정, 의존성 추가, TypeScript 코드 작성 등 TS/JS 관련 작업 시 반드시 이 스킬을 사용할 것. npm, yarn, ESLint, Prettier, Jest 사용을 금지하고 pnpm/Biome/Vitest로 대체한다. 프론트엔드 프레임워크(React, Next.js, Vue 등)의 설계 패턴은 이 스킬의 범위가 아니다.
|
| hooks | {"PostToolUse":[{"matcher":"Write|Edit","hooks":[{"type":"command","command":"python3 ${CLAUDE_SKILL_DIR}/scripts/ts-post-edit-lint.py","timeout":20,"statusMessage":"TypeScript Biome 린트/포맷 적용 중..."}]}],"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"python3 ${CLAUDE_SKILL_DIR}/scripts/ts-pre-commit-check.py","timeout":180,"statusMessage":"TypeScript 커밋 전 코드 검증 중 (Biome, tsc, Vitest)..."}]}]} |
Modern TypeScript Development
이 스킬은 Rust 기반 툴체인 중심의 모던 TypeScript 개발을 강제한다. Claude가 기본적으로 제안하는 npm, ESLint, Prettier, Jest 대신, pnpm/Biome/Vitest 기반의 최신 워크플로우를 사용한다.
이 스킬은 툴체인 레이어만 담당한다. React, Next.js, Vue 등 프론트엔드 프레임워크의 컴포넌트 설계, 상태 관리, CSS 패턴은 범위 밖이다.
핵심 철학
- Rust 기반 툴링 우선 — Biome(린터/포매터)은 Rust로 작성되어 ESLint+Prettier 대비 10~20배 빠르다. ruff가 Python 생태계에서 flake8+black+isort를 대체한 것과 동일한 포지션이다.
- Strict TypeScript —
strict: true는 협상 불가. any는 사유 없이 사용하지 않는다. 타입 시스템을 최대한 활용하여 런타임 버그를 컴파일 타임에 잡는다.
- ESM-first —
"type": "module"을 기본으로 한다. CommonJS(require)는 레거시 호환이 필요한 경우에만 허용한다.
- 단일 설정 표면 —
biome.json(lint+format), tsconfig.json(타입), package.json(나머지). .eslintrc, .prettierrc, .babelrc 등 개별 설정 파일을 만들지 않는다.
금지 도구 → 대체 매핑
이 도구들은 절대 사용하지 않는다. 대신 우측의 대체 도구를 사용한다.
| 금지 | 대체 | 이유 |
|---|
npm install / npm add | pnpm add | pnpm이 디스크 효율적이고 phantom dependency 방지 |
yarn | pnpm | pnpm이 더 엄격한 의존성 해결과 더 빠른 설치 제공 |
npx | pnpm exec / pnpm dlx | 일관된 패키지 매니저 사용, pnpm exec는 로컬, dlx는 일회성 |
ESLint | Biome (lint) | Biome이 Rust 기반으로 10~20배 빠르고 lint+format 통합 |
Prettier | Biome (format) | Biome이 포매팅까지 통합, 별도 도구 불필요 |
ESLint + Prettier (조합) | Biome | 단일 도구가 두 도구의 역할을 더 빠르게 수행 |
Jest | Vitest | Vitest가 ESM 네이티브, 더 빠르고 Jest 호환 API 제공 |
ts-node | tsx | tsx가 더 빠르고 ESM 지원 우수 |
tslint | Biome | TSLint는 deprecated |
babel | tsc / tsup | 네이티브 TS 컴파일이 더 빠르고 단순 |
webpack (새 프로젝트) | Vite / tsup | Vite가 더 빠르고 설정 단순 |
package-lock.json | pnpm-lock.yaml | pnpm 락 파일 사용 |
yarn.lock | pnpm-lock.yaml | pnpm 락 파일 사용 |
.eslintrc.* | biome.json | 단일 설정 파일로 통합 |
.prettierrc.* | biome.json | 단일 설정 파일로 통합 |
프로젝트 초기화
새 프로젝트 생성
mkdir <project-name> && cd <project-name>
pnpm init
pnpm add -D typescript @biomejs/biome vitest
pnpm exec tsc --init
pnpm exec biome init
프로젝트 유형별 빌드 도구 선택
라이브러리 (npm 배포)
→ tsup
설치: pnpm add -D tsup
CLI 도구
→ tsup + package.json "bin" 필드
백엔드 서비스
→ tsx (개발), tsc (프로덕션 빌드)
설치: pnpm add -D tsx
프론트엔드 앱
→ Vite
설치: pnpm create vite
모노레포
→ pnpm workspace + 공유 설정
디렉토리 레이아웃
project-name/
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
├── biome.json
├── src/
│ ├── index.ts # 진입점
│ ├── types.ts # 공유 타입 정의
│ └── utils/
│ └── helpers.ts
├── tests/
│ ├── setup.ts # Vitest 글로벌 설정
│ └── helpers.test.ts
└── dist/ # 빌드 출력 (.gitignore)
package.json 전체 템플릿은 references/package-json-templates.md를 참조하라.
TypeScript 엄격 규칙
필수 규칙
tsconfig.json에 "strict": true를 항상 설정한다.
any 사용 시 // REASON: ... 주석으로 이유를 명시한다. unknown을 우선 고려한다.
@ts-ignore 금지. @ts-expect-error를 이유와 함께 사용한다.
- 함수 반환 타입을 명시한다 (복잡한 경우 특히).
satisfies 연산자로 타입 검증과 값 보존을 동시에 달성한다.
as const로 리터럴 타입을 활용한다.
enum 대신 as const 객체를 사용한다 (tree-shaking 가능, 런타임 오버헤드 없음).
Before / After 예시
function fetchData(url: string): Promise<any> {
return fetch(url).then((res) => res.json());
}
const config = {
port: 3000,
host: "localhost",
};
enum Status {
Active = "active",
Inactive = "inactive",
}
async function fetchData<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
return res.json() as Promise<T>;
}
const config = {
port: 3000,
host: "localhost",
} as const satisfies { port: number; host: string };
const Status = {
Active: "active",
Inactive: "inactive",
} as const;
type Status = (typeof Status)[keyof typeof Status];
외부 데이터 검증
API 응답, 환경 변수 등 외부 데이터는 런타임 검증이 필요하다. Zod를 사용한다.
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
async function getUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const data: unknown = await res.json();
return UserSchema.parse(data);
}
tsconfig.json 상세 설정은 references/tsconfig-guide.md를 참조하라.
린팅 & 포매팅
Biome이 린터와 포매터를 모두 담당한다. ESLint와 Prettier의 역할을 단일 도구로 통합한다.
기본 설정
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always"
}
}
}
실행 명령
pnpm exec biome check --write .
pnpm exec biome check .
pnpm exec biome format --write .
pnpm exec biome lint --write .
전체 Biome 설정과 규칙 그룹은 references/biome-config.md를 참조하라.
의존성 관리
pnpm add zod hono
pnpm add -D typescript @biomejs/biome vitest tsx
pnpm remove <package>
pnpm exec tsx src/index.ts
pnpm exec vitest run
pnpm exec biome check .
pnpm dlx create-vite
락 파일 관리
pnpm-lock.yaml은 반드시 git에 커밋한다 (재현 가능한 빌드 보장).
package-lock.json, yarn.lock을 생성하지 않는다.
.npmrc에 shamefully-hoist=false (기본값 유지, phantom dependency 방지).
테스팅
pnpm exec vitest run
pnpm exec vitest
pnpm exec vitest run --coverage
pnpm exec vitest run tests/helpers.test.ts
pnpm exec vitest run -t "test name"
테스트 구조
- 테스트 파일:
*.test.ts 또는 *.spec.ts
- 공통 설정:
tests/setup.ts (vitest.config.ts에서 setupFiles 지정)
import { describe, expect, it } from "vitest";
import { parseConfig } from "../src/config.js";
describe("parseConfig", () => {
it("should parse valid JSON config", () => {
const result = parseConfig('{"port": 3000}');
expect(result).toEqual({ port: 3000 });
});
it("should throw on invalid JSON", () => {
expect(() => parseConfig("not json")).toThrow();
});
});
비동기 테스트
import { describe, expect, it, vi } from "vitest";
describe("fetchUser", () => {
it("should return user data", async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ id: 1, name: "Alice" }),
});
vi.stubGlobal("fetch", mockFetch);
const user = await fetchUser(1);
expect(user.name).toBe("Alice");
vi.unstubAllGlobals();
});
});
Vitest 상세 패턴은 references/vitest-patterns.md를 참조하라.
CLI 도구
CLI가 필요한 경우 간단한 도구는 commander, 복잡한 도구는 citty를 사용한다.
pnpm add commander
import { Command } from "commander";
const program = new Command()
.name("mytool")
.description("파일 처리 도구")
.argument("<input>", "입력 파일 경로")
.option("-o, --output <dir>", "출력 디렉토리", "output")
.option("-v, --verbose", "상세 출력")
.action((input, options) => {
});
program.parse();
웹 서비스
HTTP 서비스가 필요한 경우 Hono를 기본 프레임워크로 사용한다. 경량이고 Web Standard API 기반이라 Node, Deno, Bun, Cloudflare Workers 등 어디서든 동작한다.
pnpm add hono
pnpm add -D @hono/node-server
import { Hono } from "hono";
import { serve } from "@hono/node-server";
const app = new Hono();
app.get("/health", (c) => c.json({ status: "ok" }));
serve({ fetch: app.fetch, port: 3000 }, (info) => {
console.log(`Listening on http://localhost:${info.port}`);
});
QA 체크리스트
코드를 커밋하기 전에 다음을 실행한다.
pnpm exec biome check --write .
pnpm exec tsc --noEmit
pnpm exec vitest run
모든 명령이 에러 없이 통과해야 한다.
Reference 파일
필요할 때 다음 파일을 읽어 상세 설정을 확인하라.
| 파일 | 로드 시점 |
|---|
references/package-json-templates.md | 프로젝트 초기화 또는 package.json 수정 시 |
references/biome-config.md | Biome 설정 추가/변경 또는 린트 규칙 문제 해결 시 |
references/tsconfig-guide.md | tsconfig.json 설정 또는 타입 에러 해결 시 |
references/vitest-patterns.md | 테스트 작성 또는 Vitest 설정 시 |