AST와 코드 변환 — Babel 플러그인으로 이해하는 컴파일러 기초
AST(Abstract Syntax Tree)는 소스 코드를 트리 구조로 표현한 것입니다. Babel, ESLint, Prettier 같은 도구들이 모두 AST를 기반으로 동작합니다. AST를 이해하면 코드 변환 도구를 직접 만들 수도 있습니다.
AST란?
// 이 코드가
const x = 1 + 2;
// AST로 변환되면
{
type: "Program",
body: [{
type: "VariableDeclaration",
kind: "const",
declarations: [{
type: "VariableDeclarator",
id: { type: "Identifier", name: "x" },
init: {
type: "BinaryExpression",
operator: "+",
left: { type: "NumericLiteral", value: 1 },
right: { type: "NumericLiteral", value: 2 }
}
}]
}]
}
코드 변환 파이프라인
소스 코드 → 파싱(Parse) → AST → 변환(Transform) → 수정된 AST → 생성(Generate) → 변환된 코드
const { parse } = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
// 1. 파싱
const ast = parse("const x = 1 + 2;");
// 2. 변환 — 모든 숫자를 2배로
traverse(ast, {
NumericLiteral(path) {
path.node.value *= 2;
},
});
// 3. 생성
const output = generate(ast);
console.log(output.code); // "const x = 2 + 4;"
Babel 플러그인 만들기
console.log 자동 제거
// babel-plugin-remove-console.js
module.exports = function () {
return {
visitor: {
CallExpression(path) {
if (
path.get("callee").matchesPattern("console.log") ||
path.get("callee").matchesPattern("console.debug")
) {
path.remove();
}
},
},
};
};
화살표 함수 변환
// 화살표 함수 → 일반 함수
module.exports = function ({ types: t }) {
return {
visitor: {
ArrowFunctionExpression(path) {
const { params, body } = path.node;
// 본문이 표현식이면 return 문으로 감싸기
const funcBody = t.isBlockStatement(body)
? body
: t.blockStatement([t.returnStatement(body)]);
path.replaceWith(
t.functionExpression(null, params, funcBody)
);
},
},
};
};
// 입력: const add = (a, b) => a + b;
// 출력: const add = function(a, b) { return a + b; };
자동 에러 로깅
// 모든 catch 블록에 자동으로 에러 로깅 추가
module.exports = function ({ types: t }) {
return {
visitor: {
CatchClause(path) {
const param = path.node.param;
if (!param) return;
// 이미 로깅이 있는지 확인
const hasLogging = path.node.body.body.some(
(stmt) =>
t.isExpressionStatement(stmt) &&
t.isCallExpression(stmt.expression) &&
stmt.expression.callee?.property?.name === "error"
);
if (!hasLogging) {
const logStatement = t.expressionStatement(
t.callExpression(
t.memberExpression(t.identifier("console"), t.identifier("error")),
[t.stringLiteral("[Error]"), param]
)
);
path.node.body.body.unshift(logStatement);
}
},
},
};
};
AST Explorer 활용
https://astexplorer.net/
1. 왼쪽에 소스 코드 입력
2. 오른쪽에 AST 트리 확인
3. 파서를 "babel" 선택
4. Transform 탭에서 플러그인 코드 테스트
path API 주요 메서드
// path — AST 노드의 래퍼
path.node; // 현재 노드
path.parent; // 부모 노드
path.parentPath; // 부모 path
// 탐색
path.get("body"); // 자식 path
path.getSibling(0); // 형제 path
path.findParent((p) => p.isFunction()); // 조건에 맞는 부모 찾기
// 수정
path.replaceWith(newNode); // 노드 교체
path.remove(); // 노드 제거
path.insertBefore(node); // 앞에 삽입
path.insertAfter(node); // 뒤에 삽입
// 검사
path.isIdentifier(); // 타입 확인
path.isIdentifier({ name: "x" }); // 세부 조건
path.matchesPattern("console.log");
ESLint 규칙도 AST 기반
// no-console 규칙의 간소화 버전
module.exports = {
create(context) {
return {
MemberExpression(node) {
if (
node.object.name === "console" &&
node.property.name === "log"
) {
context.report({
node,
message: "console.log 사용 금지",
});
}
},
};
},
};
AST 기반 도구들
| 도구 | 용도 | AST 활용 |
|---|---|---|
| Babel | 트랜스파일링 | 코드 변환 |
| ESLint | 린팅 | 패턴 검출 |
| Prettier | 포매팅 | 코드 재생성 |
| TypeScript | 타입 체크 | 타입 분석 |
| webpack | 번들링 | 의존성 분석 |
| jscodeshift | 코드모드 | 대규모 리팩터링 |
**기억하기 **: AST는 코드를 트리로 표현한 것이며, Parse → Transform → Generate가 코드 변환의 3단계입니다. astexplorer.net에서 AST 구조를 탐색하면 Babel 플러그인이나 ESLint 규칙을 쉽게 작성할 수 있습니다.
댓글 로딩 중...