qcast-front/docs/API_STABILITY_HANDOVER.md

137 lines
5.2 KiB
Markdown

# API 안정화 / 인증 / 세션 인수인계서
> 작성일: 2026-05-06 (옵션 B 분리)
> 대상: 후임 AI 엔지니어 또는 개발 관리자
> 작업 브랜치: `hotfix/dev_yscha`
> **이 문서의 범위**: 백엔드 API 통신, 재시도, 인증/세션 추적, 로컬 개발 환경 트랙만 다룹니다.
> 다른 트랙은 `HANDOVER_INDEX.md` 참조.
---
## 0. 한 줄 요약
API 통신이 **동시 호출로 데드락**나거나 **간헐적 실패**로 사용자 작업이 날아가는 일을 막기 위해, 클라이언트 측 직렬화 / 재시도 / 에러 저장 / stale closure 제거 / 인증 추적을 차례로 보강한 트랙입니다.
---
## 1. 동시 UPDATE 데드락 방어 (가장 중요)
### 1.1 무슨 일이 있었나
- 같은 데이터를 짧은 시간에 여러 번 저장하면 **백엔드에서 동시 UPDATE 가 부딪혀 데드락**이 났습니다.
- 사용자 입장에서는 "저장 안 됨" / "타임아웃" / "데이터 깨짐" 으로 보였습니다.
### 1.2 어떻게 풀었나 (커밋 `3000de9c`)
세 단계로 구성:
| 단계 | 무엇을 하나 |
|------|------------|
| **abort + latest** | 진행 중인 동일 요청은 즉시 abort 하고, **가장 최근 요청 1개만** 살림 |
| **`X-Idempotency-Key` 헤더** | 같은 의도의 요청이 중복 도달해도 백엔드가 한 번만 처리하도록 멱등키 부여 |
| **클라이언트 큐 직렬화** | 같은 리소스에 대한 호출은 클라이언트 단에서 줄을 세워 1개씩만 보냄 |
### 1.3 핵심 파일
- `src/hooks/useAxios.js` — 메인 구현 위치
- 호출부 여러 곳
### 1.4 주의
- abort 가 너무 공격적이면 **사용자가 의도한 마지막 요청도 취소될 수 있습니다**. 새 호출 추가 시 abort 정책을 검토하세요.
- 멱등키는 **요청 단위로 유니크** 해야 합니다. 같은 키로 다른 의미의 요청을 보내면 이전 결과가 캐시처럼 돌아옵니다.
---
## 2. 에러 저장 API (`errSave`)
- 커밋 `bb775b28`
- 클라이언트에서 발생한 예외/에러를 **백엔드로 자동 전송**해 추적할 수 있게 한 API.
- 운영 중에 사용자가 못 알아채는 에러를 잡기 위함.
### 사용처
- 글로벌 에러 핸들러
- 주요 fetch 실패 catch 블록
---
## 3. API 재시도 — `getRoofMaterialList` 10회
- 커밋 `910974f8`
- `getRoofMaterialList` API 가 간헐적으로 실패하는 이슈에 대해 **최대 10회 재시도**.
- 지붕재 목록은 화면 진입 시 반드시 필요한 데이터라 실패하면 사용자가 아무것도 못 함.
### 주의
- 10회 재시도는 **이 한 API 한정** 정책. 다른 API 에 그대로 적용하지 마세요. 일반화하면 백엔드 부하 증가.
---
## 4. `fetchBasicSettings` Stale Closure 수정
- 커밋 `63578276`
- **문제**: `fetchBasicSettings``correntObjectNo` 를 Recoil 상태에서 직접 읽고 있어, 새 오브젝트가 생성될 때 **이전 값(stale)** 이 그대로 남아 있는 클로저 문제 발생.
- **수정**: 호출 시점의 최신 값을 인자로 받아 처리하도록 변경.
### 일반화 가능한 교훈
- React + Recoil 환경에서 비동기 함수 안에서 atom 을 읽을 때 항상 stale closure 가능성을 의심하세요.
---
## 5. 인증 / 세션 추적
자동 로그인 / 로그아웃 동작이 의도대로 안 되는 케이스가 보고돼 단계적으로 추적 로그를 넣었습니다.
| 단계 | 커밋 |
|------|------|
| 로그인·로그아웃 추적 (1차) | `138bdbdd` |
| 추적 강화 (반복) | `ccf60ee7`, `c6da437a` |
| 자동 로그인·로그아웃 추적 1 | `c6da437a` |
| 자동 로그인·로그아웃 추적 2 | `afe0a377` |
| 로그아웃 결과 없이 (이슈 케이스) | `b021b7be` |
### 추적 로그의 목적
- 어디서 토큰이 사라지는지
- 어디서 자동 로그인 트리거가 안 잡히는지
- 어디서 로그아웃 결과가 누락되는지
---
## 6. 로컬 개발 환경 — dev 스크립트
- 커밋 `27ee53c3`
- `package.json` 에 다음 추가:
- `dev:local-api` — 로컬 백엔드(`localhost:8080`) 에 붙여서 실행
- `dev:log:local-api` — 위와 동일 + 로그 강화 모드
- `.env.localhost` 와 페어로 사용. 환경변수에서 API base URL 분기.
### 사용 시나리오
- 백엔드 개발자와 동시 디버깅 시
- API 응답 형태가 stage / production 과 다른 경우
---
## 7. 미해결 / 추적 중
- 동시 UPDATE 데드락의 **완전 재발 방지**는 여전히 모니터링 필요. abort 정책이 새로운 호출 패턴에서 깨질 수 있음.
- 인증 추적 로그는 **운영에서 떼어낼지** 결정 필요. 현재는 디버깅 목적 유지.
---
## 8. 핵심 파일
| 파일 | 역할 |
|------|------|
| `src/hooks/useAxios.js` | API 호출 hook + abort/멱등키/직렬화 |
| `package.json` | dev 스크립트 |
| `.env.localhost` | 로컬 환경 변수 |
| `src/...` (Recoil atoms 의 fetch wrapper) | stale closure 회피 패턴 적용 |
---
## 9. 인수 직후 권장 절차
1. 이 문서 통독.
2. `useAxios.js` 의 abort/직렬화 흐름 코드 한 번 따라가 보기.
3. `npm run dev:local-api` 로 로컬 백엔드와 붙어 동작 확인.
4. 인증 추적 로그를 운영에서 유지/제거 여부 사용자에게 확인.
끝.