본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 27. 03:24

LLM이 매 요청마다 싸우지 않고 출력 형식을 실제로 따르게 만드는 방법

요약

LLM이 JSON이나 HTML 등 엄격한 출력 형식을 따르지 않는 문제를 해결하기 위한 실무적인 가이드를 제공합니다. 프로그래밍 방식의 검증, 명시적인 부정적 제약 조건 활용, 재시도 로직 적용을 통해 안정적인 프로덕션 환경을 구축하는 방법을 다룹니다.

핵심 포인트

  • 출력 형식을 모델의 답변에 의존하지 말고 코드로 직접 검증할 것
  • 긍정적 지시 외에 '하지 말아야 할 것'을 명시하는 부정적 제약 조건 활용
  • 형식 불일치를 재시도 가능한 오류로 취급하여 백오프 로직 적용

만약 당신이 LLM에게 엄격한 형식 — 유효한 JSON, 특정 HTML 구조, 정확히 N개의 항목 — 으로 출력을 반환하도록 요청해 본 적이 있다면, 아마도 형식이 어긋나는 것을 목격했을 것입니다. 항상 그런 것은 아니지만, 결과를 파싱하는 프로덕션 코드(production code)에서 "대체로 작동함" 수준은 충분하지 않을 만큼 자주 발생합니다.

저는 웹사이트 스크린샷을 Gemini에 보내고, 그 결과로 정해진 HTML 구조(순서가 있는 목록, 각 항목은 특정 태그 레이아웃을 가짐, 추가 요소 없음)를 기대하는 도구를 만들면서 이 문제에 직면했습니다.

계속해서 발생한 문제들

제 프롬프트의 초기 버전은 원하는 형식을 단순히 산문(prose)으로 설명했습니다: "응답을 이 구조를 가진 HTML 목록 형식으로 구성하세요." 이 방식은 약 80%의 확률로 작동했습니다. 나머지 20%에서는 다음과 같은 문제가 발생했습니다:

  • 목록 앞뒤에 붙는 추가적인 설명 ("여기 제 분석 결과입니다:")
  • 특정 한 줄에만 사용되어야 할 <em> 태그가 출력의 다른 곳에 나타나거나, 때로는 문자 그대로 보이는 텍스트로 작성됨
  • 두 개의 별개 이슈가 하나의 목록 항목으로 병합됨
  • 가끔 <li> 태그가 통째로 누락됨

이 중 어느 것도 "모델이 나쁘다"는 뜻은 아닙니다. 이는 모델이 기술적인 제약(hard constraint)이 아닌 서술적인 요청을 부드러운 제안(soft suggestion)으로 취급하기 때문에 발생하는 문제입니다.

실제로 문제를 해결한 방법

1. 출력을 프로그래밍 방식으로 검증하세요, 믿지 마세요.

const roastHtml = result.response.text().trim();

if (!roastHtml.includes("<li>")) {
...

이것만으로도 실패 모드가 "하류(downstream)에서 조용히 깨짐"에서 "자동으로 재시도됨"으로 바뀝니다. 단순한 구조적 체크(예상한 태그가 존재하는가)만으로도 모든 세부 사항을 검증할 필요 없이 대부분의 어긋남을 잡아낼 수 있습니다.

2. 무엇을 해야 하는지뿐만 아니라, 무엇을 하지 말아야 하는지도 명시하세요.

긍정적인 지시 사항("이런 식으로 형식을 맞추세요")은 해석의 여지를 남깁니다. 명시적인 부정적 제약(negative constraints)을 추가하면 그 간극을 메울 수 있습니다:

형식 규칙 — 다음은 엄격히 준수해야 합니다:
- <em> 태그는 각 항목 끝의 Fix 라인에만 사용되어야 합니다 — 설명 부분에는 절대 사용하지 마세요
...

"fix를 위해 em 태그를 사용하세요"와 "그 외의 어디에서도 em 태그를 사용하지 말고, 가시적인 텍스트로 작성하지 마세요"의 차이는, 비록 두 문구 모두 동일한 의도된 동작을 설명하고 있을지라도 제안 (suggestion)과 제약 (constraint)의 차이입니다.

3. 검증 실패 시 백오프(backoff)를 적용하여 재시도하기.

async function withRetry(fn, label, maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
...

형식 드리프트 (Format drift)는 보통 일관적이지 않습니다. 동일한 입력에 대해 동일한 프롬프트를 사용하더라도 두 번째 시도에서는 성공하는 경우가 많습니다. 형식 불일치 (format mismatch)를 네트워크 타임아웃 (network timeout)과 동일하게 재시도 가능한 오류로 취급하는 것은 비용이 거의 들지 않으면서도 남은 대부분의 사례를 해결해 줍니다.

근본적인 패턴

부드럽고 서술적인 지침은 출력이 사용 가능하기 위해 논리적으로 필수적인 경우라 할지라도 요구 사항 (requirements)이 아닌 제안 (suggestions)으로 취급됩니다. 모델은 제약을 제약으로서 명시받아야 하며, 여러분의 코드는 준수를 가정하기보다 검증 (verify)해야 합니다. 어느 한 쪽도 단독으로는 충분하지 않았습니다. 프롬프트 변경은 드리프트를 줄였지만, 검증 (validation) + 재시도 (retry)가 파이프라인을 실제로 엔드 투 엔드 (end to end)로 신뢰할 수 있게 만듭니다.

만약 LLM 출력을 후속 단계에서 파싱 (parse)하는 무언가를 구축하고 있다면: 구조적으로 검증하고, 제약을 긍정문뿐만 아니라 부정문으로도 명시하며, 형식 불일치를 나중에 패치해야 할 예외 사례 (edge cases)가 아닌 재시도 가능한 실패로 취급하십시오.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0