Files
sam-react-prod/claudedocs/auth/[IMPL-2025-11-07] auth-guard-usage.md
byeongcheolryu 65a8510c0b fix: 품목기준관리 실시간 동기화 수정
- BOM 항목 추가/수정/삭제 시 섹션탭 즉시 반영
- 섹션 복제 시 UI 즉시 업데이트 (null vs undefined 이슈 해결)
- 항목 수정 기능 추가 (useTemplateManagement)
- 실시간 동기화 문서 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 22:19:50 +09:00

8.2 KiB

Auth Guard Hook 사용 가이드

개요

useAuthGuard() Hook은 보호된 페이지에 인증 검증과 브라우저 캐시 방지 기능을 제공합니다.

기능

  1. 실시간 인증 확인: 페이지 로드 시 서버에 인증 상태 확인
  2. 뒤로가기 보호: 로그아웃 후 브라우저 뒤로가기 시 캐시된 페이지 접근 차단
  3. 자동 리다이렉트: 인증 실패 시 자동으로 로그인 페이지로 이동

사용 방법

기본 사용

보호가 필요한 모든 페이지에 Hook을 추가하세요:

"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function ProtectedPage() {
  // 🔒 인증 보호 및 브라우저 캐시 방지
  useAuthGuard();

  return (
    <div>
      {/* 보호된 컨텐츠 */}
    </div>
  );
}

적용 예시

Dashboard 페이지

// src/app/[locale]/dashboard/page.tsx
"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function Dashboard() {
  useAuthGuard(); // 한 줄만 추가하면 끝!

  return <div>Dashboard Content</div>;
}

Profile 페이지

// src/app/[locale]/profile/page.tsx
"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function Profile() {
  useAuthGuard();

  return <div>Profile Content</div>;
}

Settings 페이지

// src/app/[locale]/settings/page.tsx
"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function Settings() {
  useAuthGuard();

  return <div>Settings Content</div>;
}

적용이 필요한 페이지

다음 페이지들에 useAuthGuard() Hook을 적용해야 합니다:

필수 적용 페이지

  • /dashboard - 이미 적용됨
  • /profile - 적용 필요
  • /settings - 적용 필요
  • /admin/* - 모든 관리자 페이지
  • /tenant/* - 모든 테넌트 관리 페이지
  • /users/* - 사용자 관리 페이지
  • /reports/* - 리포트 페이지
  • /analytics/* - 분석 페이지
  • /inventory/* - 재고 관리 페이지
  • /finance/* - 재무 관리 페이지
  • /hr/* - 인사 관리 페이지
  • /crm/* - CRM 페이지

적용 불필요 페이지

  • /login - 게스트 전용
  • /signup - 게스트 전용
  • /forgot-password - 게스트 전용

동작 방식

1. 페이지 로드 시

페이지 컴포넌트 마운트
    ↓
useAuthGuard() 실행
    ↓
/api/auth/check 호출 (HttpOnly 쿠키 검증)
    ↓
인증 성공 → 페이지 표시
인증 실패 → /login으로 리다이렉트

2. 뒤로가기 시 (브라우저 캐시)

브라우저 뒤로가기
    ↓
pageshow 이벤트 감지
    ↓
event.persisted === true? (캐시된 페이지인가?)
    ↓
Yes → window.location.reload() (새로고침)
    ↓
useAuthGuard() 재실행
    ↓
인증 확인 → 쿠키 없음 → /login 리다이렉트

내부 구현

src/hooks/useAuthGuard.ts:

export function useAuthGuard() {
  const router = useRouter();

  useEffect(() => {
    // 1. 인증 확인
    const checkAuth = async () => {
      const response = await fetch('/api/auth/check');
      if (!response.ok) {
        router.replace('/login');
      }
    };

    checkAuth();

    // 2. 브라우저 캐시 방지
    const handlePageShow = (event: PageTransitionEvent) => {
      if (event.persisted) {
        window.location.reload();
      }
    };

    window.addEventListener('pageshow', handlePageShow);

    return () => {
      window.removeEventListener('pageshow', handlePageShow);
    };
  }, [router]);
}

API 엔드포인트

GET /api/auth/check

목적: HttpOnly 쿠키를 통한 인증 상태 확인

요청:

GET /api/auth/check HTTP/1.1
Cookie: user_token=...

응답 (인증 성공):

{
  "authenticated": true
}

Status: 200 OK

응답 (인증 실패):

{
  "error": "Not authenticated",
  "authenticated": false
}

Status: 401 Unauthorized

테스트 시나리오

시나리오 1: 정상 접근

  1. 로그인 상태로 /dashboard 접근
  2. 페이지 정상 표시
  3. 콘솔 로그 없음 (정상 동작)

시나리오 2: 비로그인 접근

  1. 로그아웃 상태로 /dashboard URL 직접 입력
  2. 즉시 /login으로 리다이렉트
  3. 콘솔: "⚠️ 인증 실패: 로그인 페이지로 이동"

시나리오 3: 로그아웃 후 뒤로가기

  1. /dashboard 접속 (로그인 상태)
  2. Logout 버튼 클릭 → /login 이동
  3. 브라우저 뒤로가기 버튼 클릭
  4. 캐시된 페이지 감지 → 새로고침 → /login 리다이렉트
  5. 콘솔: "🔄 캐시된 페이지 감지: 새로고침"

시나리오 4: 다른 탭에서 로그아웃

  1. 탭 A: /dashboard 접속 (로그인 상태)
  2. 탭 B: 같은 브라우저에서 로그아웃
  3. 탭 A: 페이지 새로고침 또는 다른 페이지 이동
  4. 인증 확인 실패 → /login 리다이렉트

Middleware와의 관계

보안 레이어 역할 타이밍
Middleware 서버 사이드 경로 보호 모든 요청 전
useAuthGuard 클라이언트 사이드 보호 페이지 마운트 시

왜 둘 다 필요한가?

Middleware만 있으면?

  • 브라우저 뒤로가기 캐시 문제 해결 안됨
  • 실시간 인증 상태 변경 감지 안됨

useAuthGuard만 있으면?

  • URL 직접 접근 시 보호 지연 (컴포넌트 마운트 후)
  • 서버 사이드 렌더링 보호 안됨

둘 다 있으면:

  • 서버 + 클라이언트 이중 보호
  • 브라우저 캐시 문제 해결
  • 실시간 인증 상태 동기화

성능 고려사항

API 호출 최소화

  • useAuthGuard는 페이지 마운트 시 1회만 호출
  • 페이지 이동 시마다 다시 실행됨 (의도된 동작)

사용자 경험

  • 인증 확인은 비동기로 처리되어 UI 블로킹 없음
  • 인증 실패 시 router.replace() 사용 (뒤로가기 히스토리 오염 방지)

문제 해결

문제: Hook이 작동하지 않음

원인: 페이지가 Server Component로 되어 있음 해결: 파일 상단에 "use client"; 추가

문제: 무한 리다이렉트

원인: /login 페이지에도 Hook 적용됨 해결: 게스트 전용 페이지에는 Hook 사용 금지

문제: 뒤로가기 시 여전히 페이지 보임

원인: pageshow 이벤트 리스너 미등록 해결: Hook이 올바르게 import되었는지 확인

향후 개선 사항

1. 토큰 검증 추가

현재는 토큰 존재 여부만 확인하지만, 향후 PHP 백엔드에 토큰 유효성 검증 추가 가능:

// /api/auth/check 개선
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/verify`, {
  headers: { 'Authorization': `Bearer ${token}` }
});

2. 자동 새로고침 주기

장시간 페이지 유지 시 주기적 인증 확인:

useEffect(() => {
  const interval = setInterval(checkAuth, 5 * 60 * 1000); // 5분마다
  return () => clearInterval(interval);
}, []);

3. 세션 만료 경고

토큰 만료 임박 시 사용자에게 알림:

if (expiresIn < 5 * 60 * 1000) {
  showToast('세션이 곧 만료됩니다. 다시 로그인해주세요.');
}

요약

적용 완료:

  • Dashboard 페이지

적용 필요:

  • 다른 모든 보호된 페이지들

📝 사용법:

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function Page() {
  useAuthGuard(); // 이 한 줄만 추가!
  return <div>Content</div>;
}

🔒 보안 효과:

  • 브라우저 캐시 악용 방지
  • 실시간 인증 상태 동기화
  • 로그아웃 후 완전한 페이지 접근 차단

관련 파일

프론트엔드

  • src/hooks/useAuthGuard.ts - Auth Guard Hook 구현
  • src/app/api/auth/check/route.ts - 인증 체크 API
  • src/app/[locale]/(protected)/layout.tsx - Protected Layout
  • src/middleware.ts - 인증 미들웨어
  • src/lib/api/auth/auth-config.ts - 인증 설정 (라우트)

보호된 페이지

  • src/app/[locale]/(protected)/dashboard/page.tsx
  • src/app/[locale]/(protected)/profile/page.tsx
  • src/app/[locale]/(protected)/settings/page.tsx