Skip to content

Commit bb93ea8

Browse files
authored
Merge pull request #621 from solid-connection/fix/admin-clear-session-env-loss
🐛 세션 정리 시 어드민 API 환경 선택이 초기화되는 문제 수정
2 parents 8f2c130 + 9333c35 commit bb93ea8

4 files changed

Lines changed: 38 additions & 48 deletions

File tree

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,59 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
22

3-
const { removeAccessToken, removeAdminApiEnvironment, saveAdminApiEnvironment } = vi.hoisted(() => ({
3+
const { removeAccessToken } = vi.hoisted(() => ({
44
removeAccessToken: vi.fn(),
5-
removeAdminApiEnvironment: vi.fn(),
6-
saveAdminApiEnvironment: vi.fn(),
75
}));
86

97
vi.mock("@/lib/api/auth", () => ({
108
reissueAccessTokenApi: vi.fn(),
119
}));
1210

13-
vi.mock("@/lib/utils/localStorage", () => ({
14-
loadAccessToken: vi.fn(),
15-
removeAccessToken,
16-
removeAdminApiEnvironment,
17-
saveAccessToken: vi.fn(),
18-
saveAdminApiEnvironment,
19-
}));
11+
// 환경(stage/prod) 저장·조회 함수는 실제 구현(jsdom localStorage)을 그대로 사용해,
12+
// clearSession()이 저장된 환경 값을 건드리지 않는지 회귀 테스트로 검증한다.
13+
vi.mock("@/lib/utils/localStorage", async (importOriginal) => {
14+
const actual = await importOriginal<typeof import("@/lib/utils/localStorage")>();
15+
return {
16+
...actual,
17+
loadAccessToken: vi.fn(),
18+
removeAccessToken,
19+
saveAccessToken: vi.fn(),
20+
};
21+
});
2022

2123
describe("switchAdminApiEnvironment", () => {
2224
beforeEach(() => {
2325
removeAccessToken.mockReset();
24-
removeAdminApiEnvironment.mockReset();
25-
saveAdminApiEnvironment.mockReset();
26+
localStorage.clear();
2627
});
2728

28-
it("세션(access token, 환경 값)을 모두 지운 뒤 새 환경을 저장한다", async () => {
29+
it("access token을 지운 뒤 새 환경을 저장하고 로그인 페이지로 리다이렉트한다", async () => {
2930
const { switchAdminApiEnvironment } = await import("./session");
31+
const { loadAdminApiEnvironment } = await import("@/lib/utils/localStorage");
3032
const redirect = vi.fn();
31-
const callOrder: string[] = [];
32-
33-
removeAdminApiEnvironment.mockImplementation(() => callOrder.push("removeAdminApiEnvironment"));
34-
saveAdminApiEnvironment.mockImplementation(() => callOrder.push("saveAdminApiEnvironment"));
3533

3634
switchAdminApiEnvironment("prod", redirect);
3735

3836
expect(removeAccessToken).toHaveBeenCalledOnce();
39-
expect(removeAdminApiEnvironment).toHaveBeenCalledOnce();
40-
expect(saveAdminApiEnvironment).toHaveBeenCalledWith("prod");
41-
expect(callOrder).toEqual(["removeAdminApiEnvironment", "saveAdminApiEnvironment"]);
37+
expect(loadAdminApiEnvironment()).toBe("prod");
4238
expect(redirect).toHaveBeenCalledWith("/auth/login");
4339
});
4440
});
41+
42+
describe("clearSession", () => {
43+
beforeEach(() => {
44+
removeAccessToken.mockReset();
45+
localStorage.clear();
46+
});
47+
48+
it("access token은 지우지만 저장된 어드민 API 환경 값은 유지한다 (회귀 테스트)", async () => {
49+
const { clearSession } = await import("./session");
50+
const { loadAdminApiEnvironment, saveAdminApiEnvironment } = await import("@/lib/utils/localStorage");
51+
52+
saveAdminApiEnvironment("stage");
53+
54+
clearSession();
55+
56+
expect(removeAccessToken).toHaveBeenCalledOnce();
57+
expect(loadAdminApiEnvironment()).toBe("stage");
58+
});
59+
});

apps/admin/src/lib/auth/session.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
import { reissueAccessTokenApi } from "@/lib/api/auth";
22
import type { AdminApiEnvironment } from "@/lib/auth/environment";
33
import { isTokenExpired } from "@/lib/utils/jwtUtils";
4-
import {
5-
loadAccessToken,
6-
removeAccessToken,
7-
removeAdminApiEnvironment,
8-
saveAccessToken,
9-
saveAdminApiEnvironment,
10-
} from "@/lib/utils/localStorage";
4+
import { loadAccessToken, removeAccessToken, saveAccessToken, saveAdminApiEnvironment } from "@/lib/utils/localStorage";
115

126
let reissuePromise: Promise<string | null> | null = null;
137
let sessionVersion = 0;
@@ -30,13 +24,11 @@ export const clearSession = () => {
3024
sessionVersion += 1;
3125
reissuePromise = null;
3226
removeAccessToken();
33-
removeAdminApiEnvironment();
3427
};
3528

3629
/**
37-
* 어드민 API 환경(stage/prod)을 전환한다. clearSession()이 환경 저장 키까지 함께 지우므로,
38-
* 반드시 clearSession → saveAdminApiEnvironment 순서로 처리해야 새 환경 값이 유지된다.
39-
* 이후 react-query 캐시 등 메모리 상태를 확실히 비우기 위해 전체 페이지 이동으로 리다이렉트한다.
30+
* 어드민 API 환경(stage/prod)을 전환한다. 세션(access token)을 정리한 뒤 새 환경 값을 저장하고,
31+
* react-query 캐시 등 메모리 상태를 확실히 비우기 위해 전체 페이지 이동으로 리다이렉트한다.
4032
* redirect는 테스트에서 주입할 수 있도록 매개변수로 분리했다.
4133
*/
4234
export const switchAdminApiEnvironment = (

apps/admin/src/lib/utils/localStorage.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
loadAccessToken,
55
loadAdminApiEnvironment,
66
removeAccessToken,
7-
removeAdminApiEnvironment,
87
saveAccessToken,
98
saveAdminApiEnvironment,
109
} from "./localStorage";
@@ -55,14 +54,6 @@ describe("어드민 API 환경 localStorage 저장", () => {
5554
expect(loadAdminApiEnvironment()).toBe("prod");
5655
});
5756

58-
it("환경 값을 제거한다", () => {
59-
saveAdminApiEnvironment("stage");
60-
61-
removeAdminApiEnvironment();
62-
63-
expect(loadAdminApiEnvironment()).toBeNull();
64-
});
65-
6657
it("레거시 값 'dev'는 'stage'로 해석한다", () => {
6758
localStorage.setItem(ADMIN_API_ENVIRONMENT_KEY, "dev");
6859

apps/admin/src/lib/utils/localStorage.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,3 @@ export const saveAdminApiEnvironment = (environment: AdminApiEnvironment) => {
5353
console.error("Could not save admin api environment", err);
5454
}
5555
};
56-
57-
export const removeAdminApiEnvironment = () => {
58-
try {
59-
localStorage.removeItem(ADMIN_API_ENVIRONMENT_KEY);
60-
} catch (err) {
61-
console.error("Could not remove admin api environment", err);
62-
}
63-
};

0 commit comments

Comments
 (0)