Files
sam-react-prod/claudedocs/auth/[IMPL-2025-11-13] safari-cookie-compatibility.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

13 KiB

Safari 쿠키 호환성 및 크로스 브라우저 가이드

📋 목차

  1. 문제 상황
  2. 원인 분석
  3. 해결 방법
  4. 수정된 파일
  5. 크로스 브라우저 개발 가이드라인
  6. 테스트 체크리스트

문제 상황

Safari에서 발생한 인증 문제

  • 로그인: 성공했으나 대시보드로 이동 불가 ({"error":"Not authenticated"})
  • 로그아웃: 로그아웃 버튼 클릭 시 정상 동작하지 않음
  • 크롬/파이어폭스: 정상 작동

증상

# Safari 브라우저
✅ 로그인 API 호출 성공 (200 OK)
❌ 대시보드 접근 실패 (401 Unauthorized)
❌ 쿠키가 저장되지 않음

# Chrome/Firefox 브라우저
✅ 모든 기능 정상 작동

원인 분석

Safari의 엄격한 쿠키 정책

Safari는 다른 브라우저보다 쿠키 보안 정책이 엄격합니다:

1. Secure 속성 제한

// ❌ Safari에서 작동하지 않음 (HTTP 환경)
const cookie = 'access_token=xxx; HttpOnly; Secure; SameSite=Strict';

// Safari 로직:
// - HTTP (localhost:3000) + Secure 속성 = 쿠키 저장 거부
// - HTTPS만 Secure 쿠키 허용

Chrome/Firefox는 localhost에서 Secure 속성을 허용하지만, Safari는 허용하지 않습니다.

2. SameSite=Strict의 제약

// SameSite=Strict: 모든 크로스 사이트 요청에서 쿠키 차단
// - 너무 엄격하여 일부 정상적인 요청도 차단될 수 있음

// SameSite=Lax: CSRF 보호 + 유연성
// - GET 요청과 top-level navigation에서는 쿠키 전송 허용
// - 대부분의 웹 애플리케이션에 적합

3. 쿠키 삭제 시 속성 불일치

Safari는 쿠키를 삭제할 때 설정할 때와 정확히 동일한 속성을 요구합니다:

// ❌ Safari에서 쿠키 삭제 실패
// 설정: HttpOnly + SameSite=Lax (Secure 없음)
// 삭제: HttpOnly + Secure + SameSite=Strict

// ✅ Safari에서 쿠키 삭제 성공
// 설정: HttpOnly + SameSite=Lax (Secure 없음)
// 삭제: HttpOnly + SameSite=Lax (Secure 없음)

해결 방법

핵심 원칙: 환경별 조건부 쿠키 설정

// 1. 환경 감지
const isProduction = process.env.NODE_ENV === 'production';

// 2. 조건부 Secure 속성
const cookie = [
  'access_token=xxx',
  'HttpOnly',                                    // ✅ 항상 유지 (XSS 보호)
  ...(isProduction ? ['Secure'] : []),           // ✅ HTTPS에서만 적용
  'SameSite=Lax',                                // ✅ CSRF 보호 + 호환성
  'Path=/',
  'Max-Age=7200',
].join('; ');

환경별 쿠키 속성

환경 Secure SameSite HttpOnly 설명
Development (HTTP) 없음 Lax 있음 Safari 호환성
Production (HTTPS) 있음 Lax 있음 완전한 보안

수정된 파일

1. src/app/api/auth/login/route.ts

수정 위치: 150-170 라인

// ❌ 기존 코드 (Safari 비호환)
const accessTokenCookie = [
  `access_token=${data.access_token}`,
  'HttpOnly',
  'Secure',              // 개발 환경에서 문제 발생
  'SameSite=Strict',     // 너무 엄격
  'Path=/',
  `Max-Age=${data.expires_in || 7200}`,
].join('; ');
// ✅ 수정 코드 (Safari 호환)
const isProduction = process.env.NODE_ENV === 'production';

const accessTokenCookie = [
  `access_token=${data.access_token}`,
  'HttpOnly',              // ✅ JavaScript cannot access (XSS 보호)
  ...(isProduction ? ['Secure'] : []), // ✅ HTTPS only in production
  'SameSite=Lax',          // ✅ CSRF protection (Lax for compatibility)
  'Path=/',
  `Max-Age=${data.expires_in || 7200}`,
].join('; ');

const refreshTokenCookie = [
  `refresh_token=${data.refresh_token}`,
  'HttpOnly',
  ...(isProduction ? ['Secure'] : []),
  'SameSite=Lax',
  'Path=/',
  'Max-Age=604800',        // 7 days
].join('; ');

변경 사항:

  • Secure 속성을 환경에 따라 조건부 적용
  • SameSiteStrict에서 Lax로 변경
  • refresh_token도 동일하게 적용

2. src/app/api/auth/check/route.ts

수정 위치: 75-95 라인 (토큰 갱신 시)

// ✅ 수정 코드
if (refreshResponse.ok) {
  const data = await refreshResponse.json();

  // Safari compatibility: Secure only in production
  const isProduction = process.env.NODE_ENV === 'production';

  const accessTokenCookie = [
    `access_token=${data.access_token}`,
    'HttpOnly',
    ...(isProduction ? ['Secure'] : []),
    'SameSite=Lax',
    'Path=/',
    `Max-Age=${data.expires_in || 7200}`,
  ].join('; ');

  const refreshTokenCookie = [
    `refresh_token=${data.refresh_token}`,
    'HttpOnly',
    ...(isProduction ? ['Secure'] : []),
    'SameSite=Lax',
    'Path=/',
    'Max-Age=604800',
  ].join('; ');

  // ... 쿠키 설정
}

변경 사항:

  • 토큰 갱신 시에도 동일한 쿠키 설정 적용
  • login/route.ts와 일관성 유지

3. src/app/api/auth/logout/route.ts

수정 위치: 52-71 라인 (쿠키 삭제)

// ❌ 기존 코드 (Safari에서 쿠키 삭제 실패)
const clearAccessToken = [
  'access_token=',
  'HttpOnly',
  'Secure',           // 설정 시와 속성 불일치
  'SameSite=Strict',  // 설정 시와 속성 불일치
  'Path=/',
  'Max-Age=0',
].join('; ');
// ✅ 수정 코드 (Safari에서 쿠키 삭제 성공)
// Safari compatibility: Must use same attributes as when setting cookies
const isProduction = process.env.NODE_ENV === 'production';

const clearAccessToken = [
  'access_token=',
  'HttpOnly',
  ...(isProduction ? ['Secure'] : []), // ✅ login과 동일
  'SameSite=Lax',                      // ✅ login과 동일
  'Path=/',
  'Max-Age=0',  // Delete immediately
].join('; ');

const clearRefreshToken = [
  'refresh_token=',
  'HttpOnly',
  ...(isProduction ? ['Secure'] : []),
  'SameSite=Lax',
  'Path=/',
  'Max-Age=0',
].join('; ');

변경 사항:

  • 쿠키 삭제 시 설정 시와 정확히 동일한 속성 사용
  • Safari의 엄격한 쿠키 삭제 정책 대응

크로스 브라우저 개발 가이드라인

필수 테스트 브라우저

모든 브라우저 관련 기능 개발 시 다음 브라우저에서 반드시 테스트:

브라우저 우선순위 주요 특징 테스트 환경
Chrome 🔴 High 가장 관대한 정책 macOS/Windows
Safari 🔴 High 가장 엄격한 정책 macOS/iOS
Firefox 🟡 Medium 중간 수준 정책 macOS/Windows
Edge 🟢 Low Chrome 기반 Windows

개발 우선순위: Safari 기준으로 개발하면 다른 브라우저에서도 작동합니다.


쿠키 관련 개발 원칙

1. 환경별 조건부 설정

// ✅ 항상 환경 체크
const isProduction = process.env.NODE_ENV === 'production';
const isSecure = isProduction; // HTTPS 여부

// ✅ Secure 속성은 항상 조건부로
...(isSecure ? ['Secure'] : [])

2. HttpOnly는 항상 유지

// ✅ XSS 공격 방지를 위해 HttpOnly는 항상 포함
'HttpOnly',  // 절대 제거하지 말 것

3. SameSite는 Lax 권장

// ✅ CSRF 보호 + 유연성
'SameSite=Lax',  // 대부분의 웹 앱에 적합

// ⚠️ Strict는 너무 엄격
'SameSite=Strict',  // 특별한 이유가 있을 때만 사용

4. 쿠키 삭제 시 속성 일치

// ✅ 설정할 때와 삭제할 때 속성이 정확히 일치해야 함
const setCookie = 'token=xxx; HttpOnly; SameSite=Lax';
const deleteCookie = 'token=; HttpOnly; SameSite=Lax; Max-Age=0';

로컬스토리지 vs 쿠키 선택 가이드

저장소 용도 보안 Safari 호환성
HttpOnly Cookie 인증 토큰 높음 (XSS 방지) 조건부 설정 필요
LocalStorage 사용자 정보, 설정 ⚠️ 낮음 (XSS 취약) 호환성 좋음

원칙: 민감한 데이터(토큰)는 HttpOnly 쿠키, 일반 데이터는 LocalStorage


Safari 개발 시 주의사항

1. 쿠키 관련

  • HTTP 환경에서 Secure 속성 제거
  • 쿠키 설정과 삭제 시 속성 일치
  • SameSite=Lax 사용 권장

2. 네트워크 요청

// ✅ Safari는 credentials 설정에 민감
fetch('/api/auth/check', {
  method: 'GET',
  credentials: 'include',  // Safari에서 쿠키 전송 필수
});

3. 로컬스토리지

// ✅ Safari Private Mode에서 localStorage 제한
try {
  localStorage.setItem('key', 'value');
} catch (error) {
  // Safari Private Mode 대응
  console.warn('LocalStorage unavailable:', error);
}

4. 날짜/시간

// ❌ Safari에서 파싱 실패 가능
new Date('2024-01-01 12:00:00');

// ✅ ISO 8601 형식 사용
new Date('2024-01-01T12:00:00Z');

크로스 브라우저 테스트 도구

개발 환경 테스트

# Chrome
open -a "Google Chrome" http://localhost:3000

# Safari
open -a Safari http://localhost:3000

# Firefox
open -a Firefox http://localhost:3000

개발자 도구 활용

// Safari: Develop → Show Web Inspector → Storage
// Chrome: DevTools → Application → Cookies
// Firefox: DevTools → Storage → Cookies

// 쿠키 확인 사항:
// - Name: access_token, refresh_token
// - HttpOnly: ✅ 체크
// - Secure: 환경에 따라 조건부
// - SameSite: Lax

테스트 체크리스트

로그인 기능 테스트

Chrome

  • 로그인 성공
  • 대시보드 접근 가능
  • 쿠키 저장 확인 (DevTools → Application → Cookies)
  • HttpOnly 속성 확인
  • 로그아웃 성공
  • 쿠키 삭제 확인

Safari

  • 로그인 성공
  • 대시보드 접근 가능
  • 쿠키 저장 확인 (Web Inspector → Storage → Cookies)
  • HttpOnly 속성 확인
  • Secure 속성 없음 확인 (개발 환경)
  • 로그아웃 성공
  • 쿠키 삭제 확인

Firefox (선택)

  • 로그인 성공
  • 대시보드 접근 가능
  • 쿠키 저장 확인
  • 로그아웃 성공

인증 상태 확인 테스트

시나리오 1: 페이지 새로고침

  • Chrome: 로그인 상태 유지
  • Safari: 로그인 상태 유지
  • Firefox: 로그인 상태 유지

시나리오 2: 브라우저 재시작

  • Chrome: 로그인 상태 유지 (Remember me)
  • Safari: 로그인 상태 유지
  • Firefox: 로그인 상태 유지

시나리오 3: 토큰 만료

  • Chrome: 자동 토큰 갱신
  • Safari: 자동 토큰 갱신
  • Firefox: 자동 토큰 갱신

프로덕션 배포 전 체크리스트

환경 설정

  • NODE_ENV=production 설정 확인
  • HTTPS 인증서 설정 완료
  • 환경 변수 .env.production 확인

쿠키 설정 확인

  • Production 환경에서 Secure 속성 포함 확인
  • HttpOnly 속성 유지 확인
  • SameSite=Lax 설정 확인
  • Max-Age 적절히 설정 (access: 2h, refresh: 7d)

브라우저 테스트 (HTTPS)

  • Chrome: 로그인/로그아웃 정상
  • Safari: 로그인/로그아웃 정상
  • Firefox: 로그인/로그아웃 정상
  • Safari iOS: 모바일 테스트

문제 해결 가이드

쿠키가 저장되지 않는 경우

1. Safari 개발 환경

// 체크 포인트:
// ✅ Secure 속성이 조건부로 설정되어 있는가?
...(isProduction ? ['Secure'] : [])

// ✅ SameSite가 Lax인가?
'SameSite=Lax'

// ✅ HttpOnly는 포함되어 있는가?
'HttpOnly'

2. Safari Private Mode

Safari Private Mode에서는 일부 쿠키가 제한될 수 있습니다. → 일반 모드에서 테스트하세요.

3. 쿠키 도메인 설정

// ✅ localhost에서는 Domain 속성 생략
// ❌ 'Domain=localhost' (불필요)

쿠키가 삭제되지 않는 경우

Safari 로그아웃 문제

// ❌ 설정 시와 삭제 시 속성 불일치
// 설정: HttpOnly + SameSite=Lax
// 삭제: HttpOnly + Secure + SameSite=Strict

// ✅ 설정 시와 삭제 시 속성 일치
const isProduction = process.env.NODE_ENV === 'production';
const cookie = [
  'token=',
  'HttpOnly',
  ...(isProduction ? ['Secure'] : []),  // 일치
  'SameSite=Lax',                       // 일치
  'Max-Age=0',
].join('; ');

관련 문서


업데이트 히스토리

날짜 내용 작성자
2024-XX-XX Safari 쿠키 호환성 문서 작성 Claude

📌 기억하세요: 브라우저 관련 기능 개발 시 Safari를 기준으로 개발하면 다른 브라우저에서도 작동합니다!