๐Ÿ› ๏ธ Codemod ์‹ค์ „ ๊ฐ€์ด๋“œ - jscodeshift๋กœ ์ˆ˜๋ฐฑ ๊ฐœ ํŒŒ์ผ ๋ฆฌํŒฉํ† ๋ง ์ž๋™ํ™”ํ•˜๊ธฐ

โ€ข

Codemod๊ฐ€ ํ•„์š”ํ•œ ์ˆœ๊ฐ„

ํ”„๋กœ์ ํŠธ์—์„œ deprecated๋œ API๋ฅผ ์ƒˆ API๋กœ ๋ฐ”๊ฟ”์•ผ ํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฒช์–ด๋ณธ ์  ์žˆ๋‚˜์š”? ํŒŒ์ผ์ด 10๊ฐœ๋ผ๋ฉด ์ˆ˜์ž‘์—…์œผ๋กœ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ 100๊ฐœ, 1,000๊ฐœ ํŒŒ์ผ์ด๋ผ๋ฉด ์ด์•ผ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์ ธ์š”.

์ด ๊ธ€์—์„œ๋Š” jscodeshift๋ฅผ ์‚ฌ์šฉํ•ด codemod๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ๋Œ€๊ทœ๋ชจ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ๋‹ค๋ค„์š”. AST ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‹ค์ „ API ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ํ…Œ์ŠคํŠธ๊นŒ์ง€ ๋‹ค๋ฃจ๊ณ  ์žˆ์–ด์š”.

์ด ๊ธ€์€ JavaScript/TypeScript ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์„ ์ดํ•ดํ•˜๊ณ , ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ˜๋ณต์ ์ธ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์ž‘์—…์„ ์ž๋™ํ™”ํ•˜๊ณ  ์‹ถ์€ ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•ด์š”.

์ •๊ทœ์‹์˜ ํ•œ๊ณ„

๊ฐ„๋‹จํ•œ ์น˜ํ™˜์€ sed๋‚˜ IDE์˜ ์ฐพ๊ธฐ-๋ฐ”๊พธ๊ธฐ๋กœ ์ถฉ๋ถ„ํ•ด์š”. ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์ •๊ทœ์‹๋งŒ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์›Œ์š”.

  • ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์ธ์ž ์ˆœ์„œ๋ฅผ ๋ฐ”๊ฟ”์•ผ ํ•  ๋•Œ
  • ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ import๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ๋•Œ
  • ๋ณ€์ˆ˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ณ ๋ คํ•œ ์ด๋ฆ„ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•  ๋•Œ
  • JSX ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ด์•ผ ํ•  ๋•Œ

์ด๋Ÿฐ ์ž‘์—…์—๋Š” ์ฝ”๋“œ์˜ ๊ตฌ์กฐ์  ์˜๋ฏธ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•ด์š”. ๊ทธ๊ฒŒ ๋ฐ”๋กœ codemod์˜ˆ์š”.

AST์™€ jscodeshift ๊ธฐ์ดˆ

AST๋ž€

AST(Abstract Syntax Tree)๋Š” ์ฝ”๋“œ๋ฅผ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ํ‘œํ˜„ํ•œ ๊ฒƒ์ด์—์š”. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ์š”.

const greeting = "hello";

์ด ํ•œ ์ค„์€ AST์—์„œ VariableDeclaration โ†’ VariableDeclarator โ†’ StringLiteral ๊ฐ™์€ ๋…ธ๋“œ ํŠธ๋ฆฌ๋กœ ๋ณ€ํ™˜๋ผ์š”. codemod๋Š” ์ด ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ์ˆ˜์ •ํ•œ ๋’ค ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ด์š”.

AST Explorer

AST Explorer์—์„œ ์ฝ”๋“œ๋ฅผ ๋ถ™์—ฌ๋„ฃ์œผ๋ฉด AST ๊ตฌ์กฐ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”. Transform ์˜ต์…˜์—์„œ jscodeshift๋ฅผ ์„ ํƒํ•˜๋ฉด ๋ณ€ํ™˜ ๊ฒฐ๊ณผ๋„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

jscodeshift ์„ค์น˜

jscodeshift๋Š” Facebook์ด ๋งŒ๋“  codemod ์‹คํ–‰ ๋„๊ตฌ์˜ˆ์š”. ๋‚ด๋ถ€์ ์œผ๋กœ recast๋ฅผ ์‚ฌ์šฉํ•ด ์›๋ณธ ์ฝ”๋“œ์˜ ํฌ๋งคํŒ…์„ ์ตœ๋Œ€ํ•œ ๋ณด์กดํ•ด์š”.

# jscodeshift ์„ค์น˜
pnpm add -D jscodeshift @types/jscodeshift

transform ํ•จ์ˆ˜์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

๋ชจ๋“  codemod๋Š” ํ•˜๋‚˜์˜ transform ํ•จ์ˆ˜๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์–ด์š”.

// transforms/my-transform.ts
import type { API, FileInfo } from "jscodeshift";

export default function transformer(fileInfo: FileInfo, api: API) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  // AST ๋ณ€ํ™˜ ๋กœ์ง ์ž‘์„ฑ

  return root.toSource();
}

์„ธ ๊ฐ€์ง€ ํ•ต์‹ฌ ์š”์†Œ๊ฐ€ ์žˆ์–ด์š”.

  • fileInfo.source - ๋ณ€ํ™˜ ๋Œ€์ƒ ํŒŒ์ผ์˜ ์†Œ์Šค ์ฝ”๋“œ ๋ฌธ์ž์—ด
  • api.jscodeshift (๋ณดํ†ต j๋กœ ์ถ•์•ฝ) - AST ๋…ธ๋“œ๋ฅผ ์ฐพ๊ณ  ์ˆ˜์ •ํ•˜๋Š” API
  • root.toSource() - ์ˆ˜์ •๋œ AST๋ฅผ ๋‹ค์‹œ ์†Œ์Šค ์ฝ”๋“œ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜

์ฒซ ๋ฒˆ์งธ Codemod ์ž‘์„ฑํ•˜๊ธฐ

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊ฒŒ์š”. ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์—์„œ console.log๋ฅผ ์ œ๊ฑฐํ•˜๋Š” codemod๋ฅผ ๋งŒ๋“ค์–ด ๋ด์š”.

console.log ์ œ๊ฑฐ codemod

// transforms/remove-console-log.ts
import type { API, FileInfo } from "jscodeshift";

export default function transformer(fileInfo: FileInfo, api: API) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  // console.log ํ˜ธ์ถœ์„ ์ฐพ์•„์„œ ์ œ๊ฑฐ
  root
    .find(j.ExpressionStatement, {
      expression: {
        type: "CallExpression",
        callee: {
          object: { name: "console" },
          property: { name: "log" },
        },
      },
    })
    .remove();

  return root.toSource();
}

์ด ์ฝ”๋“œ์˜ ๋™์ž‘ ํ๋ฆ„์„ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”.

  1. j(fileInfo.source)๋กœ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ AST๋กœ ํŒŒ์‹ฑํ•ด์š”
  2. .find()๋กœ console.log() ํ˜ธ์ถœ์— ํ•ด๋‹นํ•˜๋Š” ๋…ธ๋“œ๋ฅผ ์ฐพ์•„์š”
  3. .remove()๋กœ ํ•ด๋‹น ๋…ธ๋“œ๋ฅผ ์ œ๊ฑฐํ•ด์š”
  4. .toSource()๋กœ ์ˆ˜์ •๋œ AST๋ฅผ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•ด์š”

์‹คํ–‰๊ณผ ๊ฒฐ๊ณผ ํ™•์ธ

--dry์™€ --print ์˜ต์…˜์œผ๋กœ ์‹ค์ œ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฏธ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

# ๋ณ€๊ฒฝ ์‚ฌํ•ญ ๋ฏธ๋ฆฌ ํ™•์ธ (ํŒŒ์ผ ์ˆ˜์ • ์—†์Œ)
npx jscodeshift -t transforms/remove-console-log.ts src/ --dry --print

# ์‹ค์ œ ์‹คํ–‰
npx jscodeshift -t transforms/remove-console-log.ts src/ --parser=tsx

์ฃผ์˜

codemod๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ๋ฐ˜๋“œ์‹œ Git์— ์ปค๋ฐ‹ํ•˜์„ธ์š”. --dry --print๋กœ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•œ ๋’ค, ์‹ค์ œ ์‹คํ–‰ ํ›„์—๋Š” git diff๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฒ€ํ† ํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ด์š”.

์‹ค์ „ Codemod - API ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” codemod ํŒจํ„ด์€ API ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด์—์š”. deprecated๋œ ํ•จ์ˆ˜๋ฅผ ์ƒˆ ํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋Š” ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.

์‹œ๋‚˜๋ฆฌ์˜ค

fetchData(url, callback) ํ˜•ํƒœ์˜ ์ฝœ๋ฐฑ ๊ธฐ๋ฐ˜ API๋ฅผ fetchData(url, options) ํ˜•ํƒœ์˜ ์˜ต์…˜ ๊ฐ์ฒด ํŒจํ„ด์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด์š”.

๋ณ€ํ™˜ ์ „

// ๊ธฐ์กด ์ฝ”๋“œ
fetchData("/api/users", (err, data) => {
  if (err) handleError(err);
  setUsers(data);
});

๋ณ€ํ™˜ ํ›„

// ๋ณ€ํ™˜๋œ ์ฝ”๋“œ
fetchData("/api/users", {
  onSuccess: (data) => {
    setUsers(data);
  },
  onError: (err) => {
    handleError(err);
  },
});

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ codemod

// transforms/migrate-fetch-data.ts
import type { API, FileInfo } from "jscodeshift";

export default function transformer(fileInfo: FileInfo, api: API) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  root
    .find(j.CallExpression, {
      callee: { name: "fetchData" },
    })
    .filter((path) => {
      // ๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์ธ ๊ฒฝ์šฐ๋งŒ ๋ณ€ํ™˜
      const args = path.node.arguments;
      return args.length === 2 && args[1].type === "ArrowFunctionExpression";
    })
    .replaceWith((path) => {
      const [urlArg, callbackArg] = path.node.arguments;

      if (callbackArg.type !== "ArrowFunctionExpression") return path.node;
      const params = callbackArg.params;
      const errParam = params[0];
      const dataParam = params[1];

      // ์ฝœ๋ฐฑ ๋ณธ๋ฌธ์—์„œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ์„ฑ๊ณต ๋กœ์ง ๋ถ„๋ฆฌ
      const body = callbackArg.body;
      if (body.type !== "BlockStatement") return path.node;

      const statements = body.body;
      const errorStatements = statements.filter(
        (stmt) =>
          j(stmt).toSource().includes(j(errParam).toSource()) &&
          !j(stmt).toSource().includes(j(dataParam).toSource()),
      );
      const successStatements = statements.filter(
        (stmt) => !errorStatements.includes(stmt),
      );

      // ์ƒˆ๋กœ์šด ์˜ต์…˜ ๊ฐ์ฒด ์ƒ์„ฑ
      const optionsObj = j.objectExpression([
        j.property(
          "init",
          j.identifier("onSuccess"),
          j.arrowFunctionExpression(
            dataParam ? [dataParam] : [],
            j.blockStatement(successStatements),
          ),
        ),
        j.property(
          "init",
          j.identifier("onError"),
          j.arrowFunctionExpression(
            errParam ? [errParam] : [],
            j.blockStatement(errorStatements),
          ),
        ),
      ]);

      return j.callExpression(j.identifier("fetchData"), [urlArg, optionsObj]);
    });

  return root.toSource();
}

์ด codemod๋Š” fetchData์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์ธ ๊ฒฝ์šฐ๋ฅผ ์ฐพ์•„ ์˜ต์…˜ ๊ฐ์ฒด ํŒจํ„ด์œผ๋กœ ๋ณ€ํ™˜ํ•ด์š”. .filter()๋กœ ๋ณ€ํ™˜ ๋Œ€์ƒ์„ ์ •ํ™•ํžˆ ์ขํžˆ๊ณ , .replaceWith()๋กœ ์ƒˆ๋กœ์šด AST ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํŒจํ„ด์ด์—์š”.

๋ณต์žกํ•œ codemod๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” AST Explorer์—์„œ ๋ณ€ํ™˜ ์ „/ํ›„ ์ฝ”๋“œ์˜ AST ๊ตฌ์กฐ๋ฅผ ๋น„๊ตํ•˜๋ฉด์„œ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ด์—์š”.

Codemod ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น…

codemod๋Š” ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋„๊ตฌ์ด๊ธฐ ๋•Œ๋ฌธ์—, ํ…Œ์ŠคํŠธ ์—†์ด ์‹คํ–‰ํ•˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์–ด์š”.

jscodeshift ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ

jscodeshift๋Š” Jest ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ด์š”. ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋งž์ถ”๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์š”.

transforms/
โ”œโ”€โ”€ remove-console-log.ts
โ”œโ”€โ”€ __tests__/
โ”‚   โ””โ”€โ”€ remove-console-log.test.ts
โ””โ”€โ”€ __testfixtures__/
    โ”œโ”€โ”€ remove-console-log.input.ts
    โ””โ”€โ”€ remove-console-log.output.ts
// transforms/__testfixtures__/remove-console-log.input.ts
const value = 42;
console.log("debug:", value);
doSomething(value);
// transforms/__testfixtures__/remove-console-log.output.ts
const value = 42;
doSomething(value);
// transforms/__tests__/remove-console-log.test.ts
import { defineTest } from "jscodeshift/src/testUtils";

defineTest(__dirname, "remove-console-log", null, "remove-console-log", {
  parser: "tsx",
});

defineTest๋Š” input ํŒŒ์ผ์„ transform์— ํ†ต๊ณผ์‹œํ‚จ ๊ฒฐ๊ณผ๊ฐ€ output ํŒŒ์ผ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ์ž๋™์œผ๋กœ ๊ฒ€์ฆํ•ด์š”.

์—ฃ์ง€ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ

์‹ค์ œ ์ฝ”๋“œ๋ฒ ์ด์Šค์—๋Š” ๋‹ค์–‘ํ•œ ํŒจํ„ด์ด ์กด์žฌํ•ด์š”. ๋Œ€ํ‘œ์ ์ธ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ๋ฏธ๋ฆฌ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”.

  • console.log๊ฐ€ ์•„๋‹Œ console.warn, console.error
  • ๋ณ€์ˆ˜์— ํ• ๋‹น๋œ const log = console.log ํŒจํ„ด
  • ์˜ต์…”๋„ ์ฒด์ด๋‹ console?.log() ํŒจํ„ด
  • ์ฃผ์„์ด ํฌํ•จ๋œ ์ฝ”๋“œ

--dry-run์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ํ™•์ธ

codemod ์‹คํ–‰ ์ „ ๋ฐ˜๋“œ์‹œ ์•ˆ์ „ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์น˜์„ธ์š”.

# 1๋‹จ๊ณ„. ๋ณ€๊ฒฝ๋  ํŒŒ์ผ ๋ชฉ๋ก ํ™•์ธ
npx jscodeshift -t transforms/remove-console-log.ts src/ --dry

# 2๋‹จ๊ณ„. ๋ณ€๊ฒฝ ๋‚ด์šฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
npx jscodeshift -t transforms/remove-console-log.ts src/ --dry --print

# 3๋‹จ๊ณ„. ์‹คํ–‰ (ํŒŒ์„œ ์ง€์ •)
npx jscodeshift -t transforms/remove-console-log.ts src/ --parser=tsx --extensions=ts,tsx

# 4๋‹จ๊ณ„. Git diff๋กœ ๊ฒฐ๊ณผ ๊ฒ€ํ† 
git diff

์œ ์šฉํ•œ CLI ์˜ต์…˜

--verbose=2๋กœ ์ƒ์„ธ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜๊ณ , --run-in-band๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ๋„๋ฉด ๋””๋ฒ„๊น…์ด ์‰ฌ์›Œ์ ธ์š”. --ignore-pattern=**/node_modules/**๋กœ ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ์„ ์ œ์™ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋งˆ๋ฌด๋ฆฌ

codemod๋ฅผ ํ™œ์šฉํ•œ ๋ฆฌํŒฉํ† ๋ง์˜ ํ•ต์‹ฌ์„ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”.

  • AST ๊ธฐ๋ฐ˜ ๋ณ€ํ™˜ - ์ •๊ทœ์‹๊ณผ ๋‹ฌ๋ฆฌ ์ฝ”๋“œ์˜ ๊ตฌ์กฐ์  ์˜๋ฏธ๋ฅผ ์ดํ•ดํ•˜๋ฏ€๋กœ ์•ˆ์ „ํ•œ ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•ด์š”
  • jscodeshift - find() โ†’ filter() โ†’ replaceWith() โ†’ toSource() ํŒจํ„ด์œผ๋กœ codemod๋ฅผ ์ž‘์„ฑํ•ด์š”
  • AST Explorer ํ™œ์šฉ - ๋ณ€ํ™˜ ์ „/ํ›„ ์ฝ”๋“œ์˜ AST ๊ตฌ์กฐ๋ฅผ ๋น„๊ตํ•˜๋ฉฐ ๊ฐœ๋ฐœํ•˜๋ฉด ํšจ์œจ์ ์ด์—์š”
  • ํ…Œ์ŠคํŠธ ํ•„์ˆ˜ - input/output fixture ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ๋กœ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ๊ฒ€์ฆํ•ด์š”
  • ์•ˆ์ „ํ•œ ์‹คํ–‰ - --dry --print๋กœ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๊ณ , Git diff๋กœ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€ํ† ํ•ด์š”

ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ˜๋ณต์ ์ธ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์ž‘์—…์ด ์žˆ๋‹ค๋ฉด codemod๋ฅผ ์ž‘์„ฑํ•ด๋ณด์„ธ์š”. ์ฒ˜์Œ์—๋Š” AST ๊ตฌ์กฐ๊ฐ€ ๋‚ฏ์„ค ์ˆ˜ ์žˆ์ง€๋งŒ, AST Explorer์™€ ํ•จ๊ป˜ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค์–ด๋ณด๋ฉด ๊ธˆ๋ฐฉ ์ต์ˆ™ํ•ด์ ธ์š”.

์ฐธ๊ณ  ์ž๋ฃŒ

์™ธ๋ถ€ ๋งํฌ