API 설계 오류에서 기인한 CORS와 라우팅 문제

햄스터아저씨·2025년 3월 19일

API 설계를 미진하게 했다가, 다시는 밟지 않을 줄 알았던 cors 를 밟고,
나의 cors 이해도가 낮았다는 것을 깨닫고 이번에 다시 정리함

2개의 문제가 연속으로 발생했는데, 두개를 구분하지 못했다.
첫번째 문제는 CORS 미설정
두번째 문제는 라우팅 api 설계 오류

사실 API 설계미스로 인해, CORS 문제가 발생한 것.
어떻게 상황이 전개되었는지 정리하면
1. FE: http://nginx-ip:nginx-external-port/api/items 를 Nginx 에 요청
2. Nginx: 도커 포트포워딩 설정에 따라 포트가 제거되고 http://server/api/items 를 받아 이를 Backend로 포워딩. 이 때 /api 는 제거하고 http://server-hostname:server-exteral-port/items 로 포워딩
3. BE: 도커 포트포워딩 설정에 따라 포트가 변경되고 http://server:server-internal-port/items 로 전달. 내부 라우팅은 아래와 같이 Tailing Slash가 있어야 하는 상황

```
@app.get("/items/")
```
  • BE 라우팅 정의에 없는 /items 를, Tailing Slash가 붙은 url로 리디렉션을 보냄 (307 Temporary Redirect), 이 때 리디렉션 되는 경로는 http://server/items/

4. FE: http://server/items/로 요청. 해당하는 URL이 없으므로 요청에 실패

문제가 발생한 원인은 다음과 같다

  • 서버를 구성하는 프레임워크에 따라, Trailing Slash (URL 마지막에 붙는 /를 의미) 에 대한 처리동작에 차이가 있다
  • 이번에 다룬 FastAPI는 Trailing Slash 가 붙을 경우, redirection 한다
  • 이 때 server 가 인지하는 자신의 ip:port 값을 리디렉션하는데, nginx와 frontend 는 그 값을 그대로 사용하기 때문에 리디렉션 URL이 달라서 최종 실패.

문제 해결에 실패한 이유 분석하기

나는 처음에 cors 문제를 인식했으나, 올바른 판단을 하지 못했다. 왜? 아래 사항을 몰랐기 때문.

1. nginx를 쓰는지에 따라, CORS 설정의 해결 포인트가 달라진다

  • 쓰지 않는다 -> Frontend 가 바로 Backend로 붙는 상황이다
    이때가 Backend Code 에서 allowed origins 설정을 해야하는 상황이다

  • 쓰고 있다 -> Frontend 가 nginx에 붙는 상황이다.
    이때는 Nginx 설정을 바꿔야 하는 상황이다.

초반에는 이 둘의 차이를 구분하지 못해, 서버에 allow origin 설정을 시도했었다.
api를 잘 설계하고 nginx 설정을 잘 내리면 수행이 잘 되는 문제였다.
(뒤늦게 생각해보건데, 애초에 문제를 잘못 분석했다면, 처음부터 CORS 문제가 아니었을지도 모르겠다.. 처음부터 CORS 문제라고 인식한 탓에, 잘못된 해결책을 적용하였으므로 이문제가 그 문제가 아니었음을 구분하기 위해 글을 남겨둠)

2. Tailing Slash 를 처리해야 하며, 이는 API 설계의 영역이다.

결론적으로, 나는 다음과 같이 문제를 해결했다.

1. API 설계 확정:

  • BE 에서 Tailing Slash가 있는 API 의 존재 자체를 인지하지 못했던 건 실수.
  • 모든 API 는 Tailing Slash가 없도록 변경: @app.get("/items/") -> @app.get("/items")
  • FE의 API 요청은 /api/items 로 고정.
    - FE의 URL 이 http://nginx-ip:nginx-external-ip/api/items 이므로, FE -> Nginx 요청은 nginx-ip:nginx-external-ip/api/items 경로를 따라 Nginx에 무사히 도달함. 이는 Nginx 설정을 통해 올바르게 포워딩 됨.
    location /api/ {
        proxy_pass http://server-hostname:server-internal-ip/;
        proxy_redirect / /api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
profile
서버도 하고 웹도 하고 시스템이나 인프라나 네트워크나 그냥 다 함.

0개의 댓글