98 lines
6.2 KiB
Markdown
98 lines
6.2 KiB
Markdown
# 백엔드 수정 요청서 (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 재현(사전 요청):
|
|
|
|
```bash
|
|
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 미적용.
|
|
|
|
- 실제 요청도 동일하게 헤더가 비어 있음:
|
|
|
|
```bash
|
|
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. 요청 사항
|
|
1. **백엔드 Actix CORS 설정 재점검**
|
|
- `build_cors`가 실제 배포 바이너리에도 적용되는지 확인하고, 필요한 경우 `Cors::default()` 대신
|
|
`Cors::permissive()` 또는 `allowed_origin_fn` 로깅을 추가해 런타임에서 허용 여부를 추적한다.
|
|
- `supports_credentials()`를 유지하면서도 최소 `http://localhost` 기반 개발 포트 전체를 허용하도록
|
|
`allowed_origin_fn`에서 와일드카드 검사를 추가하거나, 설정 파일에 와일드카드 표기를 허용하도록 개선한다.
|
|
```rust
|
|
.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 도메인)을 명시한다.
|
|
2. **리버스 프록시/로드밸런서 검증**
|
|
- Nginx/ALB 등 중간 계층이 `OPTIONS` 메서드를 백엔드로 전달하는지 확인하고, 차단 시 `proxy_set_header Access-Control-Allow-Origin` 등을 설정한다.
|
|
- 모든 사전 요청이 최소 `204` 혹은 `200`과 함께 `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`를 반환하도록 보장한다.
|
|
3. **로그인 핸들러 응답 헤더 확인**
|
|
- 인증 성공/실패 여부와 관계없이 `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. 후속 조치
|
|
- 백엔드 담당자가 실제 배포 서버의 환경 변수/리버스 프록시 설정을 확인 후 조치 내용을 공유.
|
|
- 수정 배포 이후 프런트 팀이 실서버 연결 테스트를 수행하고, 필요한 경우 추가 허용 오리진 목록을 요청.
|