2025 . 오은

repo

polling 적용기

🧩 문제 정의

프로젝트에서 OpenAI의 image edit 기능을 활용한 이미지 생성을 구현하던 중,
Amplify 환경에서 API 요청이 504 Gateway Timeout 에러를 반환하는 문제에 직면했습니다.

  • Vercel 배포 환경에서는 요청이 정상적으로 처리되지만,
  • Amplify에서는 Lambda@Edge의 timeout 제한으로 인해 SSR 함수가 29초를 넘기면 강제 종료
  • 특히 이미지 생성은 평균 40초 이상 걸리는 작업이라, Amplify 환경에서 실행 불가능

🛠 해결 과정

1. 구조 분리 (백엔드 역할 Next.js 앱 도입)

  • 이미지 생성 로직을 별도의 Next.js 앱에 분리해 ‘백엔드 역할’로 사용
  • 클라이언트는 이 앱에 요청을 보내고, 실제 작업은 해당 앱에서 수행

2. Polling 방식 적용

  • 첫 번째 API는 클라이언트 요청을 받아 즉시 202 응답을 반환하고,
    내부적으로 두 번째 API를 fire-and-forget 방식으로 호출
  • 두 번째 API는 실제로 시간이 오래 걸리는 OpenAI image edit 요청을 수행하고 DB에 결과 저장

3. 클라이언트에서 Polling 처리

  • 클라이언트는 202 응답을 받은 후 15초 대기, 이후 5초 간격으로 최대 20회 status 확인
  • 결과가 completed 상태가 되면 사용자에게 결과 페이지로 전환

4. 기타 개선 사항

  • Amplify에서 SSR을 유도하기 위해 middleware.ts 및 app/api/.../route.ts 외에
    pages/api/ping.ts도 추가하여 SSR Lambda가 확실히 생성되도록 처리

  • fire-and-forget 호출 시 .catch()로 예외 누락 방지

  • 이건 try catch 든 .catch 든 쓰면 fire-and-forget 이 안되는 것 확인!!!

  • 아래처럼 꼼수로 처리했음

import api from "@/apis/axios";
import type { GenResponse } from "@/types/iffy.types";
import { type NextRequest, NextResponse } from "next/server";

function awaitTime(ms: number) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function GET(
	request: NextRequest,
): Promise<NextResponse<GenResponse>> {

	const searchParams = request.nextUrl.searchParams;
	const query = searchParams.get("id");
	
	  
	if (!query) {
		return NextResponse.json(
			{ status: "error", message: "No id provided" },
			{ status: 400 },
		);
	}

	// 원본 요청에서 쿠키 헤더 가져오기
	const cookieHeader = request.headers.get("cookie");
	
	// Fire-and-Forget 요청 시 쿠키 헤더 전달
	api.get(`/api/backtask?id=${query}`, {
		headers: {
			...(cookieHeader && { Cookie: cookieHeader }),
		},
	});

	// 여기가 꼼수부분.. 
	// await 을 하지 않으니 api.get 이 되기도 전에 
	// 리스폰스로 넘어가버려 실행이 안되는 문제 임시 해결용
	// upstash 등을 쓰라고 하지만.. 간단히 처리해보고 싶었기 때문...
	await awaitTime(1000);

	return NextResponse.json(
		{ status: "success", message: "IFFY 생성 요청 전달" },
		{ status: 202 },
	);
}

💡 배운 점 & 인사이트

  • Amplify의 SSR 구조는 Lambda@Edge + CloudFront 기반이며, timeout은 최대29초로 고정되어 있음
    → 장시간 작업은 구조적으로 불가능하며, 우회 전략 필요
  • Polling 구조는 Amplify와 같은 서버리스 환경에서 유용한 비동기 처리를 가능하게 함
  • OpenAI image edit API는 아직 응답 후 완료 통지를 지원하지 않아,
    webhook이나 status 확인 API가 생기면 개선 여지가 큼
  • Amplify는 cold start가 Vercel보다 다소 느리게 체감됨 → 실시간성이 중요한 앱에서는 고려 필요
  • 사실 이 모든게 프론트만 가지고 어쨌든 해결 해보려는 일종의 트릭이므로 최종적인 전략은 아님!
  • poliing 자체도 계속 DB를 확인하는 구조로 되어 있는데, supabase 사용중이므로 그냥 realtime 쓰는 것도 좋을 듯!