Seung Hun

2026년 9월 24일

어떠한 이유로 번들링된 결과물에 특정 코드를 추가해야 한다고 해보자. 이런 상황에서 소스맵은 어떻게 갱신되어야 할까?

번들 앞에 코드 추가하기

간단하게 예시를 들어보자.

예제 코드

src/main.js

javascript
import { pay } from "./payment.js";

pay(0);

src/payment.js

javascript
export function pay(amount) {
  console.log("결제 요청:", amount);

  if (amount <= 0) {
    throw new Error("결제 금액은 0보다 커야 합니다.");
  }

  console.log(`${amount}원 결제 완료`);
}

플러그인으로 로그 추가하기

renderChunk에서 받은 번들 코드 앞에 로그를 추가하고, 우선 변경된 코드만 반환해보자.

prepend.config.mjs

javascript
import MagicString from "magic-string";

export default {
  input: "src/main.js",
  output: {
    file: "dist/prepend/bundle.js",
    format: "esm",
    sourcemap: true,
    minify: false,
  },
  plugins: [
    {
      name: "prepend-log",
      renderChunk(code) {
        const edited = new MagicString(code);
        edited.prepend("console.log('추가한 코드');\n");

        return {
          code: edited.toString(),
        };
      },
    },
  ],
};

이렇게 한 뒤에 pnpm exec rolldown --config prepend.config.mjs를 실행시키면 이런 결과물이 나온다.

dist/prepend/bundle.js

javascript
console.log('추가한 코드');
//#region src/payment.js
function pay(amount) {
	console.log("결제 요청:", amount);
	if (amount <= 0) throw new Error("결제 금액은 0보다 커야 합니다.");
	console.log(`${amount}원 결제 완료`);
}

//#endregion
//#region src/main.js
pay(0);

//#endregion
//# sourceMappingURL=bundle.js.map

dist/prepend/bundle.js.map

json
{
  "version": 3,
  "file": "bundle.js",
  "names": [],
  "sources": [
    "../../src/payment.js",
    "../../src/main.js"
  ],
  "sourcesContent": [
    "export function pay(amount) {\n  console.log(\"결제 요청:\", amount);\n\n  if (amount <= 0) {\n    throw new Error(\"결제 금액은 0보다 커야 합니다.\");\n  }\n\n  console.log(`${amount}원 결제 완료`);\n}\n",
    "import { pay } from \"./payment.js\";\n\npay(0);\n"
  ],
  "mappings": ";AAAA,SAAgB,IAAI,QAAQ;CAC1B,QAAQ,IAAI,UAAU,MAAM;CAE5B,IAAI,UAAU,GACZ,MAAM,IAAI,MAAM,oBAAoB;CAGtC,QAAQ,IAAI,GAAG,OAAO,QAAQ;AAChC;;;;ACNA,IAAI,CAAC"
}

소스맵이 잘못된 위치를 가리킨다

이 상태로 node --enable-source-maps dist/prepend/bundle.js를 실행시키면 다음과 같은 결과가 나온다.

% node --enable-source-maps dist/prepend/bundle.js
추가한 코드
결제 요청: 0
/Users/seunghunlee/Desktop/workspace/study/bundler-basics/examples/source-map/src/payment.js:8
  console.log(`${amount}원 결제 완료`);
                         ^


Error: 결제 금액은 0보다 커야 합니다.
    at pay (/Users/seunghunlee/Desktop/workspace/study/bundler-basics/examples/source-map/src/payment.js:8:25)
    at <anonymous> (/Users/seunghunlee/Desktop/workspace/study/bundler-basics/examples/source-map/src/main.js:3:6)
    at ModuleJob.run (node:internal/modules/esm/module_job:371:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:683:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)

Node.js v24.7.0

에러 위치가 src/payment.js:8로 표시되는데, 실제 코드를 보면 throw는 5행에 있다.

이유는 간단하다. 플러그인에서 변경된 코드만 반환하고, prepend()로 코드의 위치가 바뀌었다는 정보를 반환하지 않았기 때문이다. 그래서 기존 매핑에 이 변화가 반영되지 않았다.

이 경우 Rolldown은 빌드할 때 SOURCEMAP_BROKEN 경고를 내지만, 번들과 소스맵 파일은 생성한다. 그리고 전에 썼던 글 마지막 내용처럼, 이번 Node 실행에서도 소스맵 불일치에 대한 별도 에러 없이 잘못된 위치가 표시된다.

코드 변경에 맞춰 소스맵 갱신하기

이걸 어떻게 해결할 수 있을까?

변경된 코드와 소스맵을 함께 반환하기

prepend.config.mjs

javascript
import MagicString from "magic-string";

export default {
  input: "src/main.js",
  output: {
    file: "dist/prepend/bundle.js",
    format: "esm",
    sourcemap: true,
    minify: false,
  },
  plugins: [
    {
      name: "prepend-log",
      renderChunk(code) {
        const edited = new MagicString(code);
        edited.prepend("console.log('추가한 코드');\n");

        return {
          code: edited.toString(),
          map: edited.generateMap({ hires: true }),
        };
      },
    },
  ],
};

간단하게 map: edited.generateMap({ hires: true })를 추가하면 된다.

생성한 소스맵은 어떻게 연결될까?

MagicString은 전달받은 번들 코드와 편집 내역을 기록한다. generateMap()은 추가 후 번들의 위치를 추가 전 번들의 위치에 연결하는 소스맵을 만든다. hires: true는 열 위치까지 정밀하게 매핑하도록 하는 옵션이다.

Rolldown은 플러그인이 반환한 이 맵을 기존 소스맵과 합쳐, 추가 후 번들에서 원본 코드의 위치까지 찾을 수 있게 한다. 공식 문서에서도 이 과정을 설명하고 있다.

추가 후 번들 → 추가 전 번들 → 원본 코드

다시 실행해 원본 위치 확인하기

다시 빌드한 뒤 실행시켜 보자.

bash
pnpm exec rolldown --config prepend.config.mjs
node --enable-source-maps dist/prepend/bundle.js

이번에는 에러 위치가 src/payment.js:5:11로 나온다. 코드만 반환했을 때는 8행을 가리켰지만, 변경에 대한 소스맵을 함께 반환하니 실제 throw가 있는 5행을 가리킨다.