개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

Pydantic AI로 LLM 응답을 타입 안전하게 검증하기

LLM을 프로덕션에 붙여본 분이라면 이런 상황을 한 번쯤 겪어봤을 겁니다. GPT한테 JSON으로 응답하라고 시스템 프롬프트까지 꼼꼼히 썼는데, 정작 런타임에 KeyError가 터지는 상황. 아니면 result["block_card"]가 어떤 날은 True고 어떤 날은 "true"인 상황. LLM 출력을 딕셔너리로 받아 쓰는 코드는 언제나 이런 시한폭탄을 안고 있습니다.

Pydantic AI는 LLM 응답을 Pydantic BaseModel로 강제 파싱·검증함으로써, 타입 불일치와 누락 필드를 런타임 이전에 잡아내는 Python 기반 에이전트 프레임워크입니다. 2024년 말 Pydantic 팀이 공개하고 2025년 9월 v1.0 안정 버전을 출시했으며, Thoughtworks Technology Radar 2025에 "Trial" 단계로 등재됐습니다. "FastAPI가 웹 API 개발에 가져온 DX를 GenAI 에이전트 개발에도"라는 철학이 코드 곳곳에 녹아 있어서, FastAPI를 써본 Python 백엔드 개발자라면 학습 곡선이 생각보다 완만하게 느껴질 겁니다.

이 글을 읽고 나면 LLM 연동 코드에서 mypy가 통과하고, CI에서 실제 API 비용 없이 에이전트를 테스트하며, FastAPI 엔드포인트에 추가 직렬화 코드 없이 LLM 분석을 붙이는 방법을 바로 써먹을 수 있습니다. LangChain·LangGraph와 어떤 상황에서 무엇을 선택하면 좋은지도 솔직하게 다룹니다.

목차

  • 핵심 개념 — 타입 안전 출력, 의존성 주입, 모델 어그노스틱
  • 실전 적용 — 뱅킹 에이전트, FastAPI 통합
  • 비용 없는 테스트
  • 어떤 프레임워크를 선택할까?
  • 장단점 분석
  • 마치며

핵심 개념

타입 안전 출력 — output_type으로 LLM 응답을 Python 객체로

기존 에이전트 프레임워크들은 LLM 응답을 문자열이나 느슨한 딕셔너리로 돌려줍니다. Pydantic AI는 다릅니다. output_type에 Pydantic 모델을 선언하면, 그 JSON Schema가 LLM 프롬프트에 자동으로 주입되고, 응답은 Pydantic 검증을 거쳐 완전한 Python 객체로 반환됩니다.

python
import asyncio
from typing import Literal
from pydantic import BaseModel
from pydantic_ai import Agent
 
class AnalysisResult(BaseModel):
    sentiment: Literal["positive", "negative", "neutral"]  # str이 아닌 리터럴 타입
    confidence: float  # 0.0 ~ 1.0
    summary: str
 
agent = Agent('openai:gpt-4o', output_type=AnalysisResult)
 
async def main():
    result = await agent.run('이 리뷰를 분석해줘: 배송이 너무 늦었어요')
    print(result.output.confidence)  # float 보장, IDE 자동완성 작동
    print(result.output.sentiment)   # "positive" | "negative" | "neutral" 중 하나
 
asyncio.run(main())

저도 처음엔 "프롬프트에 JSON Schema를 주입한다는 게 실제로 얼마나 잘 먹히나?" 싶었는데, 생각보다 훨씬 안정적입니다. LLM이 잘못된 JSON을 반환하면 검증 오류 메시지 전체를 LLM에 피드백으로 다시 전달하면서 재시도합니다. 기본값은 1회 재시도(총 2번 시도)이며, Agent(retries=3)처럼 조정할 수 있습니다. 그래도 실패하면 ValidationError가 명확하게 터집니다.

참고로 LLM 제공자마다 Structured Output 구현 방식이 다른데, Pydantic AI가 이를 알아서 추상화해줍니다. OpenAI는 response_format={"type": "json_schema"}를 쓰고, Anthropic은 tool_use 방식으로 스키마를 강제하는데, 개발자 입장에서는 output_type 하나만 선언하면 됩니다.

Structured Output — LLM이 자유 텍스트가 아닌 특정 JSON 스키마에 맞는 응답을 반환하도록 유도하는 기법. Pydantic AI는 이 과정을 자동화하고, 검증 실패 시 오류 피드백과 함께 재시도까지 처리합니다.


의존성 주입 — DB, HTTP 클라이언트를 타입 안전하게 툴에 전달

FastAPI의 Depends 패턴을 써본 분이라면 바로 이해가 될 겁니다. deps_type으로 의존성 컨테이너를 선언하면, 에이전트 툴 함수 안에서 ctx.deps를 통해 타입 안전하게 꺼내 쓸 수 있습니다. DB 커넥션, HTTP 클라이언트, 설정 값 등을 전역 상태나 환경 변수 없이 명시적으로 주입할 수 있는 구조입니다.

python
from dataclasses import dataclass
from httpx import AsyncClient  # pip install httpx
from pydantic_ai import Agent, RunContext
 
# from myapp.db import DatabaseConn  # 실제 프로젝트의 DB 커넥션 클래스
 
@dataclass
class AppDeps:
    db: DatabaseConn
    http_client: AsyncClient
    user_id: int
 
agent = Agent('anthropic:claude-3-7-sonnet', deps_type=AppDeps)
 
@agent.tool
async def fetch_user_orders(ctx: RunContext[AppDeps]) -> list[dict]:
    # ctx.deps.db, ctx.deps.user_id 모두 타입 추론 가능
    return await ctx.deps.db.query(
        'SELECT * FROM orders WHERE user_id = $1', ctx.deps.user_id
    )

RunContext[T] — 툴 함수에 전달되는 컨텍스트 객체. T에 deps 타입을 제네릭으로 선언하면 ctx.deps 접근 시 IDE 자동완성과 mypy 검사가 모두 작동합니다.

이 구조의 실질적인 장점은 테스트에서 드러납니다. AppDeps에 목 객체를 넣어 실제 API 비용 없이 단위 테스트를 구성할 수 있습니다. 자세한 내용은 아래 비용 없는 테스트 섹션에서 이어집니다.


모델 어그노스틱 — 벤더 교체가 한 줄

공식 문서에 따르면 OpenAI, Anthropic, Gemini, DeepSeek, Mistral, Ollama 등 25개 이상의 LLM 제공자를 지원하며, 비즈니스 로직을 건드리지 않고 모델 문자열 하나만 바꾸면 됩니다.

python
# 개발 환경: 로컬 Ollama (API 비용 없음)
agent = Agent('ollama:llama3.1')
 
# 프로덕션: OpenAI
agent = Agent('openai:gpt-4o')
 
# 비용 절감 실험: DeepSeek
agent = Agent('deepseek:deepseek-chat')

Model-Agnostic — 특정 LLM 벤더에 종속되지 않는 설계. 벤더 가격 변동이나 서비스 장애에 유연하게 대응할 수 있습니다.


실전 적용

예시 1: 뱅킹 고객 지원 에이전트

금융 도메인에서 LLM 출력을 프로그래밍 방식으로 소비해야 하는 대표적인 케이스입니다. 카드 차단 여부(block_card)가 True/"true"/1로 들쑥날쑥하면 안 되는 상황이죠. 저희 팀도 처음에 output_type 없이 딕셔너리로 시작했다가 나중에 마이그레이션하면서 예상보다 훨씬 많은 코드를 손봤습니다. 처음부터 Pydantic 모델을 정의해두는 게 훨씬 낫다는 걸 그때 체감했습니다.

python
import asyncio
from dataclasses import dataclass
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
 
# from myapp.db import DatabaseConn  # 실제 프로젝트의 DB 커넥션 클래스
 
@dataclass
class SupportDeps:
    customer_id: int
    db: DatabaseConn
 
class SupportResult(BaseModel):
    support_advice: str
    block_card: bool
    risk_level: int = Field(ge=1, le=10)  # Pydantic 검증: 1~10 사이
 
agent = Agent(
    'openai:gpt-4o',
    deps_type=SupportDeps,
    output_type=SupportResult,
    system_prompt='당신은 은행 고객 지원 에이전트입니다. 부정 거래 위험을 평가하세요.',
)
 
@agent.tool
async def get_customer_balance(ctx: RunContext[SupportDeps]) -> float:
    return await ctx.deps.db.get_balance(ctx.deps.customer_id)
 
@agent.tool
async def get_recent_transactions(ctx: RunContext[SupportDeps]) -> list[dict]:
    return await ctx.deps.db.get_transactions(ctx.deps.customer_id, limit=10)
 
async def main():
    result = await agent.run(
        '카드를 잃어버렸어요, 해외에서 모르는 결제가 있어요',
        deps=SupportDeps(customer_id=123, db=db_conn)
    )
 
    # 완전한 타입 안전 접근 — IDE 자동완성, mypy 통과
    if result.output.block_card:
        await card_service.block(customer_id=123)
    print(f"위험 등급: {result.output.risk_level}/10")  # int 보장
 
asyncio.run(main())
포인트 설명
block_card: bool LLM이 어떤 형태로 응답해도 Python bool로 강제 변환·검증
risk_level: int = Field(ge=1, le=10) 범위 검증까지 Pydantic이 담당, 범위 이탈 시 재시도 트리거
RunContext[SupportDeps] 툴 함수 안에서 타입 추론 완벽히 작동, 전역 상태 불필요

예시 2: FastAPI 엔드포인트와의 통합

실무에서 자주 맞닥뜨리는 상황인데, FastAPI 라우터에 LLM 분석을 붙이면서 별도 직렬화 코드 없이 깔끔하게 연결할 수 있습니다. FastAPI의 response_model과 에이전트의 output_type이 동일한 Pydantic 모델을 공유하기 때문에 가능한 패턴입니다.

python
from fastapi import FastAPI
from pydantic import BaseModel, Field
from pydantic_ai import Agent
 
class ReviewRequest(BaseModel):
    text: str
    language: str = 'ko'
 
class ReviewAnalysis(BaseModel):
    sentiment: str
    key_issues: list[str]
    recommended_action: str
    priority: int = Field(ge=1, le=5)
 
app = FastAPI()
agent = Agent('anthropic:claude-3-7-sonnet', output_type=ReviewAnalysis)
 
@app.post('/analyze-review', response_model=ReviewAnalysis)
async def analyze_review(request: ReviewRequest) -> ReviewAnalysis:
    result = await agent.run(
        f"다음 {request.language} 리뷰를 분석해주세요: {request.text}"
    )
    return result.output  # 이미 검증된 Pydantic 모델 그대로 반환

result.output이 이미 ReviewAnalysis 타입으로 검증된 객체이기 때문에 FastAPI가 그대로 직렬화합니다. API 경계에서 별도 변환 코드가 전혀 필요 없다는 게 이 패턴의 핵심입니다.


비용 없는 테스트

솔직히 이게 Pydantic AI에서 가장 마음에 드는 기능입니다. TestModel을 쓰면 실제 LLM API를 전혀 호출하지 않고 에이전트 로직을 테스트할 수 있습니다.

TestModel은 Pydantic 모델의 각 필드 타입에 맞는 기본값을 자동 생성합니다. str은 빈 문자열(""), int는 0, bool은 False, float는 0.0 식으로요. 덕분에 LLM이 "어떤 값을 반환할지"가 아니라 "에이전트가 올바른 타입 계약을 지키는지"를 테스트하는 데 집중할 수 있습니다.

python
import pytest
from unittest.mock import AsyncMock
from pydantic_ai.models.test import TestModel
 
# pytest-asyncio 필요: pip install pytest-asyncio
# @pytest.mark.asyncio — async 테스트 함수를 pytest가 실행할 수 있게 해주는 데코레이터
 
@pytest.mark.asyncio
async def test_support_result_schema_contract():
    mock_db = AsyncMock()
    mock_db.get_balance.return_value = 50000.0
    mock_db.get_transactions.return_value = [
        {"amount": 9999.99, "country": "NG", "merchant": "unknown"}
    ]
 
    with agent.override(model=TestModel()):
        result = await agent.run(
            '해외 고액 결제가 수상해요',
            deps=SupportDeps(customer_id=456, db=mock_db)
        )
 
    # TestModel 반환값: block_card=False, risk_level=0, support_advice=""
    assert isinstance(result.output, SupportResult)    # 타입 계약 검증
    assert isinstance(result.output.block_card, bool)  # bool 타입 보장
    # Field(ge=1, le=10) 제약 → risk_level=0은 실패하므로 실제 검증 범위도 확인 가능

CI에서 LLM API 비용 걱정 없이 타입 계약 위반을 잡아낼 수 있습니다. 실제 LLM 동작 검증이 필요할 때는 pydantic-evals로 별도 평가 파이프라인을 구성하는 것도 좋은 접근입니다.


어떤 프레임워크를 선택할까?

도입부에서 LangChain·LangGraph 비교를 예고했으니 솔직하게 정리해봅니다. "무조건 Pydantic AI가 낫다"는 이야기가 아니라, 상황에 따라 선택이 달라집니다.

상황 추천 프레임워크 이유
LLM 출력을 프로그래밍 방식으로 소비 (API 응답, DB 저장) Pydantic AI 타입 안전성, mypy 통합, FastAPI 시너지
RAG 파이프라인, 다양한 문서 로더 필요 LangChain 풍부한 로더·임베딩·벡터 DB 생태계
분기·순환이 많은 복잡한 멀티에이전트 LangGraph 성숙한 그래프 기반 상태 제어
역할 기반 멀티에이전트 협업 (리서처·작성자·리뷰어) CrewAI 역할 추상화, 사람이 읽기 쉬운 구성
경량 Structured Output만 필요 Instructor Pydantic AI보다 가볍고, LLM 라이브러리에 직접 패치 방식

실무에서는 섞어 쓰는 것도 충분히 현실적인 선택입니다. 문서 청킹과 임베딩은 LangChain으로, 최종 LLM 출력 처리는 Pydantic AI로 구성하는 아키텍처도 자연스럽게 동작합니다.

pydantic-graph가 필요한 시점

Pydantic AI 내에서도 복잡한 워크플로우가 필요할 때 pydantic-graph 모듈로의 전환을 고려해볼 수 있습니다. 대략 다음 기준이 참고가 됩니다.

  • 분기 조건이 2개 이상이고 각 분기마다 다른 툴을 호출해야 할 때
  • 단계 사이에 상태를 저장하고 재개(resume)해야 할 때
  • 특정 툴 호출에 사람의 승인(Human-in-the-Loop)이 필요할 때

다만 LangGraph에 비해 아직 성숙도가 낮은 편이라, 복잡한 그래프 워크플로우라면 LangGraph 병행 검토를 권장합니다.

pydantic-graph — 상태 머신 기반 복잡한 멀티스텝 워크플로우를 지원하는 Pydantic AI의 선택적 모듈. LangGraph의 그래프 기반 접근과 유사하지만 아직 성숙도는 낮은 편입니다.


장단점 분석

장점

항목 내용
런타임 전 오류 감지 mypy/pyright가 에이전트 로직의 타입 불일치를 개발 단계에서 잡아냄
자동 재시도·검증 LLM이 잘못된 JSON을 반환하면 오류 피드백과 함께 재시도, 최종 실패 시 명확한 예외 발생
비용 없는 테스트 TestModel과 deps 모킹으로 실제 API 비용 없이 단위 테스트 가능
모델 독립성 25개 이상 LLM 지원, 비즈니스 로직 변경 없이 제공자 전환 가능
FastAPI 친화성 동일 팀 제작, 동일 DI 패턴, 에코시스템 통합이 자연스러움
순수 Python 별도 DSL 없이 async/await 네이티브 지원

단점 및 주의사항

항목 내용 대응 방안
Python 전용 TypeScript/Go 팀은 사용 불가 JS 생태계는 Mastra 등 대안 검토
작은 커뮤니티 GitHub 스타 ~16.5K, LangChain(100K+) 대비 템플릿·예제 부족 공식 문서와 예제 저장소가 잘 정리되어 있어 보완 가능
관찰성 유료 의존 프로덕션 트레이싱은 Pydantic Logfire(유료)에 의존 OpenTelemetry 직접 연결 설정으로 대체 가능
복잡한 멀티에이전트 정교한 분기·순환 워크플로우는 pydantic-graph 필요, LangGraph보다 성숙도 낮음 복잡한 그래프 워크플로우는 LangGraph 병행 검토
툴 호출 비효율 일부 시나리오에서 툴 호출 반복이 많아 토큰 비용 상승 툴 설계 시 호출 수 최소화, 캐싱 레이어 추가 고려

OpenTelemetry — 벤더 독립적인 관찰성(Observability) 표준. Pydantic Logfire의 기반으로, 직접 연결하면 Datadog·Grafana 같은 기존 인프라와 통합할 수 있습니다.

실무에서 가장 흔한 실수

  1. output_type 없이 에이전트를 정의하는 것 — 저희 팀도 처음에 문자열 응답으로 시작했다가 나중에 Pydantic 모델로 마이그레이션하면서 예상보다 훨씬 많은 코드를 손봤습니다. output_type은 처음부터 선언해두는 게 훨씬 낫습니다. 추가 비용은 Pydantic 모델 정의 몇 줄이 전부입니다.

  2. 의존성을 전역 상태로 관리하는 것 — deps_type을 쓰지 않고 글로벌 DB 커넥션을 참조하면 테스트 격리가 불가능해집니다. deps를 통한 명시적 주입 패턴이 유지보수성에 훨씬 유리합니다.

  3. RAG 파이프라인 전체를 Pydantic AI로 구축하려는 것 — Pydantic AI는 에이전트 레이어에 특화되어 있습니다. 다양한 문서 로더·임베딩 파이프라인이 필요한 RAG는 LangChain이 더 풍부한 생태계를 갖고 있습니다. 두 가지를 혼합해서 쓰는 것도 충분히 현실적인 선택입니다.


마치며

output_type → deps_type → TestModel 세 가지 패턴을 직접 써볼 수 있는 코드와 함께 살펴봤습니다. 단일 에이전트 수준에서는 이 조합만으로 대부분의 프로덕션 타입 안전성 요구사항을 충족할 수 있습니다. 자연스럽게 남는 질문은 하나입니다 — 이 에이전트가 프로덕션에서 정확히 무슨 일을 하고 있는지, 토큰 비용은 어디서 오는지.

Python 백엔드 위주로 작업하고 LLM 출력을 프로그래밍 방식으로 소비해야 하는 프로젝트라면 지금 바로 시작해볼 수 있습니다.

  1. pip install pydantic-ai로 설치 → 기존에 딕셔너리로 처리하던 LLM 응답 하나를 Pydantic BaseModel로 교체해볼 수 있습니다. output_type 파라미터 하나만 추가하면 IDE 자동완성이 즉시 달라집니다.

  2. deps_type 도입 → DB 커넥션이나 HTTP 클라이언트를 전역 상태 대신 명시적 의존성으로 리팩터링해볼 수 있습니다. TestModel()을 활용한 단위 테스트 작성이 자연스럽게 가능해집니다.

  3. FastAPI 엔드포인트 하나에 연결 → response_model과 output_type을 같은 Pydantic 모델로 공유하면 직렬화 코드 없이 깔끔하게 통합되는 패턴을 바로 확인할 수 있습니다.


참고 자료

공식 문서

  • Pydantic AI 공식 문서
  • Pydantic AI GitHub 리포지터리
  • Pydantic Logfire AI Observability 공식 문서

비교 분석

  • Pydantic AI — Thoughtworks Technology Radar
  • PydanticAI v1: The Type-Safe Agent Framework Rewriting the Python Agent Stack — AgentMarketCap
  • Pydantic AI vs LangChain 2026: Type-Safe or Flexible — Which Wins? — Kunal Ganglani
  • LangChain vs PydanticAI for building an AI Agent — Medium
  • The 2026 AI Agent Framework Decision Guide — DEV Community

심화 학습

  • Pydantic AI: Build Type-Safe LLM Agents in Python — Real Python
  • Pydantic AI Tutorial: How I Build Type-Safe AI Agents That Actually Work in Production — DEV Community
  • Building AI Agents in Python with Pydantic AI — MachineLearningMastery
  • Pydantic AI and MCP: Building Production-Grade AI Applications — Medium
  • What is Pydantic AI? Type-Safe Agent Framework in 2026 — FutureAGI
#PydanticAI#Python#LLM#타입안전성#FastAPI#의존성주입#StructuredOutput#AI에이전트#LangChain#OpenTelemetry
공유하기

목차

핵심 개념타입 안전 출력 —의존성 주입 — DB, HTTP 클라이언트를 타입 안전하게 툴에 전달모델 어그노스틱 — 벤더 교체가 한 줄실전 적용예시 1: 뱅킹 고객 지원 에이전트예시 2: FastAPI 엔드포인트와의 통합비용 없는 테스트어떤 프레임워크를 선택할까?장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략
AI

Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략

AI 에이전트를 프로덕션에 올리고 나서 처음 청구서를 받았을 때의 충격을 아직도 기억한다. 단순 챗봇이라면 예측 가능했을 텐데, 에이전트는 달랐다. 버그 하나 고치는 데 컨텍스트가 수만 토큰씩 쌓이고, 실패하면 그 비용 그대로 날리고 처음부터 다시. 단일 실행이 예상의 열 배를 넘어가는...

2026년 06월 07일읽는 데 24분
AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기
AI

AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기

솔직히 말하면, 저도 처음 이 개념을 들었을 때 "그게 실제로 돌아가?" 싶었습니다. 에이전트가 혼자 코드를 쓰는 것도 신기한데, 그 코드를 또 다른 에이전트가 검토하고 피드백까지 남긴다니. 그런데 2026년 현재, 이건 이미 실무에서 쓰이는 이야기입니다. 에이전틱 코딩 루프(Ag...

2026년 06월 07일읽는 데 20분
AI 코딩 에이전트가 바꾸는 개발팀 구조: 오케스트레이터로 전환하는 법
AI

AI 코딩 에이전트가 바꾸는 개발팀 구조: 오케스트레이터로 전환하는 법

솔직히 말하면, 처음 "코딩 에이전트 도입 후 팀을 재편한다"는 말을 들었을 때 그냥 과장된 마케팅 문구라고 생각했습니다. AI 보조 도구가 코드 완성 속도를 높여준다는 건 체감하고 있었는데, 팀 구조 자체가 바뀐다고? 좀 과했죠. 그런데 2026년 현재, 팀 내에서 그 변화가 실...

2026년 06월 12일읽는 데 25분
사내 REST API를 LLM이 직접 호출하게 만드는 법: TypeScript MCP 서버 구현과 Gateway 패턴
AI

사내 REST API를 LLM이 직접 호출하게 만드는 법: TypeScript MCP 서버 구현과 Gateway 패턴

팀에서 AI 에이전트를 도입하려다 "그래서 우리 내부 API는 어떻게 연결해?"라는 질문에 막혀본 적 있으신가요? 저도 처음엔 LLM에게 사내 API 문서를 통째로 붙여넣거나, 직접 fetch 코드를 프롬프트에 넣어주는 방식을 시도했습니다. 잘 됩니다—딱 한 번만요. 그 다음 요청부터는...

2026년 06월 07일읽는 데 19분
에이전틱 AI 설계 7대 패턴
AI

에이전틱 AI 설계 7대 패턴

Use + ReAct | KB, 티켓 DB 등 외부 시스템 반복 조회 | | 응답 작성 | 응대 에이전트 | Reflection | 발송 전 어조·정확도 자체 검토 | | 에스컬레이션 | 전체 파이프라인 | Human-in-the-Loop | 자동 해결 불가 시 담당자 연결 | ...

2026년 06월 06일읽는 데 9분
오픈웨이트 vs 클로즈드 AI 2026: 벤치마크 격차가 좁혀진 지금, 선택의 기준이 바뀌었다
AI

오픈웨이트 vs 클로즈드 AI 2026: 벤치마크 격차가 좁혀진 지금, 선택의 기준이 바뀌었다

솔직히 말하면, 1년 전까지만 해도 저는 클로즈드 모델이 한동안은 압도적 우위를 유지할 거라 생각했습니다. GPT-4급 성능을 내려면 OpenAI API 키를 꽂는 게 당연한 수순이었고, 오픈웨이트 진영은 "쓸 만은 하지만 아직..." 수준에 머물렀으니까요. 그런데 그 확신이 흔들리기 ...

2026년 06월 06일읽는 데 23분