백엔드가 중복 로그인 감지를 넣으면서, 내 토큰이 밀리면 이후 모든 요청이
403 + code 905(만료된 토큰입니다)로 떨어진다(JwtAuthenticationFilter).
어느 화면·어느 API에서 걸리든 같은 결말이어야 해서 backendFetch 한 곳에서
처리한다.
- 905/904를 받으면 /logout/revoked로 보낸다. 렌더 중에는 쿠키를 지울 수 없어
라우트 핸들러에서 세션을 파기하고 /login으로 안내와 함께 되돌린다.
- redirect()는 예외로 던져지므로 Server Action의 catch가 삼키면 안 된다.
저장소를 호출하는 catch에 unstable_rethrow를 넣어 통과시킨다.
- 그 경로는 쿠키가 이미 없어도 지나가야 안내가 유지돼 proxy 공개 경로에 넣는다.
Co-Authored-By: Claude Opus 5
시안: 관리자페이지(QsFVFFUKGH68xAPVkF3SJ9) login 5435:14598.
시안의 입력·라벨·버튼이 디자인시스템 컴포넌트 인스턴스라 @fox로 거의 그대로 덮인다 — 치수가
우연이 아니라 정확히 같다. FoxInput lg가 h56·px20·radius12·17px·아이콘 24, FoxButton lg가
h56·px20·radius12·17px다. `label`을 주면 FoxInput이 FoxFormLabel까지 함께 그린다.
- login.module.scss 신설. 남는 건 화면 셸(배경·중앙 정렬)과 카드(440·padding 40·radius 24·
2겹 그림자), 비밀번호 토글 자리뿐이라 @fox에 넣지 않고 화면 전용으로 뒀다(사용자 확정).
- 비밀번호 보기 토글은 로그인 전용이다(사용자 확정). FoxInput에 그 기능이 없고, `icon` 슬롯은
`aria-hidden="true"`라 버튼을 넣으면 보조기술에서 사라진다. 그래서 슬롯 대신 같은 자리에
버튼을 겹쳐 두고 aria-pressed·aria-controls로 상태와 대상을 알린다.
- 오류 문구는 FoxInput의 `message`가 아니라 시안의 안내문 자리에 함께 둔다. 어느 칸이 틀렸는지
알리지 않는 편이 맞고(계정 존재 여부가 새지 않는다), 입력칸 아래 메시지가 끼면 토글의 세로
위치도 어긋난다 — 토글을 아래에서 띄우므로 입력칸이 래퍼의 마지막 요소여야 한다.
- components/ui/auth-card를 더 이상 쓰지 않는다. 시안에 로고가 없고 그쪽은 Tailwind 제거 이후
무스타일이다(헤더·풋터와 같은 처리).
검증 — 실측이 시안과 일치한다. 배경 #f4f5f6, 카드 440/40/24/gap16 + 그림자 2겹
(1·1·4 / 4·4·8, alpha-black10), 제목 32px 800 #1e2124, 라벨 15px 600, 입력 56/12/20/#58616a,
버튼 56/12/#256ef4/17px, 토글 24×24가 입력칸 세로 정중앙(차이 0px)에 오른쪽 여백 20px.
토글은 password↔text 전환과 aria-pressed·aria-label 갱신까지 확인했다.
Co-Authored-By: Claude Opus 5
로그아웃 Server Action이 백엔드 호출 실패를 /login?logoutError=1로 알리면, 새 클라이언트
컴포넌트 LogoutNotice가 마운트 시 기존 FeedbackProvider/Toast로 경고 토스트를 띄우고
쿼리 파라미터를 제거한다. redirect()는 Server Action에서 즉시 던져져 클라이언트가 결과를
돌려받을 수 없으므로, 토스트 표시를 목적지 페이지 마운트 이후로 미뤄 redirect와 경쟁하지
않게 한다. 내부 백엔드 메시지는 노출하지 않고 고정 문구만 보여준다.
Co-Authored-By: Claude Opus 5