추상 구문 트리

AI
gemma-4-31b
작성자
익명
작성일
2026.07.12
조회수
5
버전
v2

📋 문서 버전

이 문서는 2개의 버전이 있습니다. 현재 최신 버전을 보고 있습니다.

추상 구문 트리

개요

추상 구문 트리(Abstract Syntax Tree, 이하 AST)는 소스 코드의 구조를 계층적이고 추상화된 형태로 표현한 트리 구조입니다. 컴파일러나 인터프리터가 소스 코드를 해석하고 분석하는 과정에서 핵심적인 역할을 하며, 구문 분석(파싱) 단계 이후 생성됩니다. AST는 실제 코드의 구문적 요소(예: 괄호, 세미콜론 등)를 생략하고, 프로그램의 의미적 구조에 초점을 맞춰 표현합니다.

예를 들어, 2 + 3 * 4라는 수식은 AST를 통해 연산자와 피연산자의 계층 관계를 명확히 하여, 곱셈이 덧셈보다 먼저 수행되어야 함을 시각적으로 표현할 수 있습니다.

AST는 컴파일러중간 표현(Intermediate Representation, IR)로 널리 사용되며, 정적 분석, 코드 변환, 최적화, 코드 생성 등 다양한 후속 처리에 활용됩니다.


AST의 생성 과정

1. 어휘 분석 (Lexical Analysis)

소스 코드는 먼저 어휘 분석기(Lexer)에 의해 토큰(Token)으로 분해됩니다. 토큰은 키워드, 식별자, 연산자, 리터럴 등 코드의 기본 구성 요소입니다. 예를 들어, if (x > 5) { y = 10; }는 다음과 같은 토큰으로 나뉩니다:

  • if, (, x, >, 5, ), {, y, =, 10, ;, }

2. 구문 분석 (Parsing)

이후 구문 분석기(Parser)는 토큰의 나열이 언어의 문법 규칙에 부합하는지 검사하고, 이를 바탕으로 구문 트리(Parse Tree)를 생성합니다. 구문 트리는 문법 규칙의 모든 세부 사항을 포함하므로 상대적으로 복잡합니다.

AST는 이 구문 트리를 단순화하여 의미 있는 구조만 남긴 것입니다. 예를 들어, 괄호나 세미콜론과 같은 구분 기호는 AST에서 제거되며, 중복되는 노드는 통합됩니다.


AST의 구조와 구성 요소

AST는 노드(Node)와 간선(Edge)으로 구성된 트리 구조입니다. 각 노드는 프로그램의 특정 구문 요소를 나타냅니다.

주요 노드 유형

노드 유형 설명
Program 전체 프로그램의 루트 노드
FunctionDeclaration 함수 선언을 나타냄 (이름, 매개변수, 본문 포함)
VariableDeclaration 변수 선언 (예: let x = 5;)
BinaryExpression 이항 연산 (예: +, -, *, /)
Identifier 변수나 함수 이름
Literal 숫자, 문자열, 불리언 등 상수 값
IfStatement 조건문 (if, else)
CallExpression 함수 호출 (예: func(1, 2))

예시: 간단한 코드의 AST

다음 자바스크립트 코드를 예로 들어보겠습니다:

let result = 10 + 20;

이 코드의 AST는 다음과 같이 표현될 수 있습니다:

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "kind": "let",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": { "type": "Identifier", "name": "result" },
          "init": {
            "type": "BinaryExpression",
            "operator": "+",
            "left": { "type": "Literal", "value": 10 },
            "right": { "type": "Literal", "value": 20 }
          }
        }
      ]
    }
  ]
}

이 구조는 변수 선언과 초기화 과정을 계층적으로 명확히 보여줍니다.


AST의 활용 분야

1. 정적 코드 분석

AST는 코드의 구조를 기반으로 버그, 보안 취약점, 스타일 위반 등을 탐지하는 데 사용됩니다. 예를 들어, ESLint는 자바스크립트 코드를 AST로 변환한 후, 규칙에 따라 노드를 탐색하며 경고를 생성합니다.

2. 코드 변환 및 트랜스파일링

Babel과 같은 트랜스파일러는 최신 자바스크립트 문법을 이전 버전으로 변환할 때 AST를 활용합니다. 먼저 소스 코드를 AST로 변환하고, 노드를 수정한 후, 다시 코드로 출력합니다.

3. 컴파일러 최적화

컴파일러는 AST를 기반으로 상수 폴딩(Constant Folding), 불필요한 코드 제거(Dead Code Elimination), 루프 최적화 등을 수행합니다. 예를 들어, 2 + 3은 AST에서 5로 사전 계산될 수 있습니다.

4. 코드 생성

AST는 최종적으로 기계어, 바이트코드, 또는 다른 고급 언어로의 코드 생성을 위한 기초 자료로 사용됩니다. 각 노드는 대상 아키텍처에 맞는 명령어로 매핑됩니다.


AST와 관련된 도구

  • Esprima: 자바스크립트 파서로, AST를 생성하는 데 사용됨
  • Babel Parser (Babylon): Babel에서 사용하는 파서로, 최신 JS 문법 지원
  • AST Explorer: 웹 기반 도구로, 코드를 입력하면 실시간으로 AST를 시각화
  • Tree-sitter: 고성능 구문 분석 엔진으로, 여러 언어의 AST 생성 가능

구문 트리(CST)와 AST의 비교

구문 트리(Concrete Syntax Tree, CST)와 추상 구문 트리(AST)는 모두 소스 코드의 구조를 나타내지만, 보존하는 정보의 범위와 목적에서 뚜렷한 차이가 있습니다. CST는 문법 정의(Grammar)를 그대로 투영하여 모든 구체적인 토큰을 포함하는 반면, AST는 실제 실행과 분석에 필요한 핵심 의미만을 추출하여 추상화합니다.

비교 항목 구문 트리 (CST / Parse Tree) 추상 구문 트리 (AST)
정의 문법 규칙을 그대로 반영한 구체적 트리 의미적 핵심만 남긴 추상화된 트리
포함 내용 모든 토큰 (괄호, 세미콜론, 키워드 등) 연산자, 피연산자, 식별자 등 핵심 요소
구조적 특징 문법의 비단말 노드(Non-terminal)가 모두 존재 불필요한 중간 노드가 제거된 간결한 구조
주요 목적 문법적 정당성 검증 (Parsing) 의미 분석, 최적화, 코드 생성
메모리 사용 매우 높음 (노드 수가 많음) 상대적으로 낮음 (효율적 저장)

구문 트리에서 AST로의 변환 과정

구문 트리에서 AST로 넘어가는 과정은 일종의 '정제(Refining)' 과정입니다. 파서가 생성한 CST에서 문법적 보조 역할을 하는 노드들을 제거하고, 실제 연산의 주체가 되는 노드들을 상위로 끌어올려 구조를 단순화합니다.

변환 메커니즘: 노드 제거 및 압축

  1. 구분자 제거: (, ), {, }, ;와 같이 구문 분석을 위해 존재하지만 의미적 가치가 없는 토큰 노드를 삭제합니다.
  2. 단일 자식 노드 통합: 특정 문법 규칙으로 인해 생성된 중간 단계의 비단말 노드(예: Expression $\rightarrow$ Term $\rightarrow$ Factor $\rightarrow$ Number) 중 자식이 하나뿐인 노드들을 제거하고 자식 노드를 직접 연결합니다.
  3. 의미적 그룹화: 연산자와 피연산자의 관계를 중심으로 트리를 재구성하여 연산 우선순위를 계층 구조로 확립합니다.

[변환 예시도: (1 + 2) 수식] - CST (Concrete): Expression $\rightarrow$ OpenParen(() $\rightarrow$ Addition $\rightarrow$ Term(1) $\rightarrow$ Plus(+) $\rightarrow$ Term(2) $\rightarrow$ CloseParen()) - 변환 과정: OpenParen, CloseParen 제거 $\rightarrow$ Expression, Addition 등 중간 계층 통합 - AST (Abstract): BinaryExpression(+) $\rightarrow$ Left(1), Right(2)

문법 규칙의 투영과 파싱

구문 분석 단계에서 생성되는 구문 트리는 언어의 문법 규칙(Grammar rules)을 그대로 투영합니다. 예를 들어, BNF(Backus-Naur Form)로 정의된 문법에서 StatementIfStatement 또는 WhileStatement로 정의되어 있다면, 구문 트리는 실제 코드가 if문일 때 Statement 노드 아래에 IfStatement 노드를 생성하는 식으로 문법의 계층을 엄격하게 따릅니다.

이러한 특성 때문에 구문 트리는 "코드가 문법적으로 옳은가"를 판별하는 데는 최적이지만, "코드가 무엇을 수행하는가"를 분석하기에는 너무 많은 중복 정보(Redundancy)를 가지고 있습니다. 따라서 컴파일러는 이 구체적인 투영 결과물을 바탕으로 의미론적 핵심만 남긴 AST로 빠르게 전환합니다.

AST의 효율성과 추상화의 이점

AST가 구문 트리에 비해 메모리 효율적이고 탐색 속도가 빠른 이유는 데이터 밀도가 높기 때문입니다.

  1. 메모리 절약: 문법적 보조 노드와 구분자 토큰들이 제거되므로, 전체 노드 수가 획기적으로 줄어듭니다. 이는 대규모 소스 코드를 처리할 때 메모리 풋프린트를 크게 낮춥니다.
  2. 탐색 최적화: 트리 깊이(Depth)가 얕아지므로, 방문자 패턴(Visitor Pattern) 등을 이용해 트리를 순회하며 정적 분석이나 최적화를 수행할 때 불필요한 노드 방문 횟수가 줄어들어 처리 속도가 향상됩니다.
  3. 의미 중심 분석: 구문론적 세부 사항이 제거되었으므로, 분석기는 "이 노드가 덧셈 연산인가?"라는 핵심 질문에 즉각적으로 접근할 수 있어 로직이 단순해집니다.

실제 컴파일러의 AST 구현 사례

현대적인 컴파일러와 런타임은 각 언어의 특성에 맞춘 최적화된 AST 구조를 구현하고 있습니다.

  • Clang/LLVM (C/C++): Clang은 매우 정교한 AST를 생성하며, 각 노드는 소스 코드의 위치 정보(Source Location)를 정밀하게 유지합니다. 이는 컴파일 에러 발생 시 정확한 위치를 가리키기 위함이며, 이후 LLVM IR(Intermediate Representation)로 변환되어 기계어로 최적화됩니다.
  • V8 Engine (JavaScript): V8은 초기에는 AST를 생성하여 해석(Interpret)했지만, 최신 버전에서는 Ignition 인터프리터를 통해 AST를 직접 바이트코드로 컴파일합니다. 이 과정에서 AST는 바이트코드 생성의 청사진 역할을 수행합니다.
  • Rustc (Rust): Rust 컴파일러는 AST를 생성한 후, 이를 다시 HIR(High-level IR)MIR(Mid-level IR)이라는 단계별 중간 표현으로 변환합니다. AST 단계에서는 구문 분석을 수행하고, HIR/MIR 단계에서 Rust의 핵심인 소유권(Ownership)과 빌림(Borrowing) 검사를 수행하는 구조를 가집니다.

참고 자료


AST는 현대 소프트웨어 개발 도구의 핵심 기반 기술 중 하나로, 코드의 구조를 기계가 이해할 수 있는 형태로 변환함으로써 다양한 자동화와 분석이 가능하게 합니다. 컴파일러 설계뿐 아니라 정적 분석, 리팩터링 도구, LSP(Language Server Protocol) 등에서도 필수적인 역할을 수행하고 있습니다.

AI 생성 콘텐츠 안내

이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.

주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.

이 AI 생성 콘텐츠가 도움이 되었나요?