kt-event-marketing-fe/CORS_FIX.md
cherry2250 6cccafa822 AI 이미지 생성 기능 완성 및 실제 API 연동
주요 변경사항:
- Step flow 통합: localStorage 기반 eventId 사용
- 자동 이미지 생성: 이미지 없을 시 자동 생성 트리거
- 진행률 바 추가: 0-100% 진행률 표시
- 동적 로딩 메시지: 단계별 메시지 업데이트
- Next.js 15 API routes 수정: params를 Promise로 처리
- 실제 배포 API 연동: Content API 서버 URL 설정

기술 세부사항:
- API proxy routes 추가 (CORS 우회)
- 2초 폴링 메커니즘 (최대 60초)
- 환경변수: NEXT_PUBLIC_CONTENT_API_URL 설정
- CDN URL 디버그 오버레이 제거

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 23:08:57 +09:00

6.0 KiB

CORS 문제 해결 방법

문제 상황

프론트엔드(http://localhost:3000)에서 백엔드 Content API(http://localhost:8084)를 직접 호출하면 CORS(Cross-Origin Resource Sharing) 에러가 발생했습니다.

에러 메시지

Network Error
AxiosError: Network Error
  code: "ERR_NETWORK"

원인 분석

# CORS preflight 요청 테스트
curl -X OPTIONS http://localhost:8084/api/v1/content/images/generate \
  -H 'Origin: http://localhost:3000' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type'

# 결과: HTTP/1.1 403 Forbidden
# Invalid CORS request

백엔드 서버가 http://localhost:3000 origin에서의 CORS 요청을 허용하지 않음.


해결 방법: Next.js API Proxy

백엔드 CORS 설정을 수정하는 대신, Next.js API Routes를 프록시로 사용하여 CORS 문제를 우회했습니다.

아키텍처

[Browser]
  ↓ (Same-Origin Request)
[Next.js Frontend: localhost:3000]
  ↓ [Next.js API Proxy: /api/content/*]
  ↓ (Server-to-Server Request, CORS 무관)
[Content API Backend: localhost:8084]

구현 파일

1. 이미지 생성 프록시 (/api/content/images/generate/route.ts)

export async function POST(request: NextRequest) {
  const body = await request.json();

  const response = await fetch('http://localhost:8084/api/v1/content/images/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });

  return NextResponse.json(await response.json());
}

URL 매핑:

  • Frontend: POST /api/content/images/generate
  • Backend: POST http://localhost:8084/api/v1/content/images/generate

2. Job 상태 조회 프록시 (/api/content/images/jobs/[jobId]/route.ts)

export async function GET(request: NextRequest, { params }: { params: { jobId: string } }) {
  const { jobId } = params;

  const response = await fetch(`http://localhost:8084/api/v1/content/images/jobs/${jobId}`, {
    method: 'GET',
    headers: { 'Content-Type': 'application/json' },
  });

  return NextResponse.json(await response.json());
}

URL 매핑:

  • Frontend: GET /api/content/images/jobs/{jobId}
  • Backend: GET http://localhost:8084/api/v1/content/images/jobs/{jobId}

3. 이미지 목록 조회 프록시 (/api/content/events/[eventDraftId]/images/route.ts)

export async function GET(request: NextRequest, { params }: { params: { eventDraftId: string } }) {
  const { eventDraftId } = params;
  const { searchParams } = new URL(request.url);

  let url = `http://localhost:8084/api/v1/content/events/${eventDraftId}/images`;
  if (searchParams.get('style')) url += `?style=${searchParams.get('style')}`;
  if (searchParams.get('platform')) url += `&platform=${searchParams.get('platform')}`;

  const response = await fetch(url, {
    method: 'GET',
    headers: { 'Content-Type': 'application/json' },
  });

  return NextResponse.json(await response.json());
}

URL 매핑:

  • Frontend: GET /api/content/events/{eventDraftId}/images?style=SIMPLE&platform=INSTAGRAM
  • Backend: GET http://localhost:8084/api/v1/content/events/{eventDraftId}/images?style=SIMPLE&platform=INSTAGRAM

클라이언트 코드 변경

Before (직접 백엔드 호출 - CORS 에러 발생)

const CONTENT_API_BASE_URL = 'http://localhost:8084';

export const contentApiClient = axios.create({
  baseURL: CONTENT_API_BASE_URL,
});

// ❌ CORS Error
await contentApiClient.post('/api/v1/content/images/generate', request);

After (Next.js API Proxy 사용 - CORS 우회)

const CONTENT_API_BASE_URL = '/api/content';  // Same-origin request

export const contentApiClient = axios.create({
  baseURL: CONTENT_API_BASE_URL,
});

// ✅ Works! (Same-origin → Server-side proxy → Backend)
await contentApiClient.post('/images/generate', request);

장점

프론트엔드 수정만으로 해결: 백엔드 CORS 설정 변경 불필요 Same-Origin 정책 준수: 브라우저는 같은 도메인으로 인식 서버 간 통신: Next.js 서버에서 백엔드 호출 (CORS 무관) 보안 강화: 백엔드 URL을 클라이언트에 노출하지 않음 환경 변수 활용: NEXT_PUBLIC_CONTENT_API_URL로 배포 환경 대응


프로덕션 배포 시 고려사항

환경 변수 설정

# .env.local (개발 환경)
NEXT_PUBLIC_CONTENT_API_URL=http://localhost:8084

# .env.production (프로덕션 환경)
NEXT_PUBLIC_CONTENT_API_URL=https://api.production.com

프록시 코드에 적용

const CONTENT_API_BASE_URL = process.env.NEXT_PUBLIC_CONTENT_API_URL || 'http://localhost:8084';

const response = await fetch(`${CONTENT_API_BASE_URL}/api/v1/content/images/generate`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
});

타임아웃 설정

const response = await fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
  signal: AbortSignal.timeout(120000), // 120초 타임아웃
});

테스트 방법

1. 개발 서버 실행

npm run dev

2. 브라우저에서 테스트

http://localhost:3000/events/create?event-creation.step=contentPreview

3. 네트워크 탭 확인

브라우저 개발자 도구 → Network 탭에서 다음 요청 확인:

POST http://localhost:3000/api/content/images/generate  (Status: 202)
GET  http://localhost:3000/api/content/images/jobs/job-xxxxx  (Status: 200)
GET  http://localhost:3000/api/content/events/7777/images  (Status: 200)

모두 Same-Origin 요청이므로 CORS 에러 없음!


참고 자료