메인 μ½˜ν…μΈ λ‘œ κ±΄λ„ˆλ›°κΈ°
TypeScript μ†ŒμŠ€ 맡을 ν™œμš©ν•œ 효과적인 디버깅 방법을 λ‹€λ£Ήλ‹ˆλ‹€.

μ†ŒμŠ€ λ§΅μ΄λž€?

μ†ŒμŠ€ λ§΅(Source Map)은 컴파일된 JavaScript μ½”λ“œλ₯Ό 원본 TypeScript μ½”λ“œλ‘œ λ§€ν•‘ν•˜λŠ” νŒŒμΌμž…λ‹ˆλ‹€. 이λ₯Ό 톡해 μ—λŸ¬ μŠ€νƒ νŠΈλ ˆμ΄μŠ€μ™€ λ””λ²„κ±°μ—μ„œ 원본 TypeScript μ½”λ“œλ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. ν™œμš©:
  • μ—λŸ¬ μŠ€νƒ νŠΈλ ˆμ΄μŠ€μ—μ„œ 원본 TypeScript μœ„μΉ˜ 확인
  • λ””λ²„κ±°μ—μ„œ TypeScript μ½”λ“œμ— 브레이크포인트 μ„€μ •
  • ν”„λ‘œλ•μ…˜ μ—λŸ¬λ₯Ό 원본 μ½”λ“œμ™€ λ§€ν•‘

μ†ŒμŠ€ λ§΅ ν™œμ„±ν™”

tsconfig.json μ„€μ •

{
  "compilerOptions": {
    "sourceMap": true, // μ†ŒμŠ€ λ§΅ 생성
    "inlineSourceMap": false, // 별도 파일둜 생성
    "inlineSources": false, // 원본 μ†ŒμŠ€ 포함 μ•ˆ 함
    "outDir": "dist"
  }
}
μƒμ„±λ˜λŠ” 파일:

inlineSourceMap vs sourceMap

// βœ… ꢌμž₯: 별도 파일 (개발/ν”„λ‘œλ•μ…˜ λͺ¨λ‘)
{
  "compilerOptions": {
    "sourceMap": true,
    "inlineSourceMap": false
  }
}

// ❌ 인라인 (λ²ˆλ“€ 크기 증가)
{
  "compilerOptions": {
    "sourceMap": false,
    "inlineSourceMap": true  // .js 파일 λ‚΄ 포함
  }
}

μ—λŸ¬ μŠ€νƒ 트레이슀

μ†ŒμŠ€ λ§΅ 없이

Error: User not found
    at UserModelClass.findById (dist/models/user.model.js:45:15)
    at processUser (dist/services/user.service.js:12:28)
    at async main (dist/index.js:8:20)
컴파일된 JavaScript μœ„μΉ˜λ§Œ ν‘œμ‹œλ˜μ–΄ 디버깅이 μ–΄λ ΅μŠ΅λ‹ˆλ‹€.

μ†ŒμŠ€ λ§΅ μžˆμ„ λ•Œ

Error: User not found
    at UserModelClass.findById (src/models/user.model.ts:32:11)
    at processUser (src/services/user.service.ts:8:24)
    at async main (src/index.ts:5:15)
원본 TypeScript 파일과 라인 번호λ₯Ό μ •ν™•νžˆ ν‘œμ‹œν•©λ‹ˆλ‹€.

Node.jsμ—μ„œ μ†ŒμŠ€ λ§΅ μ‚¬μš©

β€”enable-source-maps ν”Œλž˜κ·Έ

# Node.js 12.12.0 이상
node --enable-source-maps dist/index.js
λ˜λŠ” package.json:
{
  "scripts": {
    "start": "node --enable-source-maps dist/index.js",
    "dev": "node --enable-source-maps --watch dist/index.js"
  }
}

tsx μ‚¬μš© (ꢌμž₯)

# tsxλŠ” μžλ™μœΌλ‘œ μ†ŒμŠ€ λ§΅ 지원
pnpm dev
SonamuλŠ” tsxλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ 별도 μ„€μ • 없이 μ†ŒμŠ€ 맡이 μž‘λ™ν•©λ‹ˆλ‹€.

VSCode 디버거 μ„€μ •

launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Sonamu Dev Server",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["dev"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "sourceMaps": true,
      "outFiles": ["${workspaceFolder}/dist/**/*.js"],
      "skipFiles": ["<node_internals>/**", "node_modules/**"]
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Run Test",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["test", "${file}"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "sourceMaps": true
    }
  ]
}

디버깅 단계

  1. 브레이크포인트 μ„€μ •
    • TypeScript 파일(.ts)μ—μ„œ 쀄 번호 μ™Όμͺ½ 클릭
  2. 디버거 μ‹œμž‘
    • F5 λ˜λŠ” β€œRun and Debug” νŒ¨λ„μ—μ„œ μ‹€ν–‰
  3. λ³€μˆ˜ 검사
    • λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈμ—μ„œ λ©ˆμΆ”λ©΄ λ³€μˆ˜ κ°’ 확인
    • Watch νŒ¨λ„μ—μ„œ ν‘œν˜„μ‹ 평가
  4. 단계별 μ‹€ν–‰
    • F10: Step Over (λ‹€μŒ 쀄)
    • F11: Step Into (ν•¨μˆ˜ λ‚΄λΆ€λ‘œ)
    • Shift+F11: Step Out (ν•¨μˆ˜ λ°–μœΌλ‘œ)

ν”„λ‘œλ•μ…˜ μ†ŒμŠ€ λ§΅

λ³΄μ•ˆ 고렀사항

// 개발 ν™˜κ²½
{
  "compilerOptions": {
    "sourceMap": true,
    "inlineSources": false // 원본 μ†ŒμŠ€ 포함 μ•ˆ 함
  }
}
ν”„λ‘œλ•μ…˜ 배포 μ‹œ:
# μ†ŒμŠ€ λ§΅ 파일 μ œμ™Έ
dist/
β”œβ”€β”€ index.js           βœ… 배포
β”œβ”€β”€ index.js.map       ❌ μ œμ™Έ (λ˜λŠ” 별도 μ €μž₯)

μ—λŸ¬ 좔적 μ„œλΉ„μŠ€ 연동

μ†ŒμŠ€ 맡을 Sentry 같은 μ—λŸ¬ 좔적 μ„œλΉ„μŠ€μ— μ—…λ‘œλ“œ:
# Sentry CLI μ˜ˆμ‹œ
sentry-cli releases files VERSION upload-sourcemaps ./dist

μ†ŒμŠ€ λ§΅ 문제 ν•΄κ²°

1. μ†ŒμŠ€ 맡이 μƒμ„±λ˜μ§€ μ•ŠμŒ

확인 사항:
# tsconfig.json 확인
cat tsconfig.json | grep sourceMap

# λΉŒλ“œ ν›„ .map 파일 확인
ls -la dist/**/*.map
ν•΄κ²°:
{
  "compilerOptions": {
    "sourceMap": true // λͺ…μ‹œμ μœΌλ‘œ ν™œμ„±ν™”
  }
}

2. μŠ€νƒ νŠΈλ ˆμ΄μŠ€κ°€ μ—¬μ „νžˆ .js νŒŒμΌμ„ 가리킴

원인: Node.jsκ°€ μ†ŒμŠ€ 맡을 μΈμ‹ν•˜μ§€ λͺ»ν•¨ ν•΄κ²°:
# --enable-source-maps ν”Œλž˜κ·Έ μΆ”κ°€
node --enable-source-maps dist/index.js

# λ˜λŠ” tsx μ‚¬μš© (μžλ™ 지원)
pnpm tsx dist/index.js

3. VSCode 디버거가 μž‘λ™ν•˜μ§€ μ•ŠμŒ

확인 사항:
// launch.json
{
  "sourceMaps": true, // ν™œμ„±ν™” 확인
  "outFiles": [
    "${workspaceFolder}/dist/**/*.js" // μ˜¬λ°”λ₯Έ 경둜
  ]
}
ν•΄κ²°:
  1. .vscode/launch.json μž¬μ„€μ •
  2. TypeScript μ„œλ²„ μž¬μ‹œμž‘: Cmd+Shift+P β†’ β€œTypeScript: Restart TS Server”
  3. 디버거 μž¬μ‹œμž‘

4. 잘λͺ»λœ 라인 번호

원인: μ†ŒμŠ€ λ§΅κ³Ό μ½”λ“œκ°€ λ™κΈ°ν™”λ˜μ§€ μ•ŠμŒ ν•΄κ²°:
# 클린 λΉŒλ“œ
rm -rf dist/
pnpm build

# λ˜λŠ” 개발 λͺ¨λ“œ μž¬μ‹œμž‘
pnpm dev

μ†ŒμŠ€ λ§΅ 검증

μˆ˜λ™ 검증

# μ†ŒμŠ€ λ§΅ 파일 확인
cat dist/index.js.map | jq .sources

# 좜λ ₯ μ˜ˆμ‹œ:
# [
#   "../src/index.ts",
#   "../src/models/user.model.ts"
# ]

source-map νŒ¨ν‚€μ§€λ‘œ 검증

import { SourceMapConsumer } from "source-map";
import fs from "fs";

const rawSourceMap = JSON.parse(fs.readFileSync("dist/index.js.map", "utf-8"));

SourceMapConsumer.with(rawSourceMap, null, (consumer) => {
  // 컴파일된 μœ„μΉ˜ -> 원본 μœ„μΉ˜
  const pos = consumer.originalPositionFor({
    line: 45,
    column: 15,
  });

  console.log(pos);
  // {
  //   source: '../src/index.ts',
  //   line: 32,
  //   column: 11,
  //   name: 'findUser'
  // }
});

λΈŒλΌμš°μ €μ—μ„œ μ†ŒμŠ€ λ§΅

Vite 개발 μ„œλ²„

ViteλŠ” μžλ™μœΌλ‘œ μ†ŒμŠ€ 맡을 μƒμ„±ν•˜κ³  μ œκ³΅ν•©λ‹ˆλ‹€:
// vite.config.ts
export default defineConfig({
  build: {
    sourcemap: true, // ν”„λ‘œλ•μ…˜ λΉŒλ“œμ—λ„ 포함
  },
});

Chrome DevTools

  1. μ†ŒμŠ€ νƒ­ μ—΄κΈ°
    • F12 β†’ Sources νƒ­
  2. TypeScript 파일 확인
    • webpack:// λ˜λŠ” src/ μ•„λž˜ .ts 파일
  3. 브레이크포인트 μ„€μ •
    • TypeScript νŒŒμΌμ—μ„œ 쀄 번호 클릭
  4. 디버깅
    • νŽ˜μ΄μ§€ μƒˆλ‘œκ³ μΉ¨ν•˜μ—¬ 브레이크포인트 도달

Best Practices

1. 항상 개발 ν™˜κ²½μ—μ„œ ν™œμ„±ν™”

{
  "compilerOptions": {
    "sourceMap": true // 개발 μ‹œ ν•„μˆ˜
  }
}

2. Gitμ—μ„œ .map 파일 μ œμ™Έ

# μ†ŒμŠ€ 맡은 λΉŒλ“œ μ‹œ 생성
dist/**/*.map

# ν”„λ‘œλ•μ…˜ μ†ŒμŠ€ 맡은 별도 관리
production-sourcemaps/

3. ν”„λ‘œλ•μ…˜μ—μ„œ 선택적 μ‚¬μš©

// ν™˜κ²½λ³„ tsconfig
// tsconfig.prod.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "sourceMap": false  // ν”„λ‘œλ•μ…˜μ—μ„œλŠ” λΉ„ν™œμ„±ν™”
  }
}
// package.json
{
  "scripts": {
    "build:dev": "tsc",
    "build:prod": "tsc -p tsconfig.prod.json"
  }
}

4. μ—λŸ¬ λͺ¨λ‹ˆν„°λ§ 톡합

ν”„λ‘œλ•μ…˜μ—μ„œλŠ” μ†ŒμŠ€ 맡을 μ—λŸ¬ 좔적 μ„œλΉ„μŠ€μ—λ§Œ 제곡:
// μ—λŸ¬ λ°œμƒ μ‹œ 원본 μœ„μΉ˜ λ§€ν•‘
import { SourceMapConsumer } from "source-map";

async function mapError(error: Error) {
  // μŠ€νƒ 트레이슀 νŒŒμ‹±
  const match = error.stack?.match(/dist\/(.+):(\d+):(\d+)/);
  if (!match) return error;

  const [, file, line, column] = match;
  const mapFile = `sourcemaps/${file}.map`;

  // μ†ŒμŠ€ 맡으둜 원본 μœ„μΉ˜ μ°ΎκΈ°
  const rawMap = JSON.parse(fs.readFileSync(mapFile, "utf-8"));
  const consumer = await new SourceMapConsumer(rawMap);

  const original = consumer.originalPositionFor({
    line: parseInt(line),
    column: parseInt(column),
  });

  return {
    ...error,
    originalFile: original.source,
    originalLine: original.line,
    originalColumn: original.column,
  };
}

κ΄€λ ¨ 도ꡬ

1. source-map-support

λŸ°νƒ€μž„μ— μ†ŒμŠ€ λ§΅ μžλ™ 적용:
pnpm add source-map-support
// index.ts 상단
import "source-map-support/register";

2. @esbuild-kit/core-utils

esbuild 기반 λΉŒλ“œ λ„κ΅¬μ˜ μ†ŒμŠ€ λ§΅ 지원:
import { installSourceMapSupport } from "@esbuild-kit/core-utils";
installSourceMapSupport();

3. Chrome DevTools

  • Elements νƒ­μ—μ„œ 컴파일된 CSSλ₯Ό 원본 SCSS/LESS둜 λ§€ν•‘
  • Network νƒ­μ—μ„œ μ†ŒμŠ€ λ§΅ λ‹€μš΄λ‘œλ“œ 확인
  • Consoleμ—μ„œ 원본 파일 링크 클릭

κ΄€λ ¨ λ¬Έμ„œ