불리언 함정(The Boolean Trap): AI가 문자 그대로 "False"라고 말할 수 없을 때
요약
Claude를 이용한 폼 생성 과정에서 발생한 '불리언 함정' 문제를 다룹니다. TypeScript의 boolean 타입과 CLI 플래그 관례가 결합되어, 'optional'을 의도한 undefined 값이 실제로는 필수 필드로 처리되는 디버깅 사례를 설명합니다.
핵심 포인트
- Claude가 선택 사항 필드 생성 시 required 파라미터를 생략하여 발생한 문제
- undefined 값이 CLI 명령 생성 시 플래그 자체를 누락시키는 현상
- MCP 스키마의 z.boolean().optional() 정의와 실제 동작 간의 괴리
- normBool() 함수와 undefined 체크를 통한 해결 방법 제시
이 문제를 디버깅하는 데 두 시간이 걸렸습니다. 모든 필드가 "필수(required)"로 표시된 폼을 두 시간 동안 쳐다보았습니다. 심지어 제가 Claude에게 명시적으로 선택 사항(optional)으로 만들라고 말한 필드들조차 말이죠. AI는 제 말을 무시한 것이 아니었습니다. 제가 요청한 것을 물리적으로 표현할 수 없었던 것입니다.
TypeScript의 불리언 (boolean) 타입과 commander.js의 플래그 관례가 어떻게 협력하여 false를 불가능한 값으로 만들었는지 설명하겠습니다.
업데이트 (v0.2.0):
field_add도구는 이제formlm_exec(직접 명령 실행)를 통해 액세스됩니다. 불리언 함정과 그 해결책(normBool()+!== undefined체크)은 여전히 완전히 적용 가능합니다.formlm_generate스마트 파이프라인은 서버 측의 AssessAgent가 필드 생성을 처리하도록 하여 이 문제를 완전히 우회합니다.
설정 (The Setup)
저는 고객 피드백 폼을 만들고 있었습니다. 대부분의 필드는 필수(이름, 이메일, 평점)였지만, 몇 가지는 선택 사항이었습니다. "추가 의견"을 위한 텍스트 필드와 "저희를 어떻게 알게 되셨나요"를 위한 라디오 버튼이 그것입니다. 표준적인 내용이었습니다.
저는 Claude에게 다음과 같이 말했습니다:
"피드백 폼을 생성해줘. 이름과 이메일은 필수야. 의견 필드는 선택 사항이야. '저희를 어떻게 알게 되셨나요' 필드는 선택 사항이야."
Claude가 작업을 시작했습니다. 앱을 생성하고, 이름과 이메일 필드에 required: true를 추가한 다음... 이런 일이 발생했습니다.
Claude가 한 일
필수 필드의 경우, Claude는 required: true와 함께 field_add를 호출했습니다. 깔끔하고 정확했습니다:
{
"appId": "app_abc123",
"id": "email",
...
선택 사항 필드의 경우, Claude는 field_add를 호출하면서 단순히... required 파라미터를 포함하지 않았습니다:
{
"appId": "app_abc123",
"id": "comments",
...
겉보기에는 문제가 없어 보입니다. 필드가 생성되었고, 폼이 렌더링되었습니다. 하지만 "선택 사항" 필드를 포함한 모든 필드에 빨간색 별표가 표시되었습니다. 모든 필드가 필수였습니다.
근본 원인 (The Root Cause)
문제는 이것입니다. MCP 스키마에서 required는 다음과 같이 정의되어 있습니다:
required: z.boolean().optional().describe('Mark as required'),
z.boolean().optional()은 해당 파라미터가 true, false, 또는 undefined(제공되지 않음)를 허용한다는 의미입니다. Claude가 특정 필드를 선택 사항(optional)으로 만들고 싶을 때, 두 가지 옵션이 있습니다:
required: false를 전달 — 명시적으로false로 설정required를 아예 전달하지 않음 —undefined상태로 유지
Claude는 2번 옵션을 선택했습니다. 그리고 바로 여기서 문제가 시작됩니다.
required가 undefined일 때, CLI는 다음과 같이 명령 문자열을 생성합니다:
const required = normBool(opts.required);
if (required !== undefined) cmd += ` --required=${required}`;
만약 required가 undefined라면, --required 플래그는 명령에 전혀 추가되지 않습니다. 서버는 --required 파라미터가 없는 상태로 assess form add 명령을 받게 됩니다.
그렇다면 서버의 기본 동작은 무엇일까요? --required 플래그가 존재하지 않으면, 서버는 기본값을 true로 설정합니다.
결국 "필수 사항이라고 말하지 않았다"는 것이 "기본적으로 필수 사항"이 되어버렸습니다. 모든 선택적(optional) 필드가 소리 소문 없이 필수(mandatory) 필드로 변해버린 것입니다.
Claude가 false를 전달하지 않은 이유
제가 직접 테스트해 보았습니다. 저는 Claude에게 명시적으로 다음과 같이 지시했습니다: "comments 필드를 필수 사항이 아니게(NOT required) 만드세요. required를 false로 설정하세요."
Claude는 시도했습니다. MCP 파라미터에 required: false를 전달했습니다. 하지만 CLI 측에서는 다음과 같은 일이 벌어졌습니다:
field.ts에 있는 normBool 함수는 세 가지 케이스를 처리합니다:
function normBool(v: unknown): boolean | undefined {
if (v === undefined) return undefined;
if (typeof v === 'boolean') return v;
...
MCP 서버가 required: false(올바른 불리언 값)를 받으면, 이를 그대로 통과시켜 다음과 같이 명령을 생성합니다:
assess form add --app <appId> --id comments --required=false --json
이 방식은 정상적으로 작동해야 합니다. 실제로 제 로컬 테스트 환경에서는 잘 작동했습니다. 하지만 Claude Desktop에서는 더 이상한 일이 발생했습니다. Claude가 false를 전달하는 대신 계속해서 파라미터를 누락시키는 것이었습니다. 대화 로그를 확인해 보니, Claude의 추론은 다음과 같았습니다:
"필드가 선택 사항(optional)이므로,
required를 설정할 필요가 없다."
Claude는 "optional(선택 사항)"을 "플래그를 false로 설정하라"가 아니라 "플래그를 설정하지 마라"로 취급하고 있었습니다. 인간의 관점에서는 이것이 말이 됩니다. 무언가가 선택 사항이라면, 굳이 언급하지 않으니까요. 하지만 프로그래밍적 관점에서는, 기본값이 true일 때 "설정하지 않는 것"과 "false로 설정하는 것"은 서로 다른 연산입니다.
진짜 문제: 뒤통수를 치는 기본값 (Defaults That Bite)
더 깊은 문제는 서버 측의 기본값(default)에 있습니다. --required 없이 field_add가 호출되면, 서버는 이를 required=true로 처리합니다. 이는 폼 필드(form fields)에 있어 합리적인 기본값입니다. 대부분의 폼에서 대부분의 필드는 필수(required)이기 때문입니다.
하지만 이것은 함정을 만듭니다. 필드를 선택 사항으로 만드는 유일한 방법은 명시적으로 required=false를 전달하는 것뿐입니다. 그리고 AI 에이전트들은 불리언(boolean) 파라미터에 대해 명시적으로 false를 전달하는 데 서툽니다. 그 이유는 다음과 같습니다:
시도 3: 스키마 설명을 수정하다. 제가 이렇게 했습니다. .describe() 문자열을 기본값에 대해 명확하게 변경했습니다:
required: z.boolean().optional().describe('필수 여부를 표시합니다. 생략하면 true로 기본 설정됩니다. 필드를 선택 사항으로 만들려면, 이를 false로 명시적으로 설정하세요.'),
이 문장 하나 —
불리언 함정(The Boolean Trap)은 저의 CLI에만 국한된 문제가 아닙니다. 이는 AI가 접근 가능한 API(AI-accessible APIs)에서 발생하는 일반적인 문제입니다. null이 아닌 기본값(non-null defaults)을 가진 선택적 불리언(optional booleans)은 실수하기 쉬운 요소(footgun)입니다. AI의 본능은 명시적으로 부정하기보다는 생략하는 쪽을 택하며, 만약 당신의 기본값이 "생략됨 = 안전한 옵션"과 일치하지 않는다면, 소리 없는 버그(silent bugs)가 발생할 것입니다.
formlm-cli MCP 서버는 모든 불리언 파라미터(boolean parameters)에 대해 명시적인 설명을 제공합니다. github.com/formlm/cli에서 오픈 소스로 확인하실 수 있으며, formlm.me에서 플랫폼을 체험해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기