⚙️Backend

AI 이미지 생성 API: '고치기'와 '새로 그리기' 로직 충돌 해결기

AI 이미지 생성 API에서 발생한 '고치기'와 '새로 그리기' 로직 충돌 문제. 상태 의존성을 제거하고 순수 함수형으로 리팩토링하여 해결한 과정을 공유합니다.

📅 2026년 8월 17일·📖 3분 읽기·👁 4

이미지 스튜디오에서 '고치기(Inpainting)'와 '새로 그리기(Generation)' 요청이 섞일 때마다 preserve 옵션이 엉뚱하게 적용되는 문제가 발생했다. 분명히 새로 그리는 상황인데 이전 이미지를 유지하려는 로직이 충돌하면서, 2026년 8월 17일 현재 서비스 안정성에 빨간불이 켜졌다.

시도와 함정

처음에는 image_intent_service가 요청의 의도를 제대로 파악하지 못한다고 판단했다. 그래서 매 요청마다 promptmask의 존재 여부를 강제로 체크하는 로직을 추가했다.

// 초기 시도: 단순히 마스크 유무로 판단하려던 로직
const determineIntent = (request: ImageRequest) => {
  if (request.mask) return 'INPAINTING';
  return 'GENERATION';
};

하지만 이 방식은 프론트엔드에서 상태를 초기화하지 않았을 때 백엔드와 상태가 꼬이는 문제를 해결하지 못했다. 3시간 삽질 끝에 발견한 건, 클라이언트가 보내는 preserve 옵션이 이전 작업의 잔상으로 남아 다음 요청까지 오염시키고 있었다는 점이다.

원인

원인은 의도 판단 로직과 상태 관리 시스템의 결합도가 너무 높았기 때문이다. image_intent_service가 요청을 받을 때 내부 상태값을 참조하고 있었는데, 이 상태값이 이전 작업의 preserve 설정값을 그대로 들고 있었다. 즉, 새로 그리기 요청임에도 불구하고 이전의 preserve: true 값이 유지되어 충돌이 발생한 것이다.

해결

해결책은 의도 판단 로직을 상태 참조형에서 순수 함수형으로 분리하고, 요청 시점에 명시적으로 preserve 옵션을 강제 초기화하도록 변경했다.

// 수정된 로직: 상태 의존성을 제거하고 명시적 옵션 부여
interface IntentResult {
  type: 'INPAINTING' | 'GENERATION';
  preserve: boolean;
}

const resolveImageIntent = (request: ImageRequest): IntentResult => { const isMaskProvided = !!request.mask && request.mask.length > 0;

return { type: isMaskProvided ? 'INPAINTING' : 'GENERATION', preserve: isMaskProvided ? (request.preserve ?? true) : false }; };

이제 resolveImageIntent는 외부 상태에 의존하지 않고 오직 현재 들어온 request 객체만을 기반으로 판단한다. 새로 그리기일 경우 preserve를 무조건 false로 고정함으로써 모순을 원천 차단했다.

결과

  • 생성 로직의 판단 기준이 명확해지면서 preserve 옵션 충돌이 완전히 사라졌다.
  • API 요청 시 불필요한 상태 공유가 제거되어 디버깅 로그가 훨씬 깔끔해졌다.
  • 리뉴얼 기록을 통해 의사결정 과정을 문서화하여 향후 유사한 로직 변경 시 발생할 사이드 이펙트를 최소화했다.

정리 — 같은 함정 안 빠지려면

  • [ ] 의도 파악 로직에 외부 상태(State)를 참조하게 하지 말 것.
  • [ ] 옵션값(preserve 등)은 요청 객체 내에서 명시적으로 초기화되는지 확인할 것.
  • [ ] 프론트엔드와 백엔드의 상태 동기화가 깨졌을 때, 서버 측에서 안전한 기본값(Default)을 강제하는지 체크할 것.

태그

#AI이미지생성#API개발#Inpainting#상태관리#TypeScript#백엔드개발#기술회고