6.2 KiB
6.2 KiB
백엔드 수정 요청서 (2025-10-20 갱신)
1. 배경
- 프런트엔드는
.env.development에서API_BASE_URL=http://43.201.34.104:8080을 사용해 실서버 로그인 API를 호출한다. - 동일 API를 사용하는 운영 환경에서는 CORS 문제가 없지만, 로컬 웹 개발 시 브라우저가
http://localhost:<port>오리진으로 사전 요청(preflight)을 보내면서 403 대신 CORS 차단이 발생한다. - 프런트(
superport_v2)와 백엔드(superport_api_v2) 양쪽 구현을 재검토한 결과, 로그인 계약은 일치하나 실서버에서 CORS 응답 헤더가 전혀 내려오지 않는 것으로 확인되었다. - 로컬 개발 및 QA가 모두 실서버를 바라보고 있어, 백엔드에서 CORS 허용 정책을 명시적으로 정비해야 한다.
2. 현상 및 재현 절차
-
브라우저 콘솔 오류:
Access to XMLHttpRequest at 'http://43.201.34.104:8080/api/v1/auth/login' from origin 'http://localhost:50408' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. -
curl 재현(사전 요청):
curl -i -X OPTIONS \ http://43.201.34.104:8080/api/v1/auth/login \ -H 'Origin: http://localhost:50408' \ -H 'Access-Control-Request-Method: POST'실제 응답:
HTTP/1.1 404 Not Found+ 헤더 없음 → CORS 미적용. -
실제 요청도 동일하게 헤더가 비어 있음:
curl -i -X POST \ http://43.201.34.104:8080/api/v1/auth/login \ -H 'Origin: http://localhost:50408' \ -H 'Content-Type: application/json' \ -d '{"identifier":"test","password":"test"}'응답:
401 Unauthorized본문은 내려오지만Access-Control-Allow-Origin헤더가 없음.
3. 프런트/백엔드 로그인 계약 점검
- 프런트 요청 구조
- 경로:
POST ${ApiRoutes.apiV1}/auth/login→/api/v1/auth/login - 페이로드:
{ identifier, password, remember_me }(lib/features/auth/data/repositories/auth_repository_remote.dart:18) - 응답 파싱:
{ data: { access_token, refresh_token, expires_at, user, permissions[] } }(lib/features/auth/data/dtos/auth_session_dto.dart)
- 경로:
- 백엔드 구현
- 라우트:
#[post("/login")](backend/src/api/v1/auth.rs:17) →web::scope("/api/v1") - 요청 모델:
LoginRequest { identifier: String, password: String, remember_me: bool }(backend/src/domain/auth.rs:9) - 응답 모델:
AuthSessionResponse { data: AuthSessionData { ... } }
- 라우트:
- 계약 비교 결론
- 필드 명/자료형 모두 일치, remember_me 기본값 및 데이터 매핑도 호환.
- 로그인 자체는 401/403 흐름이 정상이나, 브라우저 오리진이 차단되어 요청이 전달되지 못함.
4. 근본 원인 분석
- 백엔드
App::new()정의는build_cors(&config.cors)미들웨어를wrap하고default_service에서OPTIONS가드를 204로 처리하도록 구현되어 있음 (backend/src/app/mod.rs:36-132). config/default.toml의[cors]설정은allowed_origins = []로 전체 허용이 기본이지만, 실제 운영 환경에서는APP_ENV에 대응하는 설정 또는 환경 변수로 제한된 오리진만 등록한 것으로 추정된다.- 하지만 허용 목록에서 로컬 호스트가 빠진 경우라면 CORS 미들웨어가
403을 반환해야 하는데, 현재는 단순 404/401 응답으로 보아 미들웨어가 동작하지 않거나, 리버스 프록시/로드밸런서 구간에서 CORS 헤더가 삭제되고 있다. - 결과적으로 브라우저는
Access-Control-Allow-Origin을 받지 못하고 사전 요청 단계에서 차단된다.
5. 요청 사항
- 백엔드 Actix CORS 설정 재점검
build_cors가 실제 배포 바이너리에도 적용되는지 확인하고, 필요한 경우Cors::default()대신Cors::permissive()또는allowed_origin_fn로깅을 추가해 런타임에서 허용 여부를 추적한다.supports_credentials()를 유지하면서도 최소http://localhost기반 개발 포트 전체를 허용하도록allowed_origin_fn에서 와일드카드 검사를 추가하거나, 설정 파일에 와일드카드 표기를 허용하도록 개선한다..allowed_origin_fn(move |origin, _| { if allow_all { return true; } if origin.as_bytes().starts_with(b"http://localhost") { return true; } allowed_list.iter().any(|allowed| allowed == origin) })- 운영 배포용 설정(
APP_ENV=production)에도cors.allowed_origins에https://{prod-domain}+http://localhost(또는 사내 VPN 도메인)을 명시한다.
- 리버스 프록시/로드밸런서 검증
- Nginx/ALB 등 중간 계층이
OPTIONS메서드를 백엔드로 전달하는지 확인하고, 차단 시proxy_set_header Access-Control-Allow-Origin등을 설정한다. - 모든 사전 요청이 최소
204혹은200과 함께Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers를 반환하도록 보장한다.
- Nginx/ALB 등 중간 계층이
- 로그인 핸들러 응답 헤더 확인
- 인증 성공/실패 여부와 관계없이
Access-Control-Allow-Origin이 반드시 포함되도록 통합 테스트를 추가한다. - 예시:
cargo test cors_allows_login_origin형태의 통합 테스트에서Origin: http://localhost:50408헤더를 넣고 응답 헤더를 검증.
- 인증 성공/실패 여부와 관계없이
6. 검증 및 수용 기준
curl -X OPTIONS및curl -X POST재현 시Access-Control-Allow-Origin: http://localhost:50408헤더가 내려오고 브라우저 CORS 에러가 사라질 것.flutter run -d chrome --web-port 50408에서 로그인 성공/실패 흐름이 정상 동작.- 백엔드
cargo fmt,cargo test모두 통과. stock_approval_system_api_v4.md또는 운영 문서에 허용 오리진 정책 및 설정 방법을 명시.
7. 후속 조치
- 백엔드 담당자가 실제 배포 서버의 환경 변수/리버스 프록시 설정을 확인 후 조치 내용을 공유.
- 수정 배포 이후 프런트 팀이 실서버 연결 테스트를 수행하고, 필요한 경우 추가 허용 오리진 목록을 요청.