세션 쿠키 복호화 실패로 인한 페이지 크래시 수정
목차
세션 쿠키 복호화 과정에서 예상 밖의 값이 들어올 때 페이지 전체가 크래시 나는 문제를 고쳤다. safeAuth 래퍼를 도입해서 복호화 실패 시에도 안정적으로 폴백할 수 있게 했다.
문제: 쿠키 복호화는 왜 실패하나
세션 쿠키는 서버에서 발급했지만, 네트워크 전송 중 손상되거나, 사용자의 로컬 스토리지가 오염되거나, 혹은 브라우저 확장 프로그램이 개입하거나, 예전 버전에서 발급한 토큰이 여전히 남아있을 수 있다. 그 외에도:
- 암호화 알고리즘 버전 변경 후 구형 토큰 재생성
- 만료된 쿠키가 삭제 안 됨
- 보안 업데이트로 인한 키 로테이션 후 기존 토큰 무효화
- 사용자가 수동으로 쿠키를 편집한 경우
이런 상황들은 프로덕션에서 분명히 발생한다. 문제는 복호화 실패를 처리하지 않으면 예외가 던져져서 전체 페이지가 먹통이 되는 것. 특히 로그인 상태를 확인하는 로직이 앱 초기화 단계에서 실행된다면, 사용자는 아무것도 할 수 없는 상황에 빠진다.
해결책: 안전한 복호화 래퍼 도입
이번 작업에서 safeAuth라는 개념을 도입했다. 복호화 시도를 try-catch로 감싸면서, 실패 시에는:
- 에러를 로깅하되 사용자 경험은 깨뜨리지 않음
- 기본값(예: 미인증 상태)으로 폴백
- 쿠키를 안전하게 초기화 (무효 쿠키 제거)
| 시나리오 | 이전 동작 | 개선 후 |
|---|---|---|
| 유효한 쿠키 | 정상 복호화 | 정상 복호화 |
| 손상된 쿠키 | ❌ 크래시 | ✅ 미인증 상태로 폴백 |
| 만료된 쿠키 | ❌ 크래시 | ✅ 쿠키 제거 후 로그인 화면 |
파일별 역할
src/auth.ts — 복호화 로직의 중심. safeAuth 함수를 여기서 정의해서 모든 복호화 시도를 감싼다. 에러 핸들링과 로깅이 여기서 일어난다.
src/app/login-bar.tsx — UI 렌더링 로직. 예전에는 "undefined나 null이 나올 리 없다"고 가정했겠지만, 이제는 safeAuth의 결과(정상 또는 폴백)를 신뢰할 수 있다. 컴포넌트는 항상 유효한 상태를 받으므로 방어 코드를 줄일 수 있다.
외부 입력 처리의 원칙
쿠키, URL 파라미터, 로컬 스토리지, API 응답처럼 외부(또는 사용자)에서 오는 데이터를 처리할 때는 항상 "실패할 가능성"을 전제해야 한다:
// ❌ 위험한 패턴: 성공만 가정
const sessionData = JSON.parse(cookie)
const userId = decrypt(sessionData.token)
// ✅ 안전한 패턴: 실패를 처리
function safeAuth(rawCookie) {
try {
const sessionData = JSON.parse(rawCookie)
return decrypt(sessionData.token)
} catch (e) {
log.warn('복호화 실패', e)
return null // 또는 기본값
}
}
이렇게 하면 에러가 발생하는 지점을 명확히 알 수 있고, 테스트도 수월하다.
회고: 왜 처음부터 이렇게 안 했을까
지금 생각해보니 이런 실패 케이스는 충분히 예상 가능했다. 아마 초기 개발 시점에서는 "우리는 항상 유효한 쿠키만 발급한다"는 낙관적 가정을 했고, 프로덕션 데이터의 복잡성을 과소평가했던 것 같다.
팀 관점에서도 배운 점이 있다. 외부 입력 처리는 코드 리뷰 때 특히 신경 써야 한다. "이게 실패할 수 있나?" 라는 질문을 매번 던져야 한다. 또한 프로덕션 에러 로그를 정기적으로 살펴보면서 이런 숨겨진 가정들을 발견하는 게 중요하다.
앞으로는 인증, 세션, 토큰 같은 민감한 영역에서는 처음부터 defensive 코드를 짜려고 한다. 사용자가 페이지를 열 수 없게 되는 상황만큼은 피해야 니까.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.