배포 환경에서 ChatGPT API custom 헤더가 사라지는 문제 해결기

ding·2025년 8월 28일

문제 상황

최근 우리 팀은 ChatGPT API를 활용해 스트리밍 응답을 받아오는 기능을 구현했습니다.
로컬 개발 환경(도커 + Nginx Reverse Proxy + HTTPS)에서는 정상적으로 동작했습니다.

  • 로컬 요청/응답 헤더에는 conversation-id 가 잘 포함됨
  • 따라서 Following Responses도 정상적으로 이어짐

하지만 Azure App Service 배포 환경에서는 이상한 문제가 발생했습니다.

  • 요청 시 custom header(conversation-id)가 빠져서 전송됨
  • 응답에도 Following Responses 가 오지 않아, 이전 대화 내용을 전혀 기억하지 못함

즉, 대화 맥락 유지 기능이 배포 환경에서 깨졌던 겁니다.

원인 분석

단순 헤더 누락 문제는 아니었다

처음에는 Uvicorn/Nginx 커스텀 헤더를 전달하지 않는 문제일 거라고 생각했습니다.
하지만 로컬 리버스 프록시(Nginx) 환경에서는 잘 동작했기 때문에 배포 환경에 특화된 문제임을 알 수 있었습니다.

브라우저에서 아예 conversation-id 를 안 붙임

개발자 도구(Network 탭)로 확인해보니, 배포 환경에서는 요청을 보낼 때부터 conversation-id 가 없었습니다.
즉, 서버에서 잘린 게 아니라 브라우저가 아예 빼고 요청을 보낸 것이었죠.

CORS Preflight 응답 문제

브라우저는 커스텀 헤더를 포함한 요청을 보낼 때 반드시 OPTIONS(Preflight) 요청을 먼저 보냅니다.
이때 서버가 응답에 다음 헤더를 내려주어야 합니다.

Access-Control-Allow-Headers: Conversation-id, Authorization, Content-type

만약 이 항목이 없다면 브라우저는 본 요청에서 해당 헤더를 제거해버립니다.
Azure App Service 기본 CORS 설정에서는 conversation-id 같은 커스텀 헤더가 허용되지 않아, 바로 이 상황이 발생했던 겁니다.

해결 방법

Azure App Service CORS 한계

App Service Portal의 CORS 설정은 단순합니다:

  • Allowed Origins
  • Request Credentials (Enable/Disable)

즉, Allowed Headers를 직접 지정할 수 없기 때문에 커스텀 헤더 허용이 불가능합니다.
따라서 Portal에서 CORS를 관리하는 접근은 한계가 있었습니다.

FastAPI에서 직접 CORS 제어

해결책은 간단했습니다.
=> StreamingResponse에서 직접 Conversation-Id 헤더를 내려주는 방식입니다.

from fastapi.responses import StreamingResponse

@app.post("/chat")
async def chat_endpoint():
    return StreamingResponse(
        response_generator(),
        status_code=status.HTTP_200_OK,
        headers={
        	...headers, #기본 헤더
            "Conversation-Id": conversation_id,  # 커스텀 헤더 직접 설정
        },
    )

이로 인해 브라우저가 preflight에 의존하지 않고도 응답 헤더에서 Conversation-Id 를 받을 수 있었고, 클라이언트가 해당 값을 다시 다음 요청에 붙여주면서 대화 맥락이 정상적으로 이어지게 되었습니다.

배운 점

CORS는 브라우저 단에서 커스텀 헤더를 아예 제거할 수 있다
→ 서버 로그만 보면 원인을 찾기 어렵다.

App Service CORS 설정은 단순하다
→ Allowed Headers를 직접 지정할 수 없으므로 세밀한 제어가 필요하다면 앱 레벨에서 처리해야 한다.

StreamingResponse에서 응답 헤더를 직접 내려주는 방법도 좋은 대안이다.
이 방법을 쓰면 커스텀 헤더를 안전하게 전달할 수 있고, 클라이언트는 그대로 이어받아 다음 요청에 사용할 수 있다.

결론

이번 이슈는 "배포 환경에서 커스텀 헤더(conversation-id)가 누락되는 문제"였지만, 본질은 CORS Preflight 응답에서 커스텀 헤더가 허용되지 않아 브라우저가 본 요청에서 제거한 것이었습니다.

0개의 댓글