13.3.5 Nullable 필드와 필수 필드의 엄격한 구분 전략

13.3.5 Nullable 필드와 필수 필드의 엄격한 구분 전략

초거대 언어 모델(LLM)을 엔터프라이즈 파이프라인에 이식하여 비정형 문서에서 수십 개의 수치 필드(Field)를 한 번에 병렬 파싱(Parsing)할 때, 경험이 부족한 주니어 설계자들이 가장 흔하게 저지르는 거대한 시스템 아키텍처 실수는 바로 딕셔너리 페이로드의 키(Key) 자체가 송두리째 누락되거나 구조적 값이 무책임하게 null로 채워져 들어오는 현상을 방치하는 것이다.

만약 우리 백엔드 시스템이 RDBMS 장부 기록에 ‘무슨 일이 있어도 반드시 값이 채워져야만 하는’ **필수 필드(Required Field)**와, 현실 세계의 이종 문서 다형성 상황에 따라 ‘값이 없으면 없는 대로 묵인되는’ **Nullable 필드(Optional Field)**를 스키마 컴파일 레벨에서 수학적으로 엄격하게 구분하여 설계해놓지 않는다면 어떻게 될까? 이는 런타임 백엔드 로직 곳곳에 언제 터질지 모르는 파괴적인 NullPointerException(NPE) 스파게티 지뢰밭을 매설해 놓는 것과 같다.

1. Pydantic의 엘립시스(…)와 Optional을 이용한 결정론적 강제

파이프라인 통과 여부를 결정하는 1단계 검문소인 Pydantic 모델을 정의할 때, 오라클 설계자는 스키마를 구성하는 단 하나의 속성(Property)에 대해서조차 타협 없이 명시적으로 이렇게 자문하고 코드로 선언해야만 한다.
“LLM 에이전트가 텍스트에서 이 데이터를 찾는 데 실패해서 빈칸을 가져왔을 경우, 나는 이 파이프라인의 숨통을 끊어버릴 것인가(Exception), 아니면 관대하게 빈 채로 통과시킬 것인가(None/Default)?”

from pydantic import BaseModel, Field
from typing import Optional

class FinancialTransactionOracle(BaseModel):
    # [1] 절대 불허 타격점: 필수 필드 (Required Field)
    # Python의 엘립시스(...) 파라미터는 "값이 없으면 자비 없이 즉각 ValidationError 격발"을 의미한다.
    total_amount: float = Field(
        ..., 
        description="총 결제 청구 금액. 이 필드는 절대로 누락될 수 없다."
    )

    # [2] 선택적 묵인 허용 구역: Nullable 필드 (Optional Field)
    # typing.Optional 타입 힌트와 기본값 None을 명시적으로 선언하여, 
    # LLM이 텍스트에서 진짜 해당 값을 찾지 못해 null을 보내오면 시스템이 의도적으로 묵인(Bypass)한다.
    discount_rate: Optional[float] = Field(
        None, 
        description="적용된 할인율. 조건부 속성이므로 없으면 안전하게 null 허용."
    )
    
    # [3] 디폴트 앵커 주입 전략: 기본값 할당 (Default Value)
    # 영수증처럼 세금 필드가 아예 안 적힌 문서의 경우, 파이프라인 하위에서 None 타입 연산 에러가 
    # 터지는 것을 막기 위해 에러 격발 대신 우리가 약속한 안전한 '0.0' 실수 값을 강제 주입하여 보호한다.
    tax_amount: float = Field(
        0.0, 
        description="부가세액. 만약 원문 문서에 명시되어 있지 않으면 시스템 기본값 0.0 주입"
    )

2. LLM의 가장 악랄한 환각: ‘비겁한 JSON Key 생략(Omission)’ 방어

LLM의 강제 구조화(Structured Outputs) 환경에서도 종종 일어나는 가장 악랄하고 비겁한 시스템 환각 중 하나는, 모델 자신이 원문 기계독해(MRC) 과정에서 해당 타겟 값을 찾지 못해 혼란스러울 때 아예 JSON 구조에서 해당 Key 자체를 슬그머니 제거(Omission)한 채 깡통을 반환해 버리는 현상이다.

만약 오라클 스키마에 필수 필드(...)로 선언된 마스터키인 vendor_name 문자열이 LLM의 출력 덤프에서 아예 증발해 버린 채 들어온다면, Pydantic 파싱 엔진은 런타임 객체 생성 시점에 단 1밀리초(ms)의 망설임도 없이 ValidationError: 1 validation error for FinancialTransactionOracle -> vendor_name: Field required 예외(Exception)를 격발 시키며 해당 트랜잭션을 찢어버린다.

이것은 파이프라인 아키텍처 관점에서 매우 훌륭하고 의도적인 기계적 패닉(Panic)이다.
이를 통해 우리는 “어리석은 LLM아, 나는 네 능력이 부족해 원문에서 값을 찾지 못했다고 해서 내 데이터베이스 스키마의 뼈대(Key 구조) 자체를 네 마음대로 파괴하는 것을 결코 허락하지 않는다. 네가 필수 값을 찾을 때까지 이 에러를 피드백으로 맞고 문서를 백 번이라도 다시 정독해라!” 라는 가장 강력한 방어적 프로그래밍(Defensive Programming) 메시지를 1차적으로 날릴 수 있게 되는 것이다.

결론적으로, 1단계 오라클에서 필수 필드와 Nullable 필드를 명시적으로 분리하고 허용하는 설계 전략은, 우리가 오라클을 그저 무식하게 융통성 없이 꽁꽁 잠그는 것이 목적이 아니다.
“우리의 시스템 인프라를 위해 어떤 필드에서는 예외를 격발 시켜 트랜잭션을 때려 부수고, 또 어떤 필드에서는 유연한 여백(None)을 주고 부드럽게 통과(Pass) 시킬 것인가?” 에 대한 거시적이고 결정론적인 시스템 통제 권한을, AI의 손에서 빼앗아 온전히 ’파이프라인 설계자인 인간’의 손으로 가져오기 위한 궁극적인 통제권 확립 목적이 그 핵심에 존재한다.