9.10.2 [JavaScript/TypeScript] TS Compiler API를 활용한 실시간 검증 서버
JavaScript(JS) 코드는 본질적으로 브라우저나 Node.js 런타임에서 실행되기 전까지는 참과 거짓을 완벽히 증명할 수 없는 자유로운 환경을 가진다. 그러나 TypeScript(TS)의 등장으로 JS 생태계에도 강력한 정적 타입 시스템(Static Type System)이라는 결정론적 오라클의 기반이 마련되었다.
특히 AI 애플리케이션 개발에 있어, 단순히 CLI에서 tsc 명령어를 실행하는 것을 넘어, **Typescript Compiler API를 미들웨어로 직접 활용하는 ‘실시간 검증 서버(Real-time Validation Server)’**를 구축하면 생성된 코드를 디스크에 저장(I/O)하지 않고도 메모리 상에서 초고속으로 검증과 피드백을 수행할 수 있다.
1. in-memory 가상 파일 시스템(Virtual File System) 체계
LLM이 코드를 생성할 때마다 이를 물리적인 하드 디스크에 파일(.ts)로 저장하고 CLI로 tsc를 호출하는 방식은 파일 I/O 병목으로 인해 시스템 레이턴시(Latency)를 치명적으로 떨어뜨린다.
대신 ts-morph 라이브러리나 순수 Typescript Compiler API를 사용하면, 서버의 RAM 공간 안에 **가상의 프로젝트(Virtual Project)**를 띄울 수 있다.
오라클은 AI가 생성한 문자열(String) 형태의 코드를 메모리 내의 가상 소스 파일(Virtual SourceFile)로 취급하여 즉시 컴파일러 워크스페이스에 편입시킨다. 이렇게 하면 디스크 쓰기 시간 없이 약 10~50ms 이내에 전체 코드의 문법 트리(AST)와 타입 체크(Type Checking) 결과를 뽑아낼 수 있다.
2. API를 통한 정밀도 높은 진단(Diagnostics) 추출
Compiler API의 핵심은 getPreEmitDiagnostics() 메서드다. 이 API는 단순히 “컴파일 실패“라는 텍스트를 던지는 것이 아니라, 실패의 원인을 정확히 가리키는 고도의 구조화된 **진단 객체(Diagnostic Object)**를 반환한다.
import * as ts from "typescript";
function validateAICode(codeString: string): DiagnosticReport {
// 1. 가상 컴파일러 설정 (Virtual Compiler Host)
const options: ts.CompilerOptions = { strict: true, target: ts.ScriptTarget.ES2022 };
const sourceFile = ts.createSourceFile("ai_temp.ts", codeString, ts.ScriptTarget.ES2022, true);
// 2. 컴파일 프로그램 생성 및 진단(에러) 추출
const program = // ... (Virtual Host Binding Logic)
const diagnostics = ts.getPreEmitDiagnostics(program);
if (diagnostics.length > 0) {
// 3. 에러를 JSON으로 파싱하여 LLM 피드백용으로 조립
const errors = diagnostics.map(diag => {
const { line, character } = diag.file!.getLineAndCharacterOfPosition(diag.start!);
return {
line: line + 1,
character: character + 1,
message: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
code: diag.code // e.g., TS2322
};
});
return { isSuccess: false, defects: errors };
}
return { isSuccess: true };
}
이 방식은 정규표현식(Regex)으로 로그를 어렵게 파싱할 필요 없이 완벽한 JSON 형식의 에러 리포트(line, character, message, error_code)를 내어준다. 이는 오라클 피드백 루프를 위한 최상의 원자재가 된다.
3. ESLint Node API를 결합한 컨벤션 방어
Typescript API가 언어의 골조(Type & Syntax)를 검증했다면, 이제 ESLint의 강력한 룰셋을 적용할 차례다. 여기서도 CLI 명령어가 아닌 **ESLint Node.js API**를 사용한다.
const { ESLint } = require("eslint");
async function lintAICode(codeString) {
const eslint = new ESLint({
useEslintrc: true, // 사내 공통 규칙 (.eslintrc) 적용
fix: false
});
// 메모리에 있는 코드를 직접 스캔
const results = await eslint.lintText(codeString, { filePath: "virtual_file.ts" });
if (results[0].errorCount > 0) {
// LLM에게 전달할 포맷으로 파싱
return extractLintErrors(results[0].messages);
}
}
사내 규칙에 설정된 no-console, max-depth(중첩 깊이 제한), @typescript-eslint/no-explicit-any(Any 타입 사용 금지) 등의 컨벤션을 AI가 어겼을 경우, 컴파일은 통과했더라도 ESLint API가 이를 즉각 캐치하여 REJECT 시킨다.
TypeScript와 JavaScript 생태계에서의 진정한 AI 오라클 파이프라인은, 이처럼 언어 자체의 인터페이스(Compiler API)와 분석 도구(ESLint API)를 서버 사이드 코드로 끌어안아 **‘밀리초 단위의 인메모리 피드백 시스템’**을 구축하는 것에 그 성패가 달려 있다.