9.3.5 제네릭(Generics) 및 복잡한 타입 정의의 유효성 검사

9.3.5 제네릭(Generics) 및 복잡한 타입 정의의 유효성 검사

코드 생성 AI가 원시 타입(Primitive Type)이나 명시적인 클래스 이름을 매핑하는 데는 제법 능숙해졌지만, 현대 객체지향 및 함수형 프로그래밍의 꽃이라 불리는 제네릭(Generics) 영역에 진입하면 그 추론 능력은 급격하게 무너진다.

제네릭은 List<T>Map<K, V>처럼 타입 자체가 매개변수화(Parameterized)되어 런타임 이전(Compile-time)에 결정되는 고도의 추상화 기법이다. LLM은 이 ’T’가 무엇으로 치환되어야 하는가를 문맥 밖에서 유추해야 하므로, 복잡도가 올라갈수록 List<Any>로 회피해버리거나, 제약 조건(Type Bounds, 예: <T extends User>)을 어기고 전혀 무관한 객체를 집어넣는 식의 타입 환각(Type Hallucination)을 일으킨다.

오라클 미들웨어는 컴파일러 수준의 타입 엔진을 가동하여 제네릭에 박혀 있는 모호성과 제약 조건 위반을 0비트의 관용도 없이 파괴해야 한다.

1. 제네릭 타입 소거(Type Erasure) 이전 단계의 제약 조건 검증

자바(Java)나 타입스크립트(TypeScript) 같은 언어는 런타임에 제네릭 타입 정보를 지워버린다(Type Erasure). 이는 곧, 제네릭과 관련된 에러는 런타임(동적 테스트)에 돌입하면 잡을 수 없으며 오직 컴파일/정적 분석 단계에서만 색출이 가능하다는 것을 의미한다.

  • 오라클 파이프라인의 타입 엔진(예: tsc나 Java 컴파일러)은 LLM이 생성한 제네릭 함수를 검사할 때, **상한/하한 경계(Upper/Lower Bounds)**를 가장 먼저 스캔한다.
  • 프롬프트에서 "AbstractRepository<T extends Entity>를 구현하라"고 지시했을 때, LLM이 class UserRepository extends AbstractRepository<String>처럼 제네릭 인자 T 자리에 Entity를 상속하지 않은 원시 타입(String)을 꽂아 넣었다면 컴파일 타임 에러가 발생한다.
  • 오라클은 즉시 Bound Mismatch Error를 캡처하여, “String 타입은 Entity의 하위 타입이 아닙니다. 제약 조건을 충족하는 도메인 모델(예: UserEntity)을 제네릭 인자로 치환하여 코드를 재생성하십시오“라는 피드백을 타겟 모델로 쏜다.

2. 복합 유니온(Union) 및 교차(Intersection) 타입의 누락 탐지

TypeScript나 Python의 typing 모듈에서는 파라미터가 가지는 타입을 TypeA | TypeB (Union) 혹은 TypeA & TypeB (Intersection)와 같은 복합 수학적 집합으로 구성할 수 있다.

AI는 이렇게 갈래가 나뉘는 타입스크립트의 유니온 타입을 받았을 때, 코드를 단순화하기 위해 **하나의 타입만을 편식하여 처리(Type Narrowing Failure)**하는 치명적인 논리 결함을 자주 발생시킨다.

// 요구된 타입: input: string | number
// LLM의 잘못된 생성 결과
function processData(input: string | number) {
    // number 타입일 때의 대비책 없이 문자열 전용 메서드를 바로 호출 (오류)
    return input.toUpperCase(); 
}

정적 타입 오라클은 T = string | number라는 도메인 위에서 toUpperCase() 메서드가 number 객체에는 존재하지 않음을 수학적 교집합(Intersection Check)을 통해 증명해 낸다 (Property 'toUpperCase' does not exist on type 'number'). 오라클은 이러한 ’처리되지 않은 타입 분기’를 조기에 적발하고, AI에게 typeofisinstance()를 사용한 명시적인 타입 가드(Type Guard) 분기문을 작성할 것을 강제한다.

3. 재귀적(Recursive) 제네릭과 깊은 중첩(Deep Nesting) 전개의 제한

가장 복잡한 형태는 제네릭이 스스로의 타입을 참조하는 재귀적 제네릭(예: class Node<T extends Comparable<T>>)이나, Map<String, List<Tuple<Integer, String>>>처럼 끝없이 파고드는 깊은 중첩 타입이다.

이러한 깊은 트리 구조의 제네릭 앞에서 LLM의 Attention 매트릭스는 토큰의 순서를 잃어버리고 괄호의 짝을 틀리거나 인자의 개수를 누락하는 실수를 연발한다.

  • 오라클의 역할은 컴파일러 API가 뱉어내는 딥 제네릭 에러(Type Argument Count Mismatch)를 탐지하는 것이다.
  • 하지만 지나치게 복잡한 제네릭 에러를 LLM에게 그대로 피드백하면, LLM은 이를 수정하지 못하고 계속 헛도는 이른바 무한 할루시네이션(Infinite Hallucination) 상태에 빠질 위험이 크다.
  • 따라서 설계자는 오라클 파이프라인 단에서 정적 분석기의 “제네릭 중첩 깊이 한계(Max Nesting Depth)“를 설정해야 한다. LLM이 지나치게 복잡한 제네릭을 생성하여 에러를 연쇄 폭발시킬 경우, 오라클은 자가 수정(Self-correction) 루프를 도는 대신 생성을 중단명령(Abort)하고 “복잡도를 낮추기 위해 제네릭 구조를 단순한 인터페이스로 쪼개어(Flatten) 다시 작성하라“라는 상위 레벨의 아키텍처 피드백을 주입해야 한다.

제네릭은 코드의 재사용성을 극대화하는 강력한 마법이지만, 그만큼 깐깐한 수학적 증명을 요구한다. 정적 타입 오라클은 이 마법이 흑마술로 변질되어 시스템의 메모리 레이아웃을 망가뜨리는 것을 컴파일 타임에 완벽하게 봉쇄하는 최후의 견제 장치다.