TypeScript์ ํ์ ์์คํ ์ ์ฝ๋ ์คํ ์ ์ ํ์ ๋ถ์ผ์น, ์์ฑ ๋๋ฝ, ์๋ชป๋ ํจ์ ์๊ทธ๋์ฒ์ ๊ฐ์ ์ค์ํ ๋ฒ๊ทธ ์ ํ์ ์ก์๋ ๋๋ค. ํ์ง๋ง ๊ทธ ์ธ์ ๋ชจ๋ ๊ฒ์ ์ก์๋ผ ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ ํ๋ฆฌ์ผ์ด์ ๋ด ๋ฐ์ดํฐ ํ๋ฆ ๋ฐฉ์์ ๋ฐ๋ผ ๋ฐ์ํ๋ ๋ณด์ ์ทจ์ฝ์ , ํ ๊ฐ ๊ฒฝ๊ณ๊ฐ ๋ชจํธํด์ง๋ฉด์ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋์ ๋๋ ์ํคํ ์ฒ ์๋ฐ, ํธ์ถ์๊ฐ ์ ๊ฑฐ๋ ํ์๋ ์ฝ๋๋ฒ ์ด์ค์ ์ค๋ซ๋์ ๋จ์ ์๋ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ, ์ปดํ์ผ์ ์ ๋๋ก ๋์ง๋ง ํน์ ๋ฐํ์ ์กฐ๊ฑด์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ์ค๋ฅ ๋ฑ์ด ์์ต๋๋ค. ์ ์ ๋ถ์ ๋๊ตฌ๋ ์ด๋ฌํ ๋ถ๋ถ์ ๋ณด์ํ๋ฉฐ, ์ด๋ค ๋๊ตฌ๋ฅผ ์ ํํ ์ง๋ ์ค์ ๋ก ๋ฌด์์ ์ฐพ๊ณ ์ ํ๋์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
์ด ๊ฐ์ด๋์์๋ 2026๋ TypeScript ํ์ ์ค์ํ ๋๊ตฌ๋ค์ ๋ค๋ฃน๋๋ค. ์ฌ๊ธฐ์๋ ๋ฆฐํ ๊ณ์ธต(ESLint์ typescript-eslint, Biome, OxcLint), ๋ณด์ ๊ณ์ธต(Semgrep, Snyk Code, SonarQube), ์ํคํ ์ฒ ๊ณ์ธต(Dependency-Cruiser, Deptrac, Nx), ์ฌ์ฉ๋์ง ์๋ ์ฝ๋ ๊ณ์ธต(Knip), ๊ทธ๋ฆฌ๊ณ ์ฌ์ธต ๋ถ์ ๊ณ์ธต(ts-morph, TypeScript ์ปดํ์ผ๋ฌ ์์ฒด)์ด ํฌํจ๋ฉ๋๋ค. ๊ฐ ๋๊ตฌ์ ๋ํด ์ค์ ๊ธฐ๋ฅ, ์ค์ ๋ฐฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ด๋ค ๊ฒฝ์ฐ์ ์ ํฉํ์ง ์์์ง ์์ธํ ์ดํด๋ด ๋๋ค.
์๋ฌด๋ ์ดํดํ์ง ๋ชปํ๋ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ํด ๋ง๋ค์ด์ก์ต๋๋ค.
SMART TS XL ์ฝ๋๋ฒ ์ด์ค ์ ์ฒด์ ๊ฑธ์ณ ํธํ์ฑ์ ๊นจ๋จ๋ฆฌ๋ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ์๋ ค์ค๋๋ค.
๋ ์์๋ณด๊ธฐTypeScript ์ ์ ๋ถ์ ๋๊ตฌ: ๋น๊ตํ
๊ฐ๋ณ ๋๊ตฌ๋ฅผ ์์ธํ ์ดํด๋ณด๊ธฐ ์ ์ ์๋ ํ์์ ๊ฐ ๋๊ตฌ์ ์ฃผ์ ๊ธฐ๋ฅ, CI ์ ํฉ์ฑ ๋ฐ ์ด์์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ๋ชจ๋ ์ธก๋ฉด์ ์์ฐ๋ฅด๋ ๋จ์ผ ๋๊ตฌ๋ ์์ผ๋ฉฐ, ํจ๊ณผ์ ์ธ TypeScript ํ์ง ๋ถ์์๋ ์ฌ๋ฌ ๋๊ตฌ๋ฅผ ์กฐํฉํ์ฌ ์ฌ์ฉํ๋ ๊ฒ์ด ํ์ํฉ๋๋ค.
| ์๋จ | ์ฃผ์ ๊ธฐ๋ฅ | CI ์ ํฉ | ๋น์ฉ | ์ง์ ๊ธฐ๊ธฐ |
|---|---|---|---|---|
| ESLint + typescript-eslint | ๋ฆฐํ , ์คํ์ผ, ํ์ ์ธ์ ๊ท์น | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ํ ์ ์ฒด ๊ท์น, ๋น๋๊ธฐ ํ์ ์์ ์ฑ |
| ๋ฐ์ด์ค ๋ฉ | ๋ฆฐํ + ํฌ๋งทํ | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ESLint + Prettier ๋์ฒดํ, ์๋ ํฅ์ |
| ์ฅ์ฌ๋ฆฐํธ | ๋ฆฐํ (ESLint ํธํ) | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ๋น ๋ฅธ ๋ฆฐํธ ๊ฒ์ฌ ์๊ฐ์ด ํ์ํ ๋ชจ๋ ธ๋ ํฌ |
| ํ์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ(tsc) | ์ ํ ํ์ธ | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ํ์ ์ค๋ฅ, ์๊ฒฉํ ๋ชจ๋ ์ ์ฉ |
| ์ ๊ทธ๋ | SAST, ์ฌ์ฉ์ ์ ์ ํจํด | ๊ฐ๋ฅ | ๋ฌด๋ฃ + ์ ๋ฃ | ๋ณด์ ๊ฒ์ฌ, ์ฌ์ฉ์ ์ง์ ์กฐ์ง ๊ท์น |
| Snyk ์ฝ๋ | SAST, ์ข ์์ฑ ๋ณด์ | ๊ฐ๋ฅ | ๋ฌด๋ฃ + ์ ๋ฃ | ๋ณด์ ์ฐ์ ํ, IDE ํตํฉ |
| ์๋ํ๋ธ / ์๋ํด๋ผ์ฐ๋ | ํ์ง ๊ด๋ฆฌ, ์ถ์ธ ์ถ์ | ๊ฐ๋ฅ | ๋ฌด๋ฃ + ์ ๋ฃ | ๊ธฐ์ ์ฉ ๊ณ ํ์ง ๋์๋ณด๋ |
| ์๋๋ฆฐํธ | IDE ์์ค์ ํ์ง ํผ๋๋ฐฑ | IDE ์ ์ฉ | ๋ฌด๋ฃ | ์ธ๋ผ์ธ ๋ณด์ ๋ฐ ํ์ง ๊ด๋ จ ํ |
| ์ข ์์ฑ-ํฌ๋ฃจ์ | ์์กด์ฑ ๊ทธ๋ํ ๊ฐ์ ์ ์ฉ | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ์ํคํ ์ฒ ๊ท์น ๊ฒ์ฆ |
| ๋ํธ๋ | ๋ ์ด์ด ๊ฒฝ๊ณ ๊ฐ์ ์ ์ฉ | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ํด๋ฆฐ ์ํคํ ์ฒ, DDD ๊ฒฝ๊ณ |
| Nx | ๋ชจ๋ ธ๋ ํฌ ์ข ์์ฑ ๊ด๋ฆฌ | ๊ฐ๋ฅ | ๋ฌด๋ฃ + ์ ๋ฃ | ๋ชจ๋ ธ๋ ํฌ ๋ชจ๋ ๊ฒฝ๊ณ |
| ์๋ฅด๋ค | ์ฌ์ฉ๋์ง ์๋ ์ฝ๋ ๋ฐ ๋ฏธ์ฌ์ฉ ๋ด๋ณด๋ด๊ธฐ | ๊ฐ๋ฅ | ๋ฌด๋ฃ | ๋๊ท๋ชจ๋ก ์ฌ์ฉ๋์ง ์๋ ์ฝ๋ ์ค์ด๊ธฐ |
| ts-๋ชจํ | ํ๋ก๊ทธ๋จ ๋ฐฉ์ TS AST ๋ถ์ | ์ ํ์ ์ธ | ๋ฌด๋ฃ | ์ฌ์ฉ์ ์ ์ ๋ถ์, ์ฝ๋ ์์ , ๋๊ตฌ |
1๋จ๊ณ: ๋ณดํ ์ ๊ฑฐ ๋ฐ ์คํ์ผ๋ง
typescript-eslint๋ฅผ ์ฌ์ฉํ ESLint
ESLint๋ ์ฌ์ ํ TypeScript ๋ฆฐํ
์ ๊ธฐ๋ฐ์
๋๋ค. typescript-eslint ์ด ํจํค์ง๋ฅผ ์ฌ์ฉํ๋ฉด TypeScript์ ํ์
์ ๋ณด์ ์ ๊ทผํ ์ ์๊ณ ํ์
์ปจํ
์คํธ๊ฐ ํ์ํ ๊ท์น์ ์ ์ฉํ ์ ์๋๋ฐ, ๊ทธ์ค ๊ฐ์ฅ ์ ์ฉํ ๊ฒ์ ์ผ๋ฐ์ ์ธ Promise ์ค๋ฅ๋ฅผ ์ก์๋ด๋ ๋น๋๊ธฐ ๊ด๋ จ ๊ท์น์
๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev typescript-eslint
์๋ฐ ์คํฌ๋ฆฝํธ
// eslint.config.js
import tseslint from "typescript-eslint";
export default tseslint.config(
...tseslint.configs.strictTypeChecked,
{
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// Async safety rules -- highest value TypeScript-specific rules
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/require-await": "warn",
// Type safety
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-return": "error",
},
}
);
์์ ๋ค ๊ฐ์ง ๋น๋๊ธฐ ๊ท์น์ typescript-eslint๊ฐ ์ผ๋ฐ ESLint์ ๋นํด ์ ๊ณตํ๋ ๊ฐ์ฅ ๊ฐ์น ์๋ ์ถ๊ฐ ๊ธฐ๋ฅ์
๋๋ค. ์ด ๊ท์น๋ค์ ์ฒ๋ฆฌ๋์ง ์์ ํ๋ก๋ฏธ์ค(๋ ๋ค๋๋ ํ๋ก๋ฏธ์ค)๋ฅผ ์ก์๋
๋๋ค. await Promise๊ฐ ์๋ ๊ฐ์ ์ ์ฉ๋๊ฑฐ๋, ๋๊ธฐ ํจ์๋ฅผ ๊ธฐ๋ํ๋ ์ฝ๋ฐฑ์ ์ ๋ฌ๋๋ Promise, ๊ทธ๋ฆฌ๊ณ ๋น๋๊ธฐ ํจ์๋ฅผ ์ ํ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ์ ์ ์ฉ๋ฉ๋๋ค. await์ด๋ฌํ ํจํด๋ค์ ์ปดํ์ผ์ ๋ฌธ์ ์์ด ๋์ง๋ง ํน์ ์กฐ๊ฑด์์ ๋ฐํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
ESLint๊ฐ ํ ์ ์๋ ๊ฒ : ํ์ผ ๊ฐ ๋ฐ์ดํฐ ํ๋ฆ ๋ถ์, ์ํคํ ์ฒ ๊ฒฝ๊ณ ์ ์ฉ, ๋ณด์ ์ค์ผ ์ถ์ ๋๋ ๋นํ์ฑ ๋ด๋ณด๋ด๊ธฐ ๊ฐ์ง. ์ด๋ฌํ ๊ธฐ๋ฅ์ ์ํด์๋ ํ์ ๊ณ์ธต์ ๋๊ตฌ๊ฐ ํ์ํฉ๋๋ค.
Biome: ํ๋์ ์ธ ESLint + ๋ ๋ณด๊ธฐ ์ข์ ๋์ฒด ํ๋ก๊ทธ๋จ
Biome์ ESLint์ Prettier๋ฅผ ํ๋์ Rust ๊ธฐ๋ฐ ๋ฐ์ด๋๋ฆฌ๋ก ๋์ฒดํ๋ฉฐ, ์คํ ์๋๋ 25~35๋ฐฐ ๋น ๋ฆ ๋๋ค. JavaScript, TypeScript, JSX, JSON์ ์ง์ํฉ๋๋ค. ESLint ํ๋ฌ๊ทธ์ธ์ ๋ณต์ก์ฑ๊ณผ ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค์์์ ๋๋ฆฐ ์ฑ๋ฅ์ ๋ถ๋ง์ ๋๋ผ๋ ์ ๊ท ํ๋ก์ ํธ๋ ํ์๊ฒ Biome์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ต์ ๋์์ ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
์ธ๊ฒ ๋๋ฆฌ๋ค
# Check and format in one pass
npx @biomejs/biome check --write src/
# CI mode -- no modifications, exit non-zero on any finding
npx @biomejs/biome ci src/
Biome์ ํ๋ฌ๊ทธ์ธ ์ํ๊ณ๋ ESLint๋ณด๋ค ๊ท๋ชจ๊ฐ ์์ต๋๋ค. ์ด๋ ์ฌ์ฉ์ ์ง์ ๊ท์น์ด๋ ํ๋ ์์ํฌ๋ณ ํ๋ฌ๊ทธ์ธ์ด ๋ง์ ํ์๊ฒ ์ค์ํ ์์์ ๋๋ค. ํ์ง๋ง ํต์ฌ ESLint ๊ท์น๊ณผ Prettier ํฌ๋งทํ ๋ง ์ฌ์ฉํ๋ ํ์ ๊ฒฝ์ฐ, Biome์ ํจ์ฌ ์งง์ ์คํ ์๊ฐ์ผ๋ก ๋๋ฑํ ์์ค์ ์ฝ๋ ๊ฒ์ฆ์ ์ ๊ณตํฉ๋๋ค.
OxcLint: Speed-First ESLint ํธํ์ฑ
OxcLint(Oxc ํ๋ก์ ํธ์ ์ผ๋ถ)๋ ESLint์ ํธํ๋๋ ๊ท์น์ 50~100๋ฐฐ ๋น ๋ฅธ ์๋๋ก ์คํํฉ๋๋ค. ESLint ์ํ๊ณ ์ ์ฒด๋ฅผ ๋์ฒดํ๋ ๊ฒ์ ์๋์ง๋ง, ๋ฆฐํธ ์๊ฐ์ด ๋ณ๋ชฉ ํ์์ธ CI ํ์ดํ๋ผ์ธ์์ ๊ฐ์ฅ ๋น ๋ฅธ ์ต์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev oxlint
npx oxlint src/
OxcLint๋ ํ์ค ESLint ์คํ์ ๋ช ๋ถ์ด ์์๋๊ณ ํผ๋๋ฐฑ ๋ฃจํ ์ง์ฐ์ผ๋ก ์ธํด ํ ๋ฆฌํ์คํธ ํ์ง์ด ์ ํ๋๋ ๋๊ท๋ชจ ๋ชจ๋ ธ๋ ํฌ ํ๊ฒฝ์ ์ ํฉํ ์ ํ์ ๋๋ค. ESLint๋ฅผ ๋์ฒดํ๊ธฐ๋ณด๋ค๋ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ํจ๊ณผ์ ์ด๋ฉฐ, ๋ณํฉ ์ ๋จ๊ณ์์ ๋น ๋ฅธ ํผ๋๋ฐฑ์ ์ํด์๋ OxcLint๋ฅผ, ์ ์ฒด ๊ท์น ์ ์ฉ์ ์ํด์๋ ESLint๋ฅผ ์คํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
2๋จ๊ณ: ํ์ ๊ฒ์ฌ, TypeScript ์ปดํ์ผ๋ฌ
ํ์
์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ(tsc)๋ ๋จ์ํ ๋น๋ ๋๊ตฌ๊ฐ ์๋๋ผ, ํ์
์์ค์ ์ ํ์ฑ์ ๊ฒ์ฌํ๋ ์ฃผ์ ์ ์ ๋ถ์ ์์ง์
๋๋ค. ์๊ฒฉ ๋ชจ๋๋ฅผ ํ์ฑํํ๋ฉด ์ค์ ํ๊ฒฝ์์ ๋ฐ์ํ๋ ๋๋ถ๋ถ์ ๋ฒ๊ทธ๋ฅผ ์ก์๋ด๋ ์ค์ ์ด ์ ์ฉ๋ฉ๋๋ค.
JSON
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true,
"useUnknownInCatchVariables": true
}
}
์ธ๊ฒ ๋๋ฆฌ๋ค
# Type-check only, no emit -- ideal for CI
npx tsc --noEmit
noUnusedLocals noUnusedParameters ์ปดํ์ผ๋ฌ ์์ค์์ ์ฌ์ฉ๋์ง ์๋ ๋ณ์์ ํจ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฐพ์๋
๋๋ค. useUnknownInCatchVariables (TypeScript 4.4 ์ด์)์์ ์์ธ๋ฅผ ํฌ์ฐฉํ ์ ํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. unknown ๋ณด๋ค๋ any์ฌ์ฉ ์ ์ ๋ช
์์ ์ธ ์ ํ ์ถ์๋ฅผ ๊ฐ์ ํฉ๋๋ค.
TypeScript ์ ์ด ํ๋ฆ ๋ถ์ ์ปดํ์ผ๋ฌ๊ฐ ์กฐ๊ฑด ๋ถ๊ธฐ ๋ด์์ ํ์
์ ์ขํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ณตํ๋ ๋ด์ฅ ๊ธฐ๋ฅ์
๋๋ค. ๋ณ๋์ ๋๊ตฌ๊ฐ ์๋๋ฉฐ, strict ์ด ๋ชจ๋๋ ์๊ฒฉํ ๊ท์น์ด ์ ์ฉ๋๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ปดํ์ผ๋ฌ์ ์ ์ด ํ๋ฆ ๋ถ์์ ํ์
๊ฐ๋๋ฅผ ์ดํดํฉ๋๋ค. typeof ์ฒดํฌ ๋ฌด๋ฌ, instanceof๊ทธ๋ฆฌ๊ณ ์ฐจ๋ณ์ ์ธ ๋
ธ๋์กฐํฉ ํจํด.
tsc์ ํ๊ณ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. TypeScript ์ปดํ์ผ๋ฌ๋ ๋ณด์ ์ทจ์ฝ์ , ์ํคํ ์ฒ ๊ฒฝ๊ณ ์๋ฐ ๋๋ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ฐพ์๋ด์ง ์์ต๋๋ค. ํ์ ์ค๋ฅ๋ง ์ฐพ์๋ด๋ฉฐ, ๊ทธ ์ค๋ฅ๋ ๋ช ํํ๊ฒ ์ง๋จํฉ๋๋ค.
3๋จ๊ณ: ๋ณด์, TypeScript์ฉ SAST
Semgrep: ํจํด ๊ธฐ๋ฐ ๋ณด์ ์ค์บ๋
Semgrep์ ์ฝ๋ ํจํด ๋งค์นญ์ ํตํด ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์๋
๋๋ค. TypeScript์ ๊ฒฝ์ฐ SQL ์ธ์ ์
, XSS, ํ๋์ฝ๋ฉ๋ ์๊ฒฉ ์ฆ๋ช
, ์์ ํ์ง ์์ ์ฝ๋ ๋ฑ์ ํ์งํ ์ ์์ต๋๋ค. eval ์ฌ์ฉ ๋ฐฉ์, ํ๋กํ ํ์
์ค์ผ, ๊ทธ๋ฆฌ๊ณ ์์ ํ์ง ์์ Express.js ๊ตฌ์ฑ ํจํด์ ์ปดํ์ผ์ ์ ๋๋ก ๋์ง๋ง ์
์ฉ ๊ฐ๋ฅํ ์ทจ์ฝ์ ์ ์ ๋ฐํฉ๋๋ค.
์
# Custom rule: flag user input in SQL queries (TypeScript)
rules:
- id: ts-sql-injection-risk
patterns:
- pattern: |
const query = `SELECT ... ${$USER_INPUT} ...`;
message: "User input directly interpolated into SQL -- use parameterized queries"
languages: [typescript]
severity: ERROR
์ธ๊ฒ ๋๋ฆฌ๋ค
# Run with the community TypeScript security rules
semgrep scan --config=p/typescript --config=p/owasp-top-ten src/
Semgrep์ ESLint์ ๊ฒฝ์ํ๋ ๊ฒ์ด ์๋๋ผ ์ํธ ๋ณด์์ ์ธ ๋๊ตฌ์ ๋๋ค. ESLint๋ ๊ท์น์ ๊ฐ์ ํ๋ ๋ฐ๋ฉด, Semgrep์ ๋ณด์ ์ํฐ ํจํด์ ์ฐพ์๋ ๋๋ค. ๋ณด์์ ๊ด์ฌ ์๋ ๋๋ถ๋ถ์ TypeScript ๊ฐ๋ฐํ์ ๋ ๋๊ตฌ๋ฅผ ๋ชจ๋ ์คํํด์ผ ํฉ๋๋ค.
Snyk Code: IDE ํตํฉ ๊ธฐ๋ฅ์ ๊ฐ์ถ ML ๊ธฐ๋ฐ SAST
Snyk Code๋ ๋จธ์ ๋ฌ๋ ๊ธฐ๋ฐ ๋ถ์ ์์ง์ ์ฌ์ฉํ์ฌ ํ์ผ ์ ์ฒด์ ์ค์ผ ํ๋ฆ์ ์ถ์ ํ๋ SAST(์ํํธ์จ์ด ์์ ํ ์คํธ)๋ฅผ ์ํํฉ๋๋ค. VS Code ๋ฐ JetBrains IDE์ ํตํฉ๋์ด ๊ฐ๋ฐ์๊ฐ CI ์คํ์ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ก ํ์ํฉ๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev snyk
npx snyk auth
npx snyk code test
Snyk Code๋ ๊ฐ๋ฐ์ ๊ฒฝํ์ ์ค์ ์ ๋๊ณ IDE ๋ด์์ ํผ๋๋ฐฑ์ ์ ๊ณตํ๋ฉฐ, ๊ฐ ๋ฐ๊ฒฌ ์ฌํญ์ ๋ํ ์์ ์ ์๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ์ํ๊ธฐ ๋๋ฌธ์ ๋ณด์ ํ์ง๋งํผ ๋ณด์ ๊ต์ก์ด ์ค์ํ ๊ฒฝ์ฐ์ ๋ ๋์ ์ ํ์ ๋๋ค.
SonarQube์ SonarLint: ํ์ง ๊ฒ์ดํธ ๋ฐ ํธ๋ ๋ ์ถ์
SonarQube๋ ๋์๋ณด๋, ํธ๋ ๋ ์ถ์ , ํ ๋ฆฌํ์คํธ ๋ฐ์ฝ๋ ์ด์ ๊ธฐ๋ฅ์ ํตํด ์ง์์ ์ธ ์ฝ๋ ํ์ง ๋ถ์์ ์ ๊ณตํฉ๋๋ค. TypeScript์ ๊ฒฝ์ฐ ๋ฒ๊ทธ, ์ฝ๋ ์ค๋ฉ, ๋ณด์ ์ทจ์ฝ์ , ์ฝ๋ ์ค๋ณต์ ๊ฐ์งํฉ๋๋ค. SonarLint๋ SonarQube ๊ท์น์ IDE ๋ด์์ ํ์ธํ ์ ์๋๋ก ํด์ฃผ๋ ํ์ฅ ํ๋ก๊ทธ๋จ์ ๋๋ค.
TypeScript ํ์ ๊ฒฝ์ฐ SonarCloud(ํด๋ผ์ฐ๋ ํธ์คํ ๋ฒ์ )๊ฐ ๋ ๊ฐํธํ ๋ฐฉ๋ฒ์ ๋๋ค. ๊ณต๊ฐ ์ ์ฅ์๋ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, GitHub Actions ๋๋ GitLab CI๋ฅผ ํตํด ๋ช ๋ถ ๋ง์ CI ํตํฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
์
# .github/workflows/sonar.yml
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SonarQube๊ฐ ๋จ์ํ ๋ฆฐํ ๊ธฐ๋ฅ๋ณด๋ค ๋ฐ์ด๋ ์ฃผ๋ ์ด์ ๋ ์ฝ๋ ํ์ง์ด ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์ด๋ป๊ฒ ๋ณํํ๋์ง, ์ด๋ค ๊ตฌ์ฑ ์์๊ฐ ์ ํ๋๊ณ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ฐ ํ ๋ฆฌํ์คํธ์ ๋ํ ์๋ก์ด ์ฝ๋ ํ์ง ๊ฒ์ดํธ ์ ์๋ ์ผ๋ง์ธ์ง ๋ฑ์ ๋ณด์ฌ์ฃผ๋ ์ถ์ธ ๋ชจ๋ธ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ด๋ฆฌ์์ฉ ์งํ๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด SonarQube์ ๊ฐ์ ์ด๋ฉฐ, ESLint๋ Semgrep์ ์ด๋ฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ๋ชปํฉ๋๋ค.
4๋จ๊ณ: ์ํคํ ์ฒ ๋ถ์
Dependency-Cruiser: ๋ชจ๋ ๊ฒฝ๊ณ ๊ท์น ์ ์ฉ
Dependency-Cruiser๋ ์ํฌํธ ๊ทธ๋ํ๊ฐ ์ ์๋ ์ํคํ ์ฒ ๊ท์น์ ๋ฐ๋ฅด๋์ง ๊ฒ์ฆํฉ๋๋ค. ์๊ฐ์ ์ธ ์ข ์์ฑ ๊ทธ๋ํ๋ฅผ ์์ฑํ๊ณ , ์ฝ๋๊ฐ ๋ชจ๋ ๊ฒฝ๊ณ ์ ์ฝ ์กฐ๊ฑด์ ์๋ฐํ ๊ฒฝ์ฐ CI๋ฅผ ์คํจ ์ฒ๋ฆฌํฉ๋๋ค.
์๋ฐ ์คํฌ๋ฆฝํธ
// .dependency-cruiser.cjs
module.exports = {
forbidden: [
{
name: "no-circular",
severity: "error",
comment: "Circular dependencies make code hard to test and maintain",
from: {},
to: { circular: true },
},
{
name: "no-ui-in-domain",
severity: "error",
comment: "Domain modules must not import from UI layer",
from: { path: "^src/domain" },
to: { path: "^src/ui" },
},
{
name: "no-external-in-shared",
severity: "warn",
comment: "Shared utilities should minimize external dependencies",
from: { path: "^src/shared" },
to: { pathNot: "^(src|node_modules/(lodash|date-fns))" },
},
],
};
์ธ๊ฒ ๋๋ฆฌ๋ค
npx depcruise --validate .dependency-cruiser.cjs src/
Dependency-Cruiser๋ ๊ฒ์ ์ฝ์ ๋ฐ์ดํฐ์์ "react dependency analysis cli tool" ์ฟผ๋ฆฌ์ ์ง์ ์ ์ผ๋ก ๋์ํ๋ฉฐ, TypeScript/React ํ๋ก์ ํธ์์ ์์กด์ฑ ๊ทธ๋ํ๋ฅผ ์์ฑํ๊ณ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ ํ์ค ๋๊ตฌ์ ๋๋ค.
Deptrac: ๋ ์ด์ด ๊ธฐ๋ฐ ๊ฒฝ๊ณ ๊ฐ์ ์ ์ฉ
Deptrac์ ์ํคํ ์ฒ ๊ณ์ธต์ ๊ฐ์ ํ์ฌ ์๊ตฌ ์ ์ฅ ์ฝ๋๊ฐ ํ๋ ์ ํ ์ด์ ์์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ๊ณ , ๋๋ฉ์ธ ๊ฐ์ฒด๊ฐ ์ธํ๋ผ์ ์์กดํ์ง ์๋๋ก ํ๋ฉฐ, ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค์์ ๋ชจ๋ ๊ฒฝ๊ณ๊ฐ ์ค์๋๋๋ก ํฉ๋๋ค.
์
# deptrac.yaml
parameters:
layers:
- name: Domain
collectors:
- type: directory
value: src/domain
- name: Application
collectors:
- type: directory
value: src/application
- name: Infrastructure
collectors:
- type: directory
value: src/infrastructure
ruleset:
Domain:
- ~Infrastructure # Domain must not depend on Infrastructure
Application:
- Domain
Infrastructure:
- Application
- Domain
Deptrac์ ๋ ์ด์ด ๋ถ๋ฆฌ๊ฐ ๋จ์ํ ์ ํธ๊ฐ ์๋ ์ค๊ณ ์ ์ฝ ์กฐ๊ฑด์ธ ํด๋ฆฐ ์ํคํ ์ฒ, DDD ๋๋ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ ํจํด์ ๋ฐ๋ฅด๋ ํ๋ก์ ํธ์์ ๊ฐ์ฅ ํฐ ๊ฐ์น๋ฅผ ๋ฐํํฉ๋๋ค.
Nx: ๋ชจ๋ ธ๋ ํฌ ์์ค์ ์ข ์์ฑ ๊ด๋ฆฌ
TypeScript ๋ชจ๋ ธ๋ ํฌ์ ๊ฒฝ์ฐ, Nx๋ ์ ์ฅ์์ ๋ชจ๋ ํ๋ก์ ํธ์ ๊ฑธ์ณ ๋ชจ๋ ๊ฒฝ๊ณ ๊ฐ์ , ์ํฅ์ ๋ฐ๋ ๋น๋ ๊ฐ์ง ๋ฐ ์ข ์์ฑ ๊ทธ๋ํ ์๊ฐํ๋ฅผ ์ ๊ณตํฉ๋๋ค.
JSON
// .eslintrc.json -- Nx module boundary rules
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"allow": [],
"depConstraints": [
{ "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] },
{ "sourceTag": "scope:feature", "onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"] },
{ "sourceTag": "scope:app", "onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"] }
]
}
]
}
}
Nx์ affected ์ด ๋ช
๋ น์ด๋ ๋ณ๊ฒฝ๋ ๋ชจ๋๊ณผ ๊ด๋ จ๋ ํ
์คํธ ๋ฐ ๋ฆฐํ
๋ง ์คํํ์ฌ ๋๊ท๋ชจ ๋ชจ๋
ธ๋ ํฌ์์ CI ์๊ฐ์ ํฌ๊ฒ ๋จ์ถํฉ๋๋ค.
5๊ณ์ธต: ๋ฐ๋ ์ฝ๋ ๊ฐ์ง
Knip: ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ, ํ์ผ ๋ฐ ์ข ์์ฑ ์ฐพ๊ธฐ
Knip์ ์ ์ฒด ๋ชจ๋ ๊ทธ๋ํ๋ฅผ ๋ถ์ํ์ฌ ์ฌ์ฉ๋์ง ์๋ ๋ด๋ณด๋ด๊ธฐ, ์ฌ์ฉ๋์ง ์๋ ํ์ผ ๋ฐ ์ฌ์ฉ๋์ง ์๋ ํญ๋ชฉ์ ์๋ณํฉ๋๋ค. package.json ์์กด์ฑ, ESLint๊ฐ ์ ์ํ๋ ์ฝ๋ ๋์ ์ ํ no-unused-vars ํ์ผ ๋ด๋ถ๋ง ํ์ธํ๊ธฐ ๋๋ฌธ์ ํฌ์ฐฉํ ์ ์์ต๋๋ค.
์ธ๊ฒ ๋๋ฆฌ๋ค
npm install --save-dev knip
npx knip
JSON
// knip.json
{
"entry": ["src/index.ts", "src/**/*.test.ts"],
"project": ["src/**/*.ts"],
"ignore": ["src/generated/**"],
"ignoreDependencies": ["vitest"]
}
Knip์ SC ๋ฐ์ดํฐ์์ "dead code detection unused exports javascript typescript"๋ก ์ง์ ๊ฒ์๋ฉ๋๋ค. ์ด๋ TypeScript ํ๋ก์ ํธ์์ ๋ชจ๋ ์์ค์ ์ฌ์ฉ๋์ง ์๋ ์ฝ๋(dead code)๋ฅผ ์๋ณํ๋ ๋ฐ ์์ด ํ์ฌ ๊ฐ์ฅ ๋ฐ์ด๋ ๋๊ตฌ์ ๋๋ค.
๋ ์ด์ด 6: ํ๋ก๊ทธ๋จ ๋ถ์, ts-morph
ts-morph๋ TypeScript ์ปดํ์ผ๋ฌ API ๋ํผ๋ก, TypeScript AST์ ๋ํ ์ฌ์ฉ์ ์ง์ ๋ถ์, ์ฝ๋ ์์ ๋ฐ ๋๊ตฌ๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋๋ก ํด์ค๋๋ค. SC ๋ฐ์ดํฐ์์ "ts-morph" ๋๋ "ts morph"๋ก ์ง์ ๊ฒ์ํ ์ ์์ต๋๋ค.
ํ์ดํ ์คํฌ๋ฆฝํธ
import { Project } from "ts-morph";
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
// Find all async functions that never await anything
const suspiciousAsyncFns: string[] = [];
for (const sourceFile of project.getSourceFiles()) {
for (const fn of sourceFile.getFunctions()) {
if (fn.isAsync()) {
const hasAwait = fn.getDescendantsOfKind(
SyntaxKind.AwaitExpression
).length > 0;
if (!hasAwait) {
suspiciousAsyncFns.push(
`${sourceFile.getFilePath()}:${fn.getName() ?? "anonymous"}`
);
}
}
}
}
console.log("Async functions with no await:", suspiciousAsyncFns);
ts-morph๋ ๋ฐ๋ก ์คํ ๊ฐ๋ฅํ ๋ถ์ ๋๊ตฌ๊ฐ ์๋๋ผ, ๊ทธ๋ฌํ ๋๊ตฌ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ๊ธฐ์กด ๋๊ตฌ์์ ์ง์ํ๋ ๋ฒ์๋ฅผ ๋์ด์๋ ๋ง์ถคํ ๋ถ์์ด ํ์ํ ํ, ์๋ฅผ ๋ค์ด ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ, ๋ง์ถคํ ์ํคํ ์ฒ ์ ํจ์ฑ ๊ฒ์ฌ๊ธฐ, ์๋ ๋ฆฌํฉํ ๋ง ๋๋ ์ฝ๋ ์์ฑ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ์ ์ ํฉํฉ๋๋ค.
TypeScript ๋น๋๊ธฐ/Await ์ ์ ๋ถ์
๊ฒ์ ์ฝ์ ๋ฐ์ดํฐ๋ "typescript ์ ์ ๋ถ์ async await ๋ ผ๋ฌธ ๋๊ตฌ" ๋ฐ "typescript ์ ์ ๋ถ์ async await ์ทจ์ฝ์ ๋ ผ๋ฌธ ๋๊ตฌ"์ ๊ด๋ จ๋ ํน์ ๊ฒ์์ด ํด๋ฌ์คํฐ๋ฅผ ๋ณด์ฌ์ค๋๋ค. ์ด๋ ์ค๋ฌด์๋ค์ด ํ์ ์์ค์์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ์ค๋ฅ๋ฅผ ์ดํดํ๋ ๋๊ตฌ๋ฅผ ์ฐพ๊ณ ์์์ ๋ฐ์ํฉ๋๋ค.
์ค์ TypeScript ๊ฐ๋ฐํ์๊ฒ ์ค์ง์ ์ธ ํด๊ฒฐ์ฑ
์ typescript-eslint์ ๋น๋๊ธฐ ๊ด๋ จ ๊ท์น ๋ค ๊ฐ์ง์
๋๋ค.no-floating-promises, await-thenable, no-misused-promises, require-await)์ ๊ฒฐํฉ strictNullChecks useUnknownInCatchVariables์ด๋ค์ ํจ๊ป ์ฌ์ฉํ๋ฉด ํ์ ์ฐ๊ตฌ ๋๊ตฌ ์์ด๋ ๊ฐ์ฅ ํํ ๋น๋๊ธฐ ์ ํ ์ค๋ฅ๋ฅผ ์ก์๋ผ ์ ์์ต๋๋ค.
๋น๋๊ธฐ ์ทจ์ฝ์ ํจํด์ ๋ํ ๋ณด์ ์ฐ๊ตฌ๋ฅผ ์ํํ๋ ํ์๊ฒ TAJS์ Jelly๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๋๊ธฐ ์คํ ์๋ฏธ๋ก ์ ๋ชจ๋ธ๋งํ๋ ํ์ ์ ์ธ ์ ์ ๋ถ์ ๋๊ตฌ์ด์ง๋ง, ์ด๋ ์ฐ๊ตฌ ๋๊ตฌ์ผ ๋ฟ ์ค์ ๊ฐ๋ฐ ๋๊ตฌ๋ ์๋๋๋ค.
Angular ๋ฐ React: ํ๋ ์์ํฌ๋ณ ์ ์ ๋ถ์
Angular ๊ฐ๋ฐํ์ ์ํ ์ ๋ณด์
๋๋ค., @angular-eslint Angular์ ํนํ๋ ์ปดํฌ๋ํธ ํจํด, ํ
ํ๋ฆฟ ๋ถ์ ๋ฐ ์๋น์ค ์ฃผ์
๊ด๋ จ ๋ฆฐํ
๊ท์น์ ์ ๊ณตํฉ๋๋ค. ์์ ESLint + typescript-eslint ์ค์ ๊ณผ ํตํฉ๋ฉ๋๋ค.
React ํ์ ์ํด ๋ฐธ๋ฆฌ eslint-plugin-react, eslint-plugin-react-hooks์์ฐ ๋ฐ eslint-plugin-jsx-a11y ํ๋ฌ๊ทธ์ธ์ React ๊ด๋ จ ํจํด์ ๋ค๋ฃน๋๋ค. Dependency-Cruiser๋ React ๊ด๋ จ ์์กด์ฑ ๊ทธ๋ํ ๋ถ์์ ์ฒ๋ฆฌํ๋ฉฐ, SC ๋ฐ์ดํฐ์์ "react dependency analysis cli tool" ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋๊ตฌ์
๋๋ค.
IDE ํตํฉ: VS Code ๋ฐ JetBrains์ฉ ๋๊ตฌ
"VS Code ํตํฉ์ ์ํ ์ต๊ณ ์ ์ฝ๋ ํ์ง ๋๊ตฌ" ๋ฐ "IDE ํตํฉ์ ์ํ ์ต๊ณ ์ ์ฝ๋ ํ์ง ๋๊ตฌ"๋ผ๋ ๊ฒ์์ด๋ ์ค์ ์ ์ธ ํ์์ฑ์ ๋ฐ์ํฉ๋๋ค. CI ํผ๋๋ฐฑ์ ๋์์ ๋ณ๊ฒฝํ๊ธฐ์๋ ๋๋ฌด ๋ฆ๊ฒ ๋์ฐฉํฉ๋๋ค. IDE ํตํฉ ๋ถ์์ ๊ฐ์ฅ ๋น ๋ฅธ ํผ๋๋ฐฑ ๋ฃจํ๋ฅผ ์ ๊ณตํฉ๋๋ค.
VS Code : rust-analyzer Clippy์ ๋๋ฑํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ESLint ํ์ฅ ํ๋ก๊ทธ๋จ, SonarQube ๊ท์น์ ์ธ๋ผ์ธ์ผ๋ก ํ์ํ๋ SonarLint ํ์ฅ ํ๋ก๊ทธ๋จ, ๋ณด์ ์ทจ์ฝ์ ์ ์๋ ค์ฃผ๋ Snyk ํ์ฅ ํ๋ก๊ทธ๋จ, ๊ทธ๋ฆฌ๊ณ ํ์ ์์ค ํผ๋๋ฐฑ์ ์ ๊ณตํ๋ ๋ด์ฅ TypeScript ์ธ์ด ์๋น์ค๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
JetBrains(WebStorm/IntelliJ) : WebStorm์ ํ์ ์ค๋ฅ๋ฅผ ์ฝ๋ ๋ด์์ ๋ฐ๋ก ํ์ํด์ฃผ๋ TypeScript ์ง์ ๊ธฐ๋ฅ๊ณผ ESLint, Prettier, SonarLint์์ ํตํฉ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋ด์ฅ๋ ๊ฒ์ฌ ์์คํ ์ ESLint ๊ท์น์์ ๋ค๋ฃจ๋ ๋ง์ ํจํด์ ๋์ผํ๊ฒ ๊ฒ์ฌํฉ๋๋ค.
VS Code์์ TypeScript ์ ์ ๋ถ์ ๋ฒ์๋ฅผ ์ต๋ํํ๊ธฐ ์ํ ์ค์ :
JSON
// .vscode/settings.json
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["javascript", "typescript", "typescriptreact"],
"sonarlint.connectedMode.project": {
"connectionId": "my-sonarcloud",
"projectKey": "my-org_my-project"
}
}
๋ฐฉ๋ฒ SMART TS XL TypeScript ๋ถ์ ๊ธฐ๋ฅ์ ๊ธฐ์ ์ ์ฒด๋ก ํ์ฅํฉ๋๋ค.
์์ ๋๊ตฌ๋ค์ TypeScript ํ๋ก์ ํธ ๋ด์ TypeScript ์ฝ๋๋ง์ ๋ค๋ฃน๋๋ค. TypeScript ์๋น์ค๊ฐ COBOL ๋ฐฐ์น ํ๋ก๊ทธ๋จ, Java API, Python ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๋๋ ๊ธฐ์กด ๋ฉ์ธํ๋ ์ ์์คํ ๊ณผ ์ํธ ์์ฉํ๋ ์กฐ์ง์์๋ ๋จ์ผ ์ธ์ด ๋๊ตฌ๋ก๋ ์ธ์ด ๊ฒฝ๊ณ๋ฅผ ๋๋๋๋ ์ข ์์ฑ์ ํ์ ํ ์ ์์ต๋๋ค.
SMART TS XL์ ์ ์ ์ฝ๋ ๋ถ์ TypeScript๋ COBOL, JCL, Java, Python, RPG, SQL ๋ฑ ๊ธฐ์ ํ๊ฒฝ์์ ์ฌ์ฉ๋๋ ๋ชจ๋ ์ธ์ด์ ํจ๊ป ๋ค๋ค์ง๋ฉฐ, ์ด๋ฌํ ์ธ์ด๋ค ์ ๋ฐ์ ๊ฑธ์ณ ํตํฉ๋ ์ข ์์ฑ ๋ชจ๋ธ์ ๊ตฌ์ถํฉ๋๋ค. TypeScript ์๋น์ค๊ฐ COBOL ํ๋ก๊ทธ๋จ์ผ๋ก ์ง์๋๋ API๋ฅผ ํธ์ถํ ๋, SMART TS XL ๊ทธ ๊ด๊ณ๋ฅผ ์ถ์ ํ์ฌ ํฌํจ์ํฌ ์ ์์ต๋๋ค. ์ํฅ ๋ถ์ ์ด๋ค ๊ตฌ์ฑ ์์๋ผ๋ ๋ณ๊ฒฝํ๊ธฐ ์ ์.
์ํฐํ๋ผ์ด์ฆ ๊ฒ์ ๊ธฐ๋ฅ์ ํตํด ์ ์ฒด ๋ค๊ตญ์ด ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ฟผ๋ฆฌํ ์ ์์ต๋๋ค. ํน์ ๋ชจ๋์ ๋ชจ๋ TypeScript ๊ฐ์ ธ์ค๊ธฐ, TypeScript ์๋น์ค๊ฐ ํธ์ถํ๋ ๋ชจ๋ Java ๋ฉ์๋, TypeScript์์ ์ฌ์ฉํ๋ API์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๋ ๋ชจ๋ COBOL ์นดํผ๋ถ ๋ฑ์ ์๋ฐฑ๋ง ์ค์ ์ฝ๋์์ ์ด๋ค ์ธ์ด ์กฐํฉ์ผ๋ก๋ ๋ช ์ด ๋ง์ ์ฐพ์ ์ ์์ต๋๋ค.
๋ ํฐ ํฌํธํด๋ฆฌ์ค ๋ด์์ TypeScript๋ฅผ ํ๋์ ์ธ์ด๋ก ์ฌ์ฉํ๋ ์ํฐํ๋ผ์ด์ฆ ํ์ ๊ฒฝ์ฐ, SMART TS XL ๊ฑด์ถ์ ๊ฐ์์ฑ๊ณผ ๋ค๊ตญ์ด ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ข ์์ฑ ๋งคํ ์ด๋ฅผ ํตํด TypeScript ์ ์ฉ ๋๊ตฌ๋ ์์คํ ์ ํด๋น ๋ถ๋ถ์ ์ดํด๋ณด๊ฒ ๋ฉ๋๋ค. SMART TS XL ์ ์ฒด๋ฅผ ๋ณธ๋ค.
์ ํฉํ ์คํ ๊ตฌ์ถ
TypeScript์ ๋ชจ๋ ํ์ง ๊ธฐ์ค์ ์ถฉ์กฑํ๋ ๋จ์ผ ๋๊ตฌ๋ ์์ต๋๋ค. ์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ๊ณ์ธตํ์ด๋ฉฐ, ๊ฐ ๊ณ์ธต์ ์๋ก ๋ค๋ฅธ ์ ํ์ ๋ฌธ์ ๋ฅผ ๋ชฉํ๋ก ํฉ๋๋ค.
์ต์ ํ์ ๊ธฐ์ ์คํ (์ ํ๋ก์ ํธ, ์๊ท๋ชจ ํ):
- ํ์ ์คํฌ๋ฆฝํธ ์๊ฒฉ ๋ชจ๋
- typescript-eslint ๋น๋๊ธฐ ๊ท์น์ด ํฌํจ๋ ESLint
npm audit์์กด์ฑ ์ทจ์ฝ์ ์ ๊ฒฝ์ฐ
์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์คํ (์ค๊ท๋ชจ ํ, ๋ณด์ ์ค์๋ ๊ณ ๋ ค):
- ์์ ๋ชจ๋ ๊ฒ ์ธ์๋
- Biome ๋๋ Prettier๋ฅผ ์ฌ์ฉํ์ฌ ํ์์ ์ง์ ํ์ธ์.
- ๋ณด์ SAST์ฉ Semgrep
- ์ฃฝ์ ์ฝ๋์ฉ Knip
- SonarCloud๋ฅผ ํ์ฉํ ํ์ง ์ถ์ธ ํ์
์ํฐํ๋ผ์ด์ฆ/๋ชจ๋ ธ๋ ํฌ ์คํ (๋๊ท๋ชจ ํ, ์ํคํ ์ฒ ์ ์ฝ ์กฐ๊ฑด):
- ์์ ๋ชจ๋ ๊ฒ ์ธ์๋
- ๋ชจ๋ ๊ฒฝ๊ณ ๊ฐ์ ๋ฅผ ์ํ Dependency-Cruiser
- ๋ชจ๋ ธ๋ ํฌ ์ํฅ ๋น๋ ์ต์ ํ๋ฅผ ์ํ Nx
- ๋ ์ด์ด ๊ฒฝ๊ณ ๊ฒ์ฆ์ ์ํ Deptrac
- ์จํ๋ ๋ฏธ์ค ํ์ง ๊ฒ์ดํธ์ฉ SonarQube(์์ฒด ํธ์คํ )
๋ค๊ตญ์ด ์ํฐํ๋ผ์ด์ฆ ์คํ (TypeScript์ ๊ธฐ์กด ์์คํ ๋ณ์ ):
- ์์ ๋ชจ๋ ๊ฒ ์ธ์๋
- SMART TS XL ์ธ์ด ๊ฐ ์ํฅ ๋ถ์ ๋ฐ ์์กด์ฑ ๋งคํ์ ์ํด


