9.2.2 Python의 ast 모듈과 Java의 JavaParser를 활용한 구문 분석 실습
각 프로그래밍 언어 생태계는 소스 코드를 AST로 파싱하고 조작할 수 있는 강력한 네이티브 라이브러리를 보유하고 있다. 오라클 시스템 설계자는 이러한 도구들을 CI/CD 파이프라인이나 검증 미들웨어에 밀착 결합시켜 LLM의 출력물을 런타임 이전에 엄격하게 해부해야 한다.
본 절에서는 동적 타입 언어의 대표 격인 Python과 정적 타입 엔터프라이즈 언어의 대표 격인 Java 환경에서, AI가 생성한 코드를 AST 오라클로 검증하는 실전 코드를 분석한다.
1. Python ast 내장 모듈을 활용한 1차원 검증망
Python은 표준 라이브러리로 CPython 컴파일러 엔진과 동일한 원리로 텍스트를 파싱하는 ast 모듈을 제공한다. 추가적인 종속성 설치 없이 즉각적으로 완벽한 Syntax 검증망을 구축할 수 있다는 점에서 오라클 파이프라인 구축에 대단히 유리하다.
import ast
def verify_python_code(llm_generated_code: str) -> bool:
try:
# LLM이 생성한 문자열 덩어리를 메모리 상의 AST 트리로 변환 시도
tree = ast.parse(llm_generated_code)
# 파싱 성공 (Syntax Error 없음)
print("오라클 검증 통과: 문법적으로 유효한 Python 코드입니다.")
# 팁: 트리 내부를 정밀하게 순회하며 특정 노드가 존재하는지 스캔할 수 있다.
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
print(f"발견된 임포트: {alias.name}")
return True
except SyntaxError as e:
# 괄호 누락, 들여쓰기 오류 등이 발생하면 ast.parse가 즉각 SyntaxError를 던짐
print(f"오라클 검증 실패: 무효한 구문 발견 - {e.msg}")
print(f"발생 위치: 라인 {e.lineno}, 오프셋 {e.offset}")
return False
# 고의로 괄호가 빠진 LLM 결과물 시뮬레이션
bad_code = """
def calculate_area(radius):
return 3.14 * (radius ** 2
"""
verify_python_code(bad_code)
이 고작 20줄 남짓한 코드는 LLM이 저지르는 파이썬 구문 오류의 99%를 결정론적으로 적발해낸다. 오라클은 try-except 블록에서 잡힌 SyntaxError의 구체적인 메시지(e.msg)와 라인 넘버(e.lineno)를 추출하여, 타겟 LLM 프롬프트에 “당신이 작성한 코드 3번째 줄에 닫는 괄호가 없습니다. 다시 작성하세요“라는 무결점 피드백 루프를 가동할 수 있다.
2. Java JavaParser를 활용한 엔터프라이즈 횡단 관심사 검증
Java는 극도로 엄격한 파일 및 클래스 구조를 요구한다. 단순히 괄호를 닫는 것을 넘어, 패키지(Package) 선언의 위치나 클래스 스코프의 정합성을 따지기 위해 com.github.javaparser 라이브러리를 오라클 코어로 도입한다.
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ParseProblemException;
public class JavaOracleVerifier {
public static boolean verifyJavaCode(String llmOutput) {
try {
// 소스 코드를 CompilationUnit (최상위 AST 노드)으로 컴파일 시도
CompilationUnit cu = StaticJavaParser.parse(llmOutput);
System.out.println("오라클 검증 통과: 유효한 Java 소스 파일입니다.");
// 검증 로직 심화: 모든 public 클래스의 이름을 추출
cu.findAll(com.github.javaparser.ast.body.ClassOrInterfaceDeclaration.class).forEach(c -> {
if(c.isPublic()) {
System.out.println("Public 클래스 발견: " + c.getNameAsString());
}
});
return true;
} catch (ParseProblemException e) {
System.err.println("오라클 검증 실패: Java 문법 위반.");
// ParseProblemException은 상세한 컴파일러 에러 체인을 들고 있다.
System.err.println("상세 로그: " + e.getProblems().get(0).getMessage());
return false;
}
}
}
Python의 ast.parse가 느슨한 스크립트 구문을 허용한다면, StaticJavaParser.parse는 엔터프라이즈 환경 특유의 강력한 문법 구속력을 코드가 우회하지 못하도록 철벽을 친다. 세미콜론 누락부터 잘못된 제네릭(Generic) 타입 선언의 양식까지 모든 에러가 ParseProblemException이라는 명시적인 예외 객체로 격발(Trigger)되며, 오라클 시스템은 이 예외를 캐치하여 데이터 파이프라인으로 승격시키는 게이트키퍼(Gatekeeper) 역할을 완벽하게 수행하게 된다.