- 법인차량 관리 3개 페이지 (차량등록, 운행일지, 정비이력) - MES 데이터 정합성 분석 보고서 v1/v2 - sam-docs 프론트엔드 기술문서 v1 (9개 챕터) - claudedocs 가이드/테스트URL 업데이트
4.1 KiB
4.1 KiB
인증 및 API 통신
인증 아키텍처
SAM ERP는 HttpOnly Cookie 기반 인증을 사용합니다. JavaScript에서 토큰을 직접 접근할 수 없으므로, 모든 인증 API 호출은 Next.js API Proxy를 통해 처리됩니다.
클라이언트 (브라우저)
│
├── Server Action 호출 (useEffect에서)
│ └── serverFetch() → 서버에서 쿠키 읽기 → 백엔드 API 호출
│
└── 프록시 API 호출 (fetch('/api/proxy/...'))
└── API Proxy Route → 서버에서 쿠키 읽기 → 백엔드 API 호출
쿠키 구조
| 쿠키명 | HttpOnly | 용도 | Max-Age |
|---|---|---|---|
access_token |
O | API 인증 토큰 | 2시간 |
refresh_token |
O | 토큰 갱신용 | 7일 |
is_authenticated |
X | 클라이언트 인증 상태 확인 | 2시간 |
access_token,refresh_token: HttpOnly → JavaScript 접근 불가 (XSS 방지)is_authenticated: non-HttpOnly → 클라이언트에서 인증 상태 확인 가능 (FCM 등)- 프로덕션:
Secure플래그 활성화 (HTTPS만) SameSite=Lax: CSRF 방지
인증 흐름
로그인
1. 사용자 → /api/auth/login (POST)
2. 백엔드 → access_token + refresh_token 반환
3. API Route → Set-Cookie (HttpOnly) 설정
4. 클라이언트 → /(protected)/dashboard 리다이렉트
API 요청 (Server Action)
1. 클라이언트 → Server Action 호출
2. serverFetch() → 쿠키에서 access_token 읽기
3. authenticatedFetch() → Authorization 헤더에 토큰 추가
4. 백엔드 API 호출 → 응답 반환
토큰 만료 시 (401 자동 갱신)
1. API 요청 → 401 응답 수신
2. authenticatedFetch() → refresh_token으로 갱신 요청
3. 새 토큰 수신 → 쿠키 업데이트
4. 원래 요청 재시도 → 성공
5. 갱신 실패 → 쿠키 삭제 → /login 리다이렉트
토큰 갱신 중복 방지
// globalThis 레벨 캐싱 (5초)
// 여러 요청이 동시에 401을 받아도 refresh는 1회만 실행
// 진행 중인 refresh Promise를 공유하여 대기
API 프록시 (/api/proxy/[...path])
클라이언트에서 직접 백엔드 API를 호출해야 하는 경우 프록시 사용:
// 클라이언트에서 프록시 호출
const response = await fetch('/api/proxy/item-master/init');
const data = await response.json();
프록시 내부 동작:
- HttpOnly 쿠키에서
access_token읽기 - 백엔드 URL 구성 (
/api/proxy/*→ 백엔드/*) Authorization: Bearer {token}헤더 추가- 요청 전달 → 응답 반환
- 401 시 자동 토큰 갱신 후 재시도
- 새 토큰 → Set-Cookie 헤더로 클라이언트에 전달
인증 보호
Protected Layout
// (protected)/layout.tsx
export default function ProtectedLayout({ children }) {
// 인증 가드 (뒤로가기 캐시 감지)
useAuthGuard();
return (
<RootProvider>
<ApiErrorProvider> {/* 401 에러 자동 처리 */}
<FCMProvider> {/* 푸시 알림 */}
<AuthenticatedLayout>
<PermissionGate> {/* 권한 기반 접근 제어 */}
{children}
</PermissionGate>
</AuthenticatedLayout>
</FCMProvider>
</ApiErrorProvider>
</RootProvider>
);
}
인증 상태 확인 (클라이언트)
import { hasAuthToken } from '@/lib/api/auth-headers';
// is_authenticated 쿠키 확인 (non-HttpOnly)
if (hasAuthToken()) {
// 인증됨
}
로그아웃
완전한 로그아웃 절차:
- Zustand 스토어 초기화 (useAuthStore, useMasterDataStore, useItemMasterStore)
- sessionStorage 캐시 삭제 (page_config_, mes-)
- localStorage 사용자 데이터 삭제
- FCM 토큰 해제 (Capacitor 환경)
- 서버 로그아웃 API 호출
- /login 리다이렉트
주의사항
- Server Component에서 쿠키 수정 불가 → Client Component 사용 필수
alert(),confirm(),prompt()사용 금지 → Radix UI Dialog 또는toast사용- API 직접 호출 금지 → 반드시 Server Action 또는 프록시 사용