🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

A

AI Generated

2025. 12. 28. · 63 Views

LLM-as-Judge TypeScript 실전 구현 가이드

프로덕션급 LLM 평가 시스템을 TypeScript로 구현하는 방법을 다룹니다. 19개의 테스트로 검증된 Direct Scoring, Pairwise Comparison, Rubric Generation 패턴을 실습하며, Eugene Yan과 Vercel AI SDK 6의 연구를 실제 코드에 적용합니다.


목차

  1. 프로젝트_개요_프로덕션급_LLM_평가_툴
  2. 코드베이스_탐색_examples_llm_as_judge_skills
  3. TypeScript_구현_핵심_평가_패턴_네_가지
  4. 테스트_코드_분석_19개_테스트가_검증하는_것
  5. Eugene_Yan과_Vercel_AI_SDK_연구_적용_사례
  6. Provide final score with justification
  7. 박시니어의_조언_프로덕션_배포_체크리스트

1. 프로젝트 개요 프로덕션급 LLM 평가 툴

어느 날 김개발 씨는 회사에서 새로운 미션을 받았습니다. "우리 챗봇의 응답 품질을 자동으로 평가하는 시스템을 만들어주세요." 사람이 일일이 평가하기엔 하루에 쏟아지는 응답이 너무 많았습니다.

김개발 씨는 고민에 빠졌습니다. LLM이 LLM을 평가한다고?

LLM-as-Judge는 한마디로 대규모 언어 모델이 다른 모델의 출력을 평가하는 패턴입니다. 마치 숙련된 편집자가 원고를 검토하듯, LLM이 정해진 기준에 따라 응답의 품질을 판단합니다.

이 프로젝트는 19개의 테스트를 모두 통과한 프로덕션급 구현체로, 실무에 바로 적용할 수 있는 수준의 코드를 제공합니다.

다음 코드를 살펴봅시다.

// llm-as-judge-skills 프로젝트 구조
// 19 passing tests로 검증된 프로덕션급 평가 시스템

export interface EvaluationResult {
  score: number;           // 0-10 점수
  reasoning: string;       // 평가 근거
  confidence: number;      // 신뢰도 (0-1)
  criteria: string[];      // 평가 기준
}

// 핵심 평가 전략 세 가지
export type EvaluationStrategy =
  | 'direct-scoring'       // 직접 점수 매기기
  | 'pairwise-comparison'  // 두 응답 비교
  | 'rubric-based';        // 루브릭 기반 평가

김개발 씨는 입사 2년 차 백엔드 개발자입니다. 최근 회사에서 AI 챗봇 서비스를 런칭했는데, 예상보다 훨씬 많은 사용자가 몰렸습니다.

하루에 수만 건의 응답이 생성되고 있었고, 품질 관리가 시급한 상황이었습니다. 팀장님이 회의에서 말했습니다.

"응답 품질을 사람이 다 확인할 수는 없어요. 자동화된 평가 시스템이 필요합니다." 김개발 씨에게 그 임무가 떨어졌습니다.

처음에 김개발 씨는 단순한 규칙 기반 시스템을 생각했습니다. 응답 길이가 너무 짧으면 감점, 특정 키워드가 없으면 감점하는 식이었습니다.

하지만 금방 한계에 부딪혔습니다. 응답의 맥락을 이해하지 못하는 규칙으로는 진정한 품질을 평가할 수 없었습니다.

그때 선배 개발자 박시니어 씨가 다가왔습니다. "LLM-as-Judge라는 패턴을 알아?

LLM이 직접 다른 LLM의 출력을 평가하는 거야." LLM-as-Judge는 마치 숙련된 편집자가 원고를 검토하는 것과 같습니다. 편집자는 단순히 맞춤법만 보는 게 아니라, 글의 흐름, 논리적 일관성, 독자에 대한 배려까지 종합적으로 평가합니다.

LLM도 마찬가지입니다. 정해진 평가 기준에 따라 응답의 정확성, 유용성, 안전성 등을 종합적으로 판단할 수 있습니다.

이 프로젝트는 단순한 실험이 아닙니다. 19개의 테스트 케이스를 모두 통과한 프로덕션급 구현체입니다.

테스트는 다양한 시나리오를 커버합니다. 정상적인 응답, 엣지 케이스, 에러 상황까지 모두 검증되어 있습니다.

프로젝트의 핵심 평가 전략은 세 가지입니다. 첫째, Direct Scoring은 응답에 직접 점수를 매기는 방식입니다.

가장 단순하고 빠릅니다. 둘째, Pairwise Comparison은 두 응답을 비교하여 어느 것이 더 나은지 판단합니다.

상대적 품질을 측정할 때 유용합니다. 셋째, Rubric-based Evaluation은 세부 기준표에 따라 체계적으로 평가합니다.

가장 정교하지만 설정이 복잡합니다. 실제 프로덕션 환경에서는 이 세 가지 전략을 상황에 맞게 조합하여 사용합니다.

예를 들어 1차 필터링에는 빠른 Direct Scoring을, 상위 후보군 비교에는 Pairwise Comparison을, 최종 품질 보고서에는 Rubric-based를 사용하는 식입니다. 김개발 씨는 프로젝트 구조를 살펴보며 감탄했습니다.

"테스트가 이렇게 꼼꼼하게 작성되어 있다니, 이건 정말 실무에서 바로 쓸 수 있겠네요." 박시니어 씨가 고개를 끄덕였습니다. "그래, 프로덕션급이라는 건 단순히 작동한다는 게 아니야.

예외 상황까지 모두 대비되어 있다는 뜻이지."

실전 팁

💡 - LLM-as-Judge는 완벽하지 않습니다. 중요한 결정에는 여전히 사람의 검토가 필요합니다.

  • 평가 전략은 용도에 맞게 선택하세요. 속도가 중요하면 Direct Scoring, 정확도가 중요하면 Rubric-based를 사용합니다.

2. 코드베이스 탐색 examples llm as judge skills

김개발 씨는 프로젝트를 클론받고 폴더 구조를 살펴보기 시작했습니다. "어디서부터 봐야 할지 모르겠어요." 박시니어 씨가 웃으며 대답했습니다.

"코드베이스 탐색에도 순서가 있어. 먼저 전체 구조를 파악하고, 핵심 진입점을 찾는 거야."

examples/llm-as-judge-skills 디렉토리는 체계적으로 구성된 TypeScript 프로젝트입니다. 마치 잘 정리된 도서관처럼, 각 폴더와 파일이 명확한 역할을 가지고 있습니다.

이 구조를 이해하면 어떤 기능을 어디서 찾아야 하는지 즉시 알 수 있습니다.

다음 코드를 살펴봅시다.

// examples/llm-as-judge-skills/ 프로젝트 구조
//
// src/
// ├── evaluators/
// │   ├── DirectScoringEvaluator.ts    // 직접 점수 평가
// │   ├── PairwiseComparisonEvaluator.ts // 쌍비교 평가
// │   └── RubricEvaluator.ts           // 루브릭 기반 평가
// ├── agents/
// │   └── EvaluatorAgent.ts            // 통합 에이전트
// ├── prompts/
// │   └── evaluationPrompts.ts         // 프롬프트 템플릿
// └── types/
//     └── evaluation.ts                // 타입 정의
// tests/
// └── evaluators.test.ts               // 19개 테스트

좋은 프로젝트는 폴더 구조만 봐도 어떤 일을 하는지 알 수 있습니다. 마치 잘 정리된 서재처럼, 어떤 책이 어디에 있는지 직관적으로 파악할 수 있어야 합니다.

김개발 씨가 프로젝트 루트에서 터미널을 열었습니다. "일단 폴더 구조부터 볼게요." 화면에 나타난 구조를 보며 박시니어 씨가 설명을 시작했습니다.

src/evaluators 폴더가 이 프로젝트의 핵심입니다. 여기에 세 가지 평가 전략이 각각 독립된 파일로 구현되어 있습니다.

DirectScoringEvaluator는 단일 응답에 점수를 매기고, PairwiseComparisonEvaluator는 두 응답을 비교하며, RubricEvaluator는 세부 기준에 따라 평가합니다. src/agents 폴더에는 EvaluatorAgent가 있습니다.

이 에이전트는 상황에 맞는 평가 전략을 자동으로 선택합니다. 개발자가 매번 어떤 평가 방식을 쓸지 고민하지 않아도 됩니다.

에이전트가 입력을 분석하고 최적의 전략을 적용합니다. src/prompts 폴더는 프롬프트 템플릿을 모아둔 곳입니다.

LLM에게 평가를 요청할 때 사용하는 프롬프트들이 여기에 정의되어 있습니다. 프롬프트를 코드와 분리해두면 나중에 프롬프트만 수정하기가 훨씬 쉽습니다.

src/types 폴더에는 TypeScript 타입 정의가 있습니다. EvaluationResult, EvaluationConfig, RubricCriteria 같은 인터페이스들이 정의되어 있어, 코드 전체에서 일관된 타입을 사용할 수 있습니다.

tests 폴더에는 19개의 테스트가 담긴 evaluators.test.ts 파일이 있습니다. 테스트는 단순히 "작동하는지" 확인하는 것이 아닙니다.

경계값, 에러 케이스, 동시성 상황까지 꼼꼼하게 검증합니다. 김개발 씨가 질문했습니다.

"이렇게 폴더를 나누는 기준이 뭐예요?" 박시니어 씨가 대답했습니다. "단일 책임 원칙이야.

각 폴더, 각 파일이 하나의 명확한 책임만 가지도록 하는 거지." 이런 구조의 장점은 확장성입니다. 새로운 평가 전략을 추가하고 싶다면?

evaluators 폴더에 새 파일을 만들면 됩니다. 기존 코드를 건드리지 않아도 됩니다.

프롬프트를 개선하고 싶다면? prompts 폴더의 해당 파일만 수정하면 됩니다.

또한 테스트하기도 쉽습니다. 각 모듈이 독립적이므로, 개별 컴포넌트를 격리해서 테스트할 수 있습니다.

19개 테스트가 빠르게 실행될 수 있는 이유이기도 합니다. 김개발 씨는 고개를 끄덕였습니다.

"구조가 이렇게 명확하니까 어디를 봐야 할지 바로 알겠어요."

실전 팁

💡 - 새 프로젝트를 시작할 때 먼저 폴더 구조를 설계하세요. 나중에 바꾸기는 어렵습니다.

  • 파일 하나가 200줄을 넘으면 분리를 고려하세요. 작은 파일이 이해하기도, 테스트하기도 쉽습니다.

3. TypeScript 구현 핵심 평가 패턴 네 가지

폴더 구조를 파악한 김개발 씨는 이제 실제 코드를 읽기 시작했습니다. "Direct Scoring부터 볼게요." 박시니어 씨가 옆에서 지켜보며 말했습니다.

"좋아, 각 패턴의 차이점을 잘 비교해 봐. 언제 어떤 패턴을 써야 하는지가 핵심이야."

네 가지 핵심 구현체가 이 프로젝트의 심장입니다. Direct Scoring은 단일 응답에 점수를 매기고, Pairwise Comparison은 두 응답의 우열을 가리며, Rubric Generation은 평가 기준을 자동 생성하고, EvaluatorAgent는 이 모든 것을 통합하여 상황에 맞는 전략을 선택합니다.

다음 코드를 살펴봅시다.

// Direct Scoring Evaluator 핵심 구현
import { generateObject } from 'ai';
import { z } from 'zod';

const scoreSchema = z.object({
  score: z.number().min(0).max(10),
  reasoning: z.string(),
  strengths: z.array(z.string()),
  weaknesses: z.array(z.string())
});

export async function directScore(
  response: string,
  criteria: string
): Promise<EvaluationResult> {
  const { object } = await generateObject({
    model: openai('gpt-4o'),
    schema: scoreSchema,
    prompt: `Evaluate the following response (0-10):
             Criteria: ${criteria}
             Response: ${response}`
  });
  return object;
}

네 가지 핵심 패턴은 각각 다른 상황에서 빛을 발합니다. 마치 연장통에 있는 도구들처럼, 상황에 맞는 도구를 선택해야 합니다.

Direct Scoring Evaluator부터 살펴봅시다. 이것은 가장 단순하고 직관적인 평가 방식입니다.

하나의 응답을 받아서 0점부터 10점 사이의 점수를 매깁니다. 점수뿐 아니라 평가 근거(reasoning), 강점(strengths), 약점(weaknesses)도 함께 반환합니다.

코드를 보면 Vercel AI SDK의 generateObject 함수를 사용합니다. 이 함수는 LLM의 응답을 미리 정의한 스키마에 맞게 구조화된 객체로 받아옵니다.

Zod 라이브러리로 스키마를 정의하면, 타입 안전성과 런타임 검증을 동시에 얻을 수 있습니다. Pairwise Comparison Evaluator는 두 응답을 비교합니다.

"A가 더 나은가, B가 더 나은가?" 단순히 승패만 가리는 것이 아니라, 왜 그런 판단을 내렸는지 근거도 함께 제공합니다. 이 방식은 절대적인 점수보다 상대적 품질이 중요할 때 유용합니다.

예를 들어 A/B 테스트에서 어떤 프롬프트가 더 좋은 응답을 생성하는지 비교할 때 사용합니다. 또한 여러 응답 중 최선을 선택하는 랭킹 시스템에도 적합합니다.

Rubric Generation은 조금 다릅니다. 평가 자체를 수행하는 것이 아니라, 평가 기준(루브릭)을 자동으로 생성합니다.

"좋은 고객 서비스 응답이란 무엇인가?"라는 질문에 대해, LLM이 구체적인 평가 기준과 점수 체계를 만들어냅니다. 왜 이것이 필요할까요?

도메인마다 평가 기준이 다르기 때문입니다. 의료 상담 챗봇과 쇼핑 추천 챗봇의 평가 기준은 완전히 다릅니다.

매번 사람이 루브릭을 작성하는 대신, LLM에게 초안을 맡기고 사람이 검토하는 방식이 효율적입니다. 마지막으로 EvaluatorAgent는 이 모든 것을 통합하는 오케스트라 지휘자입니다.

입력을 분석하여 어떤 평가 전략을 사용할지 자동으로 결정합니다. 단일 응답이면 Direct Scoring, 두 응답 비교 요청이면 Pairwise, 새로운 도메인이면 먼저 Rubric을 생성하는 식입니다.

김개발 씨가 감탄했습니다. "에이전트가 알아서 전략을 선택한다니, 사용하는 쪽에서는 정말 편하겠네요." 박시니어 씨가 고개를 끄덕였습니다.

"그래, 그게 바로 좋은 추상화야. 복잡한 건 내부에 숨기고, 사용자에게는 단순한 인터페이스만 제공하는 거지." 각 평가자는 독립적으로 작동하므로 테스트하기도, 교체하기도 쉽습니다.

새로운 평가 방식이 필요하면 기존 코드를 건드리지 않고 새 클래스를 추가하면 됩니다.

실전 팁

💡 - generateObject를 사용할 때 스키마를 먼저 정의하세요. 타입 안전성을 확보할 수 있습니다.

  • 에이전트 패턴은 결정 로직이 복잡할 때 유용합니다. 단순한 경우에는 오히려 과도한 설계일 수 있습니다.

4. 테스트 코드 분석 19개 테스트가 검증하는 것

코드 구현을 이해한 김개발 씨는 테스트 파일을 열었습니다. "19개나 되네요." 박시니어 씨가 의미심장하게 말했습니다.

"테스트 개수보다 중요한 건 무엇을 검증하느냐야. 같이 분석해 보자."

19개의 테스트는 단순히 "작동 확인"을 넘어 프로덕션 환경에서 발생할 수 있는 다양한 상황을 검증합니다. 정상 경로뿐 아니라 에러 처리, 경계값, 타입 검증까지 포함합니다.

이 테스트들이 모두 통과한다는 것은 실무에서 바로 사용해도 된다는 신뢰의 증거입니다.

다음 코드를 살펴봅시다.

// evaluators.test.ts - 핵심 테스트 케이스들
import { describe, it, expect } from 'vitest';

describe('DirectScoringEvaluator', () => {
  it('should return score between 0-10', async () => {
    const result = await directScore(response, criteria);
    expect(result.score).toBeGreaterThanOrEqual(0);
    expect(result.score).toBeLessThanOrEqual(10);
  });

  it('should include reasoning for the score', async () => {
    const result = await directScore(response, criteria);
    expect(result.reasoning).toBeTruthy();
    expect(result.reasoning.length).toBeGreaterThan(50);
  });

  it('should handle empty response gracefully', async () => {
    const result = await directScore('', criteria);
    expect(result.score).toBe(0);
    expect(result.weaknesses).toContain('Empty response');
  });
});

테스트 코드는 때로 실제 코드보다 더 많은 것을 알려줍니다. 어떤 시나리오를 중요하게 여기는지, 어떤 엣지 케이스를 고려했는지가 테스트에 담겨 있습니다.

19개의 테스트를 크게 네 가지 카테고리로 분류할 수 있습니다. 첫 번째는 정상 경로(Happy Path) 테스트입니다.

올바른 입력이 주어졌을 때 기대한 결과가 나오는지 확인합니다. 점수가 0에서 10 사이인지, 평가 근거가 포함되어 있는지, 강점과 약점이 배열 형태인지 등을 검증합니다.

두 번째는 경계값(Boundary) 테스트입니다. 점수의 최소값 0과 최대값 10이 제대로 처리되는지 확인합니다.

아주 짧은 응답이나 아주 긴 응답도 문제없이 평가할 수 있는지 테스트합니다. 이런 경계 상황에서 버그가 자주 발생하기 때문입니다.

세 번째는 에러 처리(Error Handling) 테스트입니다. 빈 문자열이 입력되면 어떻게 될까요?

null이나 undefined가 들어오면? API 호출이 실패하면?

이런 비정상적인 상황에서도 시스템이 우아하게 실패(graceful failure)하는지 확인합니다. 에러가 발생해도 전체 시스템이 멈추지 않고, 의미 있는 에러 메시지를 반환해야 합니다.

네 번째는 타입 검증(Type Validation) 테스트입니다. generateObject가 반환하는 결과가 정의한 스키마에 맞는지 확인합니다.

score가 숫자인지, reasoning이 문자열인지, strengths가 배열인지 등을 검증합니다. TypeScript의 컴파일 타임 검사와 Zod의 런타임 검사가 함께 작동합니다.

김개발 씨가 물었습니다. "테스트 커버리지가 100%인가요?" 박시니어 씨가 대답했습니다.

"커버리지 수치보다 중요한 건 의미 있는 시나리오를 테스트하느냐야. 코드 라인을 다 실행한다고 좋은 테스트가 아니거든." 특히 주목할 만한 테스트가 있습니다.

일관성(Consistency) 테스트입니다. 같은 입력에 대해 여러 번 평가했을 때 점수가 크게 달라지지 않는지 확인합니다.

LLM의 특성상 완전히 동일한 결과를 기대하기 어렵지만, 허용 가능한 범위 내에서 일관되어야 합니다. 또 하나는 비교 정합성(Comparison Consistency) 테스트입니다.

A가 B보다 낫고, B가 C보다 낫다면, A가 C보다 나아야 합니다. 이런 이행성(transitivity)이 지켜지는지 확인합니다.

김개발 씨는 테스트 코드를 읽으며 감탄했습니다. "테스트만 봐도 이 시스템이 어떤 상황까지 고려했는지 알겠어요." 박시니어 씨가 웃었습니다.

"그래서 테스트를 문서라고도 하는 거야. 코드가 어떻게 사용되어야 하는지 알려주니까."

실전 팁

💡 - 테스트를 작성할 때 정상 경로만 생각하지 마세요. 에러 케이스가 더 중요합니다.

  • 테스트 이름은 구체적으로 작성하세요. "should work"보다 "should return score between 0-10"이 훨씬 명확합니다.

5. Eugene Yan과 Vercel AI SDK 연구 적용 사례

"이 프로젝트, 아무 근거 없이 만든 게 아니야." 박시니어 씨가 브라우저를 열며 말했습니다. "Eugene Yan이라는 연구자의 논문과 Vercel AI SDK 6 문서를 기반으로 설계한 거거든." 김개발 씨의 눈이 반짝였습니다.

학술 연구가 실제 코드로 구현되는 과정이 궁금했습니다.

Eugene Yan의 LLM-as-Judge 연구는 이 분야의 기초를 다진 중요한 논문입니다. Vercel AI SDK 6는 그 이론을 실용적인 코드로 구현할 수 있게 해주는 도구입니다.

이 프로젝트는 두 가지를 결합하여, 학술적 엄밀성과 프로덕션 실용성을 모두 갖추었습니다.

다음 코드를 살펴봅시다.

// Eugene Yan의 연구에서 도출된 핵심 원칙 구현
// Reference: "LLM-as-Judge" (Yan et al.)

// 1. Chain-of-Thought 평가 (reasoning 필수 포함)
const evaluationPrompt = `
Think step-by-step before giving a score:

5. Provide final score with justification

좋은 엔지니어링은 허공에서 나오지 않습니다. 연구와 이론에 기반하여 검증된 방법론을 적용할 때 진정한 품질이 나옵니다.

Eugene Yan은 Amazon에서 일하는 ML 엔지니어이자 연구자입니다. 그의 LLM-as-Judge 연구는 AI가 AI를 평가할 때 발생하는 다양한 편향(bias)과 한계를 분석하고, 이를 완화하는 방법을 제시합니다.

첫 번째 핵심 원칙은 Chain-of-Thought 평가입니다. 단순히 점수만 요구하면 LLM이 충분히 고민하지 않고 답합니다.

대신 "단계별로 생각하고 나서 점수를 매겨라"고 요청하면 평가의 질이 올라갑니다. 코드에서 evaluationPrompt가 이 원칙을 구현한 부분입니다.

두 번째 원칙은 Position Bias 완화입니다. Pairwise Comparison에서 항상 먼저 나오는 응답에 높은 점수를 주는 경향이 있습니다.

이를 방지하기 위해 두 응답의 순서를 무작위로 섞고, 나중에 결과를 보정합니다. mitigatePositionBias 함수가 이 역할을 합니다.

세 번째 원칙은 **구조화된 출력(Structured Output)**입니다. LLM의 자유 형식 응답은 파싱하기 어렵고 예측 불가능합니다.

Vercel AI SDK 6의 generateObject 함수를 사용하면 Zod 스키마에 맞는 JSON 객체를 직접 받을 수 있습니다. 파싱 에러 걱정 없이 타입 안전한 데이터를 얻습니다.

Vercel AI SDK 6는 이 프로젝트의 핵심 의존성입니다. 이 SDK는 다양한 LLM 프로바이더(OpenAI, Anthropic, Google 등)를 통일된 인터페이스로 사용할 수 있게 해줍니다.

또한 스트리밍, 구조화된 출력, 툴 호출 등 현대적인 AI 애플리케이션에 필요한 기능을 모두 제공합니다. 김개발 씨가 질문했습니다.

"다른 LLM으로 바꾸고 싶으면 어떻게 해요?" 박시니어 씨가 대답했습니다. "모델 이름만 바꾸면 돼.

SDK가 추상화를 잘 해놨거든. openai('gpt-4o')를 anthropic('claude-3-5-sonnet')로 바꾸면 끝이야." 연구 논문과 실용적인 SDK의 결합이 이 프로젝트를 특별하게 만듭니다.

이론적으로 검증된 방법론을 프로덕션에서 바로 사용할 수 있는 코드로 구현했습니다. 학술 연구가 현업 개발자에게까지 도달하는 데 시간이 걸리는데, 이 프로젝트가 그 간극을 메워줍니다.

박시니어 씨가 덧붙였습니다. "Eugene Yan의 블로그도 읽어봐.

연구 논문을 실무자가 이해하기 쉽게 정리해뒀어. 우리 같은 개발자에게 정말 유용해."

실전 팁

💡 - Position Bias를 완화하려면 비교 순서를 무작위화하세요. 간단하지만 효과적입니다.

  • Vercel AI SDK는 프로바이더를 쉽게 교체할 수 있습니다. 특정 벤더에 종속되지 않도록 설계하세요.

6. 박시니어의 조언 프로덕션 배포 체크리스트

모든 코드를 이해한 김개발 씨가 물었습니다. "이제 바로 배포해도 되나요?" 박시니어 씨가 고개를 저었습니다.

"아직 멀었어. 코드가 완성된 것과 프로덕션에 배포 가능한 것은 전혀 다른 이야기야." 그리고 화이트보드에 체크리스트를 적기 시작했습니다.

프로덕션 배포는 코드 완성의 끝이 아니라 새로운 시작입니다. 비용 관리, 레이트 리밋, 모니터링, 폴백 전략, 보안 등 운영에 필요한 요소들을 모두 갖춰야 합니다.

이 체크리스트는 실무에서 수많은 장애를 겪으며 정리된 교훈입니다.

다음 코드를 살펴봅시다.

// 프로덕션 배포 체크리스트 구현
interface ProductionConfig {
  // 1. 비용 제어
  maxTokensPerRequest: number;
  dailyBudgetLimit: number;

  // 2. 레이트 리밋
  requestsPerMinute: number;
  concurrentRequests: number;

  // 3. 모니터링
  enableLogging: boolean;
  alertThreshold: number;

  // 4. 폴백 전략
  fallbackModel: string;
  timeoutMs: number;
  retryCount: number;
}

const productionConfig: ProductionConfig = {
  maxTokensPerRequest: 4096,
  dailyBudgetLimit: 100, // USD
  requestsPerMinute: 60,
  concurrentRequests: 10,
  enableLogging: true,
  alertThreshold: 0.8, // 80% 예산 소진 시 알림
  fallbackModel: 'gpt-3.5-turbo',
  timeoutMs: 30000,
  retryCount: 3
};

박시니어 씨가 화이트보드 앞에 섰습니다. "프로덕션 배포 전에 반드시 확인해야 할 다섯 가지를 알려줄게." 첫 번째는 비용 관리입니다.

LLM API는 사용량에 따라 비용이 발생합니다. 예상치 못한 트래픽 폭주로 하룻밤에 수천 달러가 청구될 수도 있습니다.

반드시 일일 예산 한도를 설정하고, 한도에 가까워지면 알림을 받도록 구성해야 합니다. 두 번째는 레이트 리밋입니다.

LLM 프로바이더는 분당 요청 수에 제한을 둡니다. 이 한도를 초과하면 에러가 발생합니다.

클라이언트 측에서 미리 요청 속도를 조절하면 에러를 예방하고 사용자 경험도 개선됩니다. 동시 요청 수도 제한해야 합니다.

세 번째는 모니터링입니다. 프로덕션에서는 눈에 보이지 않는 곳에서 문제가 발생합니다.

모든 요청과 응답을 로깅하고, 평가 점수의 분포를 추적해야 합니다. 갑자기 평균 점수가 떨어지면 모델에 문제가 생겼거나 입력 데이터가 변했다는 신호입니다.

네 번째는 폴백 전략입니다. 주력 모델(예: GPT-4o)이 장애를 일으키면 어떻게 할까요?

대체 모델을 미리 설정해두어야 합니다. 품질은 약간 떨어지더라도 서비스가 완전히 중단되는 것보다 낫습니다.

타임아웃과 재시도 로직도 필수입니다. 다섯 번째는 보안입니다.

API 키는 절대 코드에 하드코딩하지 마세요. 환경 변수나 시크릿 매니저를 사용합니다.

또한 사용자 입력을 그대로 프롬프트에 넣으면 프롬프트 인젝션 공격에 취약합니다. 입력을 반드시 검증하고 정제해야 합니다.

김개발 씨가 질문했습니다. "이 모든 걸 직접 구현해야 하나요?" 박시니어 씨가 대답했습니다.

"기본적인 것들은 직접 구현하고, 복잡한 건 서비스를 활용해. 예를 들어 LangSmith나 Helicone 같은 LLM 관측 도구를 쓰면 모니터링이 훨씬 쉬워져." 마지막으로 박시니어 씨가 강조했습니다.

"그리고 가장 중요한 건, 점진적 배포야. 처음부터 모든 트래픽에 적용하지 말고, 1% 트래픽으로 시작해서 문제없으면 천천히 늘려가는 거야.

카나리 배포라고도 해." 김개발 씨는 체크리스트를 노트에 옮겨 적었습니다. 코드를 작성하는 것과 프로덕션에서 안정적으로 운영하는 것 사이에 이렇게 많은 고려사항이 있다는 것을 오늘 처음 알았습니다.

박시니어 씨가 마무리했습니다. "코드는 20%고, 운영이 80%야.

오늘 배운 것들을 하나씩 적용해 나가면, 어느새 프로덕션급 시스템을 운영하는 시니어 엔지니어가 되어 있을 거야."

실전 팁

💡 - 일일 비용 한도를 반드시 설정하세요. 예상치 못한 청구서를 막을 수 있습니다.

  • 처음에는 1% 트래픽으로 시작하는 카나리 배포를 활용하세요. 문제를 빨리 발견할 수 있습니다.
  • LLM 관측 도구(LangSmith, Helicone 등)를 도입하면 디버깅과 최적화가 훨씬 쉬워집니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#TypeScript#LLM-as-Judge#AI-Evaluation#Vercel-AI-SDK#Prompt-Engineering#AI Engineering

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.