AI Debugging/2026.05.03/8 min read

AI JSON 응답이 흔들릴 때 sanitize 계층 만들기

AI 리포트가 화면에서 기대하는 형태와 다르게 돌아올 때, 응답 계약과 정규화 계층으로 제품을 지킨 기록입니다.

AIJSONStability

Code notes

코드에서 확인한 구현 포인트

배포 기준 2026.05.03

관련 파일

apps/web/lib/ai-report.tsapps/web/app/api/reports/[sessionId]/route.tsapps/web/lib/recommendations.ts

구현 메모

AI 응답은 response_format으로 JSON 객체를 요구하지만, 서버에서 다시 sanitizeAiPayload를 거쳐 필드와 문장을 정규화한다.

내부 사진 reference나 화면에 노출되면 어색한 값은 사용자 문장에서 제거해 제품 문장으로 다듬는다.

AI 호출이나 파싱이 실패해도 기본 리포트 구조를 반환할 수 있도록 fallback을 둔다.

AI 응답은 문장보다 계약이 먼저다

피부 리포트 화면은 oneLiner, resultBadge, keyFindings, focus, actions, warnings 같은 정해진 필드를 기대한다. 그런데 AI 응답은 프롬프트를 잘 써도 가끔 필드가 빠지거나, 기대한 배열 대신 문장이 들어오거나, 내부 참조명을 그대로 말하는 식으로 흔들릴 수 있다.

이 문제는 단순히 프롬프트를 더 강하게 쓰는 것으로 끝나지 않았다. 화면이 AI 응답을 그대로 믿지 않도록, 서버에서 한 번 더 제품이 이해하는 형태로 정리하는 계층이 필요했다.

response_format과 sanitize를 같이 둔 이유

먼저 AI 호출에서는 JSON 객체 응답을 요구했다. 하지만 JSON으로 온다고 해서 모든 값이 안전한 것은 아니다. 그래서 응답을 파싱한 뒤 sanitizeAiPayload에서 badge, focus, finding, action 같은 필드를 정규화했다.

예를 들어 화면에 보여주면 어색한 사진 reference나 내부 라벨은 사용자 문장에 남지 않도록 걸러냈다. AI는 분석을 돕지만, 최종 사용자 경험의 책임은 앱이 가져야 하기 때문이다.

실패해도 리포트는 열려야 한다

AI 호출이나 파싱이 실패할 때 리포트 전체가 무너지는 것은 좋지 않다. 사용자는 분석을 기다렸고, 최소한 기본 결과와 다음 행동은 받아야 한다.

그래서 서버는 AI payload가 비어 있거나 일부 필드가 이상해도 기본 구조를 만들어 반환할 수 있게 했다. 완벽한 AI 문장보다 중요한 것은 화면이 깨지지 않는 안정적인 리포트 흐름이었다.

남겨둔 원칙

AI 기능은 ‘잘 나올 때’보다 ‘이상하게 나올 때’의 처리가 제품 품질을 결정한다. 프롬프트, 응답 포맷, sanitize, fallback을 한 묶음으로 봐야 한다.

이후 AI 기능을 추가할 때도 화면이 직접 AI 응답에 기대지 않고, 서버가 한 번 제품 언어로 번역해주는 구조를 유지하기로 했다.

Keep reading

다른 글 이어서 보기

전체 글 보기