diff --git a/journey/troubleshooting/251202_ir_valuation_gateway_timeout_and_frontend_fallback.md b/journey/troubleshooting/251202_ir_valuation_gateway_timeout_and_frontend_fallback.md new file mode 100644 index 0000000..b87e252 --- /dev/null +++ b/journey/troubleshooting/251202_ir_valuation_gateway_timeout_and_frontend_fallback.md @@ -0,0 +1,81 @@ +# IR Valuation 게이트웨이 타임아웃 및 프론트엔드 폴백 정리 + +**날짜**: 2025-12-02 +**작성자**: Auto +**관련 파일**: +- `nginx-infra/server-nginx-default` +- `frontend-ir-valuation/src/services/irDeckService.ts` +- `frontend-ir-valuation/src/hooks/useEvaluation.ts` +- `frontend-ir-valuation/src/pages/ir-valuation.tsx` + +--- + +## 문제 상황 + +- IR Deck 평가 요청 시 브라우저에서 아래 현상 반복 관측 + - `POST /rb8001/api/ir-deck/evaluate` → `504 Gateway Timeout`, 이후 `502 Bad Gateway` 간헐 발생 + - `GET /rb8001/api/ir-deck/evaluation/{evaluation_id}` → 404 반복 + - 채팅 화면에는 `(템플릿) 평가 처리 중 오류가 발생했습니다.` 텍스트만 표시되어 원인 파악 어려움 + +--- + +## 원인 분석 + +1. 게이트웨이 타임아웃 및 백엔드 연결 문제 + - 51123 Nginx에서 `/rb8001/`를 51124 `192.168.219.52:8001`로 직접 프록시 + - `nginx-infra/server-nginx-default:107-117`: `proxy_pass` 및 `proxy_read_timeout 300s`, `proxy_connect_timeout 75s` 설정 + - 같은 서버에서 직접 호출 테스트 + - `curl http://192.168.219.52:8001/api/ir-deck/evaluate` → 잘못된 `document_id`에 대해 `500`을 수 ms 내로 응답 (엔드포인트 자체는 동작) + - 이후 특정 시점에는 `curl http://192.168.219.52:8001/` → 연결 실패 (`Couldn't connect to server`) 관측 (502와 일치) + - 정리: 로컬 테스트 기준 rb8001은 응답이 가능하지만, 실제 운영 중에는 일시적인 다운/재시작 또는 평가 처리 지연으로 인해 504/502가 Nginx에서 반환됨 + +2. 프론트엔드 에러 처리 및 데이터 검증 부족 + - 평가 시작 API 실패 시 + - `frontend-ir-valuation/src/services/irDeckService.ts:61-81`: `!response.ok`이면 `Error("평가 시작 실패: {status} {body}")`로 래핑 + - `frontend-ir-valuation/src/pages/ir-valuation.tsx:286-313`: `sendMessage` `catch`에서 이 에러를 받아 `(템플릿) ...` 문구로만 표시 + - 평가 결과 폴링 시 + - `frontend-ir-valuation/src/services/irDeckService.ts:84-96`: `getEvaluation`이 404/5xx면 `Error("평가 결과 조회 실패: {status}")` 발생 + - `frontend-ir-valuation/src/hooks/useEvaluation.ts:103-129`: `checkEvaluation()`에서 `"조회 실패"` 또는 `"404"`, `"504"`가 포함된 에러는 “아직 미완료/일시적 에러”로 간주하고 폴링 지속 + - 응답 구조 검증 부족 + - 기존 코드에서는 `result.page_evaluations.map(...)`를 바로 호출해, 응답에 `page_evaluations`가 없거나 배열이 아닐 경우 프론트엔드에서 `TypeError` 발생 가능 구조였음 + - 에러가 `pollEvaluation`의 `try` 블록 안에서 발생하면 `catch → onError → "(템플릿) ..." 출력으로 이어져, 사용자는 백엔드/프론트 어느 쪽 문제인지 구분 불가 + +--- + +## 해결 방안 + +1. 프론트엔드 에러 메시지 및 폴백 정교화 + - 평가 시작 단계 + - `frontend-ir-valuation/src/pages/ir-valuation.tsx:286-313` + - 에러 메시지에 `504`, `Gateway Timeout`, `timeout`, `초과`가 포함되면 `"분석 시간이 초과되었습니다."`로 매핑 + - 네트워크/업로드/평가/채팅 각각에 대해 별도 사용자 메시지로 분리 + - 평가 결과 폴링 단계 + - `frontend-ir-valuation/src/hooks/useEvaluation.ts:103-129` + - `Error` 메시지에 `"조회 실패"`, `"404"`, `"504"`가 포함된 경우 **평가 미완료/일시적 오류로 간주하고 폴링 계속**, 그 외에는 에러로 처리하여 `onError`로 전달 + - 데이터 검증 강화 + - `frontend-ir-valuation/src/pages/ir-valuation.tsx:220-247` + - `page_evaluations`에 대해 `Array.isArray`로 검증 후 map 수행 + - 배열이 아니면 경고 로그만 남기고 `pageEvaluations`를 빈 배열로 처리하여 화면이 깨지는 것을 방지 + +2. 게이트웨이/백엔드 상태 확인용 기준 정리 + - 게이트웨이 레벨: + - 504/502 발생 시 51123 Nginx 설정 및 rb8001 포트(192.168.219.52:8001) 연결 여부 확인 + - 백엔드 레벨: + - `/rb8001/api/ir-deck/evaluate` 응답 시간 및 상태 코드 확인 (동일 헤더: `X-Team-Id`, `X-User-Id`) + - `/rb8001/api/ir-deck/evaluation/{evaluation_id}`가 404에서 200으로 전환되는지 확인 + +--- + +## 교훈 + +1. HTTP 레벨 에러와 프론트 텍스트를 분리해서 생각하기 + - 동일한 `(템플릿) 평가 처리 중 오류` 메시지라도 실제 원인은 504/502/404/500/TypeError 등 다양할 수 있음 + - 프론트에서는 에러 유형별로 다른 메시지와 로깅을 제공하고, 원인은 반드시 HTTP 상태 및 백엔드 로그로 교차 검증해야 함 + +2. 응답 구조 검증은 필수 + - 백엔드 스펙(document_id, total_score, grade, page_evaluations 등)에 의존하더라도, 프론트에서는 항상 **타입/존재 여부를 검증한 뒤 사용**해야 런타임 예외를 예방할 수 있음 + +3. 동기식 장기 작업은 게이트웨이 타임아웃을 전제로 설계해야 함 + - 평가처럼 시간이 오래 걸리는 작업은 `/evaluate`에서 모든 일을 끝내려 하기보다, **짧게 `evaluation_id`만 반환 + 폴링** 구조를 기준으로 설계하는 것이 안전함 + - 현재 구조에서도 프론트는 평가 결과 폴링을 이미 사용 중이므로, 백엔드 `/evaluate`의 동작을 재검토하여 게이트웨이 타임아웃을 일으키지 않는 방향으로 단계적 전환이 필요함 +