๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Sonamu ํ”„๋กœ์ ํŠธ๋Š” TypeScript๋กœ ์ž‘์„ฑ๋˜๋ฉฐ, API์™€ Web ๊ฐ๊ฐ ๋…๋ฆฝ์ ์ธ tsconfig.json์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ํ™˜๊ฒฝ์— ์ตœ์ ํ™”๋œ ์„ค์ •์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

API์™€ Web์€ ๊ฐ๊ฐ ๋…๋ฆฝ์ ์ธ TypeScript ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์„ค์ •์ด ์„œ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

API ์„œ๋ฒ„ ์„ค์ •

๊ธฐ๋ณธ tsconfig.json

api/tsconfig.json
{
  "compilerOptions": {
    /* Basic Options */
    "target": "esnext",
    "module": "esnext",
    "outDir": "dist",
    "sourceMap": true,
    "lib": ["esnext", "dom"],

    /* Strict Type-Checking Options */
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,

    /* Additional Checks */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "useUnknownInCatchVariables": true,

    /* Module Resolution Options */
    "moduleResolution": "bundler",
    "esModuleInterop": true,

    /* Experimental Options */
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,

    /* Advanced Options */
    "forceConsistentCasingInFileNames": true,
    "noErrorTruncation": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "noUncheckedIndexedAccess": true
  },
  "include": ["src/**/*.ts", "src/**/*.json"],
  "exclude": [
    "node_modules",
    "dist/*",
    "public",
    // "src/**/*.test.ts",  // ํ…Œ์ŠคํŠธ ํŒŒ์ผ๋„ ํƒ€์ž… ์ฒดํฌํ•˜๋ฏ€๋กœ ์ฃผ์„ ์ฒ˜๋ฆฌ
    "**/__mocks__/**",
    "vite.config.ts"
  ]
}

์ฃผ์š” ์˜ต์…˜ ์„ค๋ช…

{
  "target": "esnext",           // ์ตœ์‹  ECMAScript ๋ฌธ๋ฒ• ์‚ฌ์šฉ
  "module": "esnext",            // ES ๋ชจ๋“ˆ ์ถœ๋ ฅ
  "moduleResolution": "bundler"  // Vite/esbuild์™€ ํ˜ธํ™˜
}
moduleResolution: โ€œbundlerโ€
  • Node.js์˜ node ๋ชจ๋“œ ๋Œ€์‹  ๋ฒˆ๋“ค๋Ÿฌ ๋ชจ๋“œ ์‚ฌ์šฉ
  • Vite์™€ esbuild๊ฐ€ ๋ชจ๋“ˆ์„ ํ•ด์„ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์ผ์น˜
  • package.json์˜ exports ํ•„๋“œ ์ง€์›
moduleResolution: "node"๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Vite์™€ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ™•์žฅ ์„ค์ • ํŒŒ์ผ๋“ค

Sonamu API๋Š” ์ฝ”๋“œ ์ƒ์„ฑ์„ ์œ„ํ•ด ์ถ”๊ฐ€ tsconfig ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
api/tsconfig.schemas.json
{
  "extends": "./tsconfig.json",
  "include": ["src/sonamu.generated.ts"],
  "exclude": ["src/**/*.types.ts"]
}
์šฉ๋„:
  • sonamu.generated.ts ํŒŒ์ผ ์ƒ์„ฑ
  • Entity ์Šคํ‚ค๋งˆ ํƒ€์ž… ์ƒ์„ฑ
  • DB ์Šคํ‚ค๋งˆ โ†’ TypeScript ํƒ€์ž… ๋ณ€ํ™˜
์‚ฌ์šฉ ์‹œ์ :
# Sonamu๊ฐ€ ์ž๋™์œผ๋กœ ์‚ฌ์šฉ
pnpm sonamu entity:load
api/tsconfig.types.json
{
  "extends": "./tsconfig.json",
  "include": ["src/**/*.types.ts"],
  "references": [{ "path": "./tsconfig.schemas.json" }]
}
์šฉ๋„:
  • API ์‘๋‹ต ํƒ€์ž… ์ƒ์„ฑ
  • Request/Response ํƒ€์ž… ์ •์˜
  • ํ”„๋ก ํŠธ์—”๋“œ์™€ ํƒ€์ž… ๊ณต์œ 
์‚ฌ์šฉ ์‹œ์ :
# Web์œผ๋กœ ํƒ€์ž… ๋™๊ธฐํ™”
pnpm sonamu sync:types

Web (React) ์„ค์ •

web/tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src", "src/routeTree.gen.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

API์™€์˜ ์ฐจ์ด์ 

์˜ต์…˜APIWeb์ด์œ 
moduleResolutionbundlerBundlerVite๊ฐ€ ๋ฒˆ๋“ค๋Ÿฌ ๋ฐฉ์‹ ์‚ฌ์šฉ
jsx-react-jsxReact 17+ JSX Transform
noEmitfalsetrueVite๊ฐ€ ๋นŒ๋“œ ๋‹ด๋‹น
isolatedModules-trueVite์˜ esbuild ์š”๊ตฌ์‚ฌํ•ญ
paths-@/*์ ˆ๋Œ€ ๊ฒฝ๋กœ import
API๋Š” moduleResolution: "bundler" (์†Œ๋ฌธ์ž), Web์€ moduleResolution: "Bundler" (๋Œ€๋ฌธ์ž)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค Vite๊ฐ€ ๋ฒˆ๋“ค๋Ÿฌ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋“ˆ์„ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค.

Path Mapping

{
  "paths": {
    "@/*": ["./src/*"]
  }
}
์ ˆ๋Œ€ ๊ฒฝ๋กœ๋กœ importํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
// โŒ ์ƒ๋Œ€ ๊ฒฝ๋กœ
import { Button } from "../../../components/Button";

// โœ… ์ ˆ๋Œ€ ๊ฒฝ๋กœ
import { Button } from "@/components/Button";
์ž์„ธํ•œ ๋‚ด์šฉ์€ Path Mapping์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

ํƒ€์ž… ์ฒดํฌ ์‹คํ–‰

cd api

# ํƒ€์ž… ์ฒดํฌ๋งŒ
pnpm tsc --noEmit

# Watch ๋ชจ๋“œ
pnpm tsc --noEmit --watch
๊ฐœ๋ฐœ ์ค‘์—๋Š” --watch ๋ชจ๋“œ๋กœ ์‹ค์‹œ๊ฐ„ ํƒ€์ž… ์ฒดํฌ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ ํ•ด๊ฒฐ

์ฆ์ƒ:
Cannot find module 'sonamu' or its corresponding type declarations.
์›์ธ:
  • node_modules๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ
  • ์ž˜๋ชป๋œ import ๊ฒฝ๋กœ
ํ•ด๊ฒฐ:
# ์˜์กด์„ฑ ์žฌ์„ค์น˜
pnpm install

# ํƒ€์ž… ์ •์˜ ํ™•์ธ
ls node_modules/sonamu/dist/*.d.ts
์ฆ์ƒ:
Experimental support for decorators is a feature that is subject to change
์›์ธ:
  • experimentalDecorators๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋จ
ํ•ด๊ฒฐ:
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
์ฆ์ƒ:
Cannot use JSX unless the '--jsx' flag is provided.
์›์ธ:
  • jsx ์˜ต์…˜์ด ์„ค์ •๋˜์ง€ ์•Š์Œ
ํ•ด๊ฒฐ:
{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}
React 17+ ํ•„์ˆ˜ ์„ค์ •
์ฆ์ƒ:
Cannot find module '@/components/Button'
์›์ธ:
  • paths ์„ค์ • ๋ˆ„๋ฝ
  • baseUrl ์„ค์ • ํ•„์š”
ํ•ด๊ฒฐ:
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
Vite ์„ค์ •๋„ ํ•„์š”:
vite.config.ts
import path from "path";

export default {
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
};

์ปค์Šคํ„ฐ๋งˆ์ด์ง•

์ถ”๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ€์ž…

{
  "compilerOptions": {
    "lib": ["esnext", "dom", "dom.iterable", "webworker"]
  }
}

๋” ์—„๊ฒฉํ•œ ์ฒดํฌ

{
  "compilerOptions": {
    "noPropertyAccessFromIndexSignature": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}
์ด ์˜ต์…˜๋“ค์€ ๊ธฐ์กด ์ฝ”๋“œ์™€ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

include/exclude ํŒจํ„ด

{
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.json"
  ],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts",
    "**/*.spec.ts",
    "**/__mocks__/**"
  ]
}

๋‹ค์Œ ๋‹จ๊ณ„