- middleware: User-Agent 가 모바일이면 /login → /m/login 으로 redirect (서버 측 분기) - auth-store.logout: window 가 모바일이면 /m/login, 아니면 /login (클라이언트 측 분기) - BackButtonGuard: TWA 일부 환경에서 display-mode 가 standalone 으로 보고되지 않는 케이스 대응 — fullscreen/minimal-ui 도 포함, 모바일 UA 면 무조건 활성화 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,13 +12,16 @@ import Swal from "sweetalert2";
|
|||||||
*/
|
*/
|
||||||
export default function BackButtonGuard() {
|
export default function BackButtonGuard() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// standalone 모드 (PWA 설치 또는 TWA) 인지 확인
|
// standalone(PWA/TWA) 이거나 모바일 UA 면 활성화
|
||||||
|
// (TWA 일부 환경에서 display-mode 가 standalone 으로 보고되지 않는 케이스 대응)
|
||||||
const isStandalone =
|
const isStandalone =
|
||||||
window.matchMedia("(display-mode: standalone)").matches ||
|
window.matchMedia("(display-mode: standalone)").matches ||
|
||||||
// iOS Safari
|
window.matchMedia("(display-mode: fullscreen)").matches ||
|
||||||
|
window.matchMedia("(display-mode: minimal-ui)").matches ||
|
||||||
(window.navigator as Navigator & { standalone?: boolean }).standalone === true;
|
(window.navigator as Navigator & { standalone?: boolean }).standalone === true;
|
||||||
|
const isMobileUA = /Mobile|Android|iPhone|iPad|iPod/i.test(window.navigator.userAgent);
|
||||||
|
|
||||||
if (!isStandalone) return;
|
if (!isStandalone && !isMobileUA) return;
|
||||||
|
|
||||||
let lastBackPress = 0;
|
let lastBackPress = 0;
|
||||||
let toastTimer: ReturnType<typeof setTimeout> | null = null;
|
let toastTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ export function middleware(request: NextRequest) {
|
|||||||
return NextResponse.redirect(new URL("/m/dashboard", request.url));
|
return NextResponse.redirect(new URL("/m/dashboard", request.url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 모바일(앱/모바일브라우저)에서 /login 접근 시 /m/login 으로 전환
|
||||||
|
if (pathname === "/login") {
|
||||||
|
const ua = request.headers.get("user-agent") || "";
|
||||||
|
const isMobile = /Mobile|Android|iPhone|iPad|iPod/i.test(ua);
|
||||||
|
if (isMobile) {
|
||||||
|
return NextResponse.redirect(new URL("/m/login", request.url));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (publicPaths.some((p) => pathname.startsWith(p))) {
|
if (publicPaths.some((p) => pathname.startsWith(p))) {
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|||||||
logout: async () => {
|
logout: async () => {
|
||||||
await fetch("/api/auth/logout", { method: "POST" });
|
await fetch("/api/auth/logout", { method: "POST" });
|
||||||
set({ user: null });
|
set({ user: null });
|
||||||
window.location.href = "/login";
|
// 모바일(앱/모바일브라우저) → /m/login, 데스크톱 → /login
|
||||||
|
const isMobile =
|
||||||
|
typeof window !== "undefined" &&
|
||||||
|
(window.matchMedia("(max-width: 768px)").matches ||
|
||||||
|
/Mobile|Android|iPhone|iPad|iPod/i.test(window.navigator.userAgent));
|
||||||
|
window.location.href = isMobile ? "/m/login" : "/login";
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchUser: async () => {
|
fetchUser: async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user