CLOVA Nursing AI Agent

router.replace()가 미들웨어를 건너뛰는 문제

로그인에 성공했는데도 router.replace()가 먹히지 않아 이전 "접근 불가" 상태가 그대로 남았다. 원인은 Next.js 라우터 캐시 — 미들웨어가 언제 건너뛰어지는지부터 다시 파야 했다.

핵심 요약

Next.js 공식 문서와 실제 동작에 기반한 결론은 다음과 같다.

  • router.replace()라우터 캐시가 있을 때만 서버 요청을 생략한다.
  • useRouter()로 방문한 페이지에만 라우터 캐시가 생성된다.
  • 인증 상태가 바뀌어도 라우터 캐시는 자동으로 갱신되지 않는다.
  • 미들웨어는 서버 요청이 있을 때만 실행된다.

그래서 인증 상태 변경 후에는 window.location.href로 전체 페이지를 새로고침하는 게 가장 확실하다.

❶ 라우터 캐시와 미들웨어의 기본 원리

라우터 캐시란?

  • useRouter()를 통한 클라이언트 측 탐색 결과를 저장하는 메커니즘
  • 페이지 데이터·렌더링 결과·라우팅 결정을 포함
  • 기본적으로 브라우저 세션 동안 유지됨

미들웨어란?

  • 서버 요청이 발생할 때, 페이지가 렌더링되기 전에 실행되는 코드
  • 인증 상태 확인·리다이렉션·헤더 수정 등을 수행
  • 중요: 서버 요청이 있을 때만 실행된다.

❷ 시나리오별 동작

  1. 첫 방문 (캐시 없음) — URL 직접 입력/링크 클릭. 서버 요청 → 미들웨어 실행 → 권한 확인. 아직 라우터 캐시 없음.
  2. useRouter() 첫 접근push/replace로 첫 접근. 캐시가 없으니 서버 요청 → 미들웨어 실행. 이때 해당 경로의 라우터 캐시가 생성되고, 미들웨어의 리다이렉션 결정("접근 불가")도 함께 캐시에 저장된다.
  3. useRouter() 재접근 — 캐시가 있으니 서버 요청 생략 → 미들웨어도 실행 안 됨. 인증이 바뀌어도 캐시는 자동으로 갱신되지 않는다.
  4. 방문했지만 useRouter()로 접근하지 않은 페이지 — 주소창 입력/<a> 링크로 방문한 페이지는 캐시가 생기지 않는다. 이후 replace()로 접근하면 캐시가 없어 서버 요청 → 미들웨어 실행.

❸ 흔한 오해 바로잡기

오해 1 — "router.replace()는 항상 미들웨어를 안 거친다" → 사실: 라우터 캐시가 있을 때만 서버 요청을 생략한다. 캐시가 없으면 replace()도 미들웨어를 거친다.

오해 2 — "이미 방문한 페이지는 항상 캐시가 있다" → 사실: useRouter()(push/replace)로 방문한 페이지만 캐시가 생긴다. 주소창 입력·<a> 링크 방문은 캐시가 생기지 않는다.

❹ 실제 사례

사례 1 — 환자 로그인·등록

  1. 로그인 후 router.replace('/patient') → 캐시 없음 → 서버 요청 → 미들웨어가 name이 null임을 확인 → /patient/admission으로 리다이렉트. "/patient 접근 불가" 결정이 캐시에 저장됨.
  2. 환자 등록 후 다시 router.replace('/patient') → 캐시 있음 → 서버 요청 생략 → 미들웨어 미실행 → 캐시된 "접근 불가"로 여전히 막힘.
  3. 해결: window.location.href = '/patient'로 강제 새로고침.

사례 2 — 관리자 로그인/로그아웃

  1. 로그인 상태로 /admin 주소창 접근 → 캐시 없음.
  2. /admin에서 router.replace('/admin/login') → 캐시 없음 → 서버 요청 → 미들웨어가 "이미 로그인" 확인 → /admin으로 리다이렉트. "/admin/login 접근 불가"가 캐시에 저장됨.
  3. 백엔드 로그아웃 후 다시 router.replace('/admin/login') → 캐시 있음 → 미들웨어 미실행 → 여전히 막힘. (쿠키가 삭제돼도 서버액션과 무관해 캐시는 갱신되지 않는다.)

❺ 라우터 캐시 무효화 방법

클라이언트 측

// 전체 페이지 새로고침 — 가장 확실
window.location.href = "/admin/login";
 
// router.refresh()는 현재 URL 캐시만 갱신 (대상 경로 캐시는 그대로)
router.refresh();
router.replace("/admin/login"); // 이 경로 캐시는 여전히 존재

document.cookie로 쿠키를 지워도 라우터 캐시는 무효화되지 않으며, HttpOnly 쿠키는 JS로 접근조차 안 된다.

서버 측 (서버액션)

revalidatePath("/admin/login"); // 특정 경로 캐시 무효화
// cookies.set/delete 시 관련 경로의 라우터 캐시가 자동 무효화됨
Next.js 라우터 캐시 무효화 문서
Next.js 공식 문서 — cookies.set/delete가 라우터 캐시를 무효화한다.

❻ 권장 구현

인증 상태가 바뀌는 동작(로그인/로그아웃/환자등록) 후에는 라우터 캐시를 무시하고 전체 새로고침한다.

const handleLogout = async () => {
  await mutateAsync({}); // 백엔드에서 쿠키 삭제
  // 라우터 캐시를 무시하고 새 서버 요청 생성
  window.location.href = "/admin/login";
};

배운 점

증상이 "특정 흐름(미들웨어 경유)에서만" 재현되면 로직보다 캐시를 먼저 의심한다. 프레임워크의 캐시 계층을 이해하면 "분명 바꿨는데 안 바뀌는" 버그를 구조적으로 설명하고 재발을 막을 수 있다.

참고