From 0155ee4ee77005382f013fd7298c6395fb1d2433 Mon Sep 17 00:00:00 2001
From: Lectom
Date: Wed, 20 May 2026 11:48:31 +0900
Subject: [PATCH] =?UTF-8?q?front=EB=A5=98=20=EA=B0=9C=EB=B0=9C=EB=AA=A8?=
=?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=EB=8A=94=20=EC=84=B8=EC=85=98=20?=
=?UTF-8?q?=EA=B0=B1=EC=8B=A0=20=EB=81=84=EA=B8=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/common/LanguageSelector.tsx | 5 ++
.../src/components/layout/AppLayout.tsx | 35 +++++++++++-
.../src/components/layout/RoleSwitcher.tsx | 6 ++-
adminfront/src/features/auth/LoginPage.tsx | 7 ++-
.../routes/UserGroupDetailPage.tsx | 9 +++-
adminfront/src/lib/apiClient.ts | 14 +++++
adminfront/src/lib/sessionSliding.test.ts | 53 +++++++++++++++++++
adminfront/src/lib/sessionSliding.ts | 3 ++
common/core/session/index.ts | 49 +++++++++++++++++
common/shell/index.ts | 18 ++++---
.../components/common/LanguageSelector.tsx | 5 ++
devfront/src/components/layout/AppLayout.tsx | 29 +++++++++-
devfront/src/lib/apiClient.ts | 19 ++++++-
.../components/common/LanguageSelector.tsx | 5 ++
orgfront/src/components/layout/AppLayout.tsx | 29 +++++++++-
orgfront/src/features/auth/AuthGuard.tsx | 5 +-
orgfront/src/lib/apiClient.ts | 19 ++++++-
17 files changed, 287 insertions(+), 23 deletions(-)
diff --git a/adminfront/src/components/common/LanguageSelector.tsx b/adminfront/src/components/common/LanguageSelector.tsx
index 7f905cd0..9612b744 100644
--- a/adminfront/src/components/common/LanguageSelector.tsx
+++ b/adminfront/src/components/common/LanguageSelector.tsx
@@ -2,6 +2,7 @@ import { useState } from "react";
import { t } from "../../lib/i18n";
const LOCALE_STORAGE_KEY = "locale";
+const LOCALE_CHANGED_EVENT = "baron_locale_changed";
const SUPPORTED_LOCALES = ["ko", "en"] as const;
type Locale = (typeof SUPPORTED_LOCALES)[number];
@@ -34,6 +35,10 @@ function LanguageSelector() {
}
window.localStorage.setItem(LOCALE_STORAGE_KEY, next);
setLocale(next);
+ if (import.meta.env.MODE === "development") {
+ window.dispatchEvent(new Event(LOCALE_CHANGED_EVENT));
+ return;
+ }
window.location.reload();
};
diff --git a/adminfront/src/components/layout/AppLayout.tsx b/adminfront/src/components/layout/AppLayout.tsx
index d775e05b..d8cdeca4 100644
--- a/adminfront/src/components/layout/AppLayout.tsx
+++ b/adminfront/src/components/layout/AppLayout.tsx
@@ -43,6 +43,9 @@ import {
import LanguageSelector from "../common/LanguageSelector";
import RoleSwitcher from "./RoleSwitcher";
+const LOCALE_CHANGED_EVENT = "baron_locale_changed";
+const DEV_ROLE_CHANGED_EVENT = "baron_dev_role_changed";
+
const staticNavItems: ShellSidebarNavItem[] = [
{
labelKey: "ui.admin.nav.overview",
@@ -127,6 +130,7 @@ function AppLayout() {
const isRenewInFlightRef = useRef(false);
const lastRenewAttemptAtRef = useRef(0);
const lastVisitedRouteRef = useRef(null);
+ const isDevelopmentRuntime = import.meta.env.MODE === "development";
const isDevRoleOverrideEnabled =
import.meta.env.MODE === "development" ||
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })
@@ -139,8 +143,9 @@ function AppLayout() {
: null;
const [theme, setTheme] = useState<"light" | "dark">(readShellTheme);
const [isProfileOpen, setIsProfileOpen] = useState(false);
- const [isSessionExpiryEnabled, setIsSessionExpiryEnabled] = useState(
- readShellSessionExpiryEnabled,
+ const [, setDevelopmentRenderRevision] = useState(0);
+ const [isSessionExpiryEnabled, setIsSessionExpiryEnabled] = useState(() =>
+ readShellSessionExpiryEnabled(!isDevelopmentRuntime),
);
const {
data: profile,
@@ -290,6 +295,27 @@ function AppLayout() {
applyShellTheme(theme);
}, [theme]);
+ useEffect(() => {
+ if (!isDevelopmentRuntime) {
+ return;
+ }
+
+ const rerenderDevelopmentShell = () => {
+ setDevelopmentRenderRevision((value) => value + 1);
+ };
+
+ window.addEventListener(LOCALE_CHANGED_EVENT, rerenderDevelopmentShell);
+ window.addEventListener(DEV_ROLE_CHANGED_EVENT, rerenderDevelopmentShell);
+
+ return () => {
+ window.removeEventListener(LOCALE_CHANGED_EVENT, rerenderDevelopmentShell);
+ window.removeEventListener(
+ DEV_ROLE_CHANGED_EVENT,
+ rerenderDevelopmentShell,
+ );
+ };
+ }, [isDevelopmentRuntime]);
+
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
@@ -355,6 +381,10 @@ function AppLayout() {
]);
useEffect(() => {
+ if (isDevelopmentRuntime) {
+ return;
+ }
+
const maybeKeepSessionAlive = async () => {
const now = Date.now();
if (
@@ -397,6 +427,7 @@ function AppLayout() {
auth.isAuthenticated,
auth.isLoading,
auth.user?.expires_at,
+ isDevelopmentRuntime,
isSessionExpiryEnabled,
]);
diff --git a/adminfront/src/components/layout/RoleSwitcher.tsx b/adminfront/src/components/layout/RoleSwitcher.tsx
index ea6405cd..38c122dd 100644
--- a/adminfront/src/components/layout/RoleSwitcher.tsx
+++ b/adminfront/src/components/layout/RoleSwitcher.tsx
@@ -3,6 +3,8 @@ import type { FC } from "react";
import { useEffect, useState } from "react";
import { t } from "../../lib/i18n";
+const DEV_ROLE_CHANGED_EVENT = "baron_dev_role_changed";
+
const RoleSwitcher: FC = () => {
const [currentRole, setCurrentRole] = useState("");
const [isOverrideEnabled, setIsOverrideEnabled] = useState(false);
@@ -31,13 +33,13 @@ const RoleSwitcher: FC = () => {
window.localStorage.setItem("X-Mock-Role-Enabled", "true");
setCurrentRole(role);
setIsOverrideEnabled(true);
- window.location.reload();
+ window.dispatchEvent(new Event(DEV_ROLE_CHANGED_EVENT));
};
const clearRoleOverride = () => {
window.localStorage.removeItem("X-Mock-Role-Enabled");
setIsOverrideEnabled(false);
- window.location.reload();
+ window.dispatchEvent(new Event(DEV_ROLE_CHANGED_EVENT));
};
if (import.meta.env.MODE === "production") return null;
diff --git a/adminfront/src/features/auth/LoginPage.tsx b/adminfront/src/features/auth/LoginPage.tsx
index cdd807ab..b0fcabf1 100644
--- a/adminfront/src/features/auth/LoginPage.tsx
+++ b/adminfront/src/features/auth/LoginPage.tsx
@@ -84,8 +84,11 @@ function LoginPage() {
variant="ghost"
className="p-0 h-auto text-destructive underline mt-2 hover:bg-transparent"
onClick={() => {
- window.location.href =
- window.location.origin + window.location.pathname;
+ void auth.signinRedirect({
+ state: {
+ returnTo,
+ },
+ });
}}
>
다시 시도하기
diff --git a/adminfront/src/features/user-groups/routes/UserGroupDetailPage.tsx b/adminfront/src/features/user-groups/routes/UserGroupDetailPage.tsx
index 3abdbd43..79f6c966 100644
--- a/adminfront/src/features/user-groups/routes/UserGroupDetailPage.tsx
+++ b/adminfront/src/features/user-groups/routes/UserGroupDetailPage.tsx
@@ -194,7 +194,14 @@ export function UserGroupDetailPage() {
"Not found"}
-