diff --git a/apps/_template/tsconfig.json b/apps/_template/tsconfig.json index dab9b0b8e..366c4b4eb 100644 --- a/apps/_template/tsconfig.json +++ b/apps/_template/tsconfig.json @@ -19,11 +19,21 @@ } ], "paths": { - "@/*": ["./src/*"], - "@common/components": ["../../packages/common/components/dist/"], - "@common/network": ["../../packages/common/network/"] + "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/as/package.json b/apps/as/package.json index cebdf28ee..ed4016352 100644 --- a/apps/as/package.json +++ b/apps/as/package.json @@ -32,7 +32,6 @@ "next": "15.5.18", "react-hook-form": "^7.54.2", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "zod": "^3.24.2" }, "devDependencies": { diff --git a/apps/as/src/entities/auth/api/auth-service.ts b/apps/as/src/entities/auth/api/auth-service.ts index b7158bf80..527d72e7a 100644 --- a/apps/as/src/entities/auth/api/auth-service.ts +++ b/apps/as/src/entities/auth/api/auth-service.ts @@ -1,7 +1,11 @@ import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config"; -import { type AuthPayload, type AuthService } from "@common/auth"; +import { + type AuthPayload, + type AuthService, + clearDeviceId, + getOrCreateDeviceId, +} from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, setToken, tokenManager } from "../lib/token-manager"; async function login(payload: AuthPayload): Promise { diff --git a/apps/as/src/entities/auth/lib/device-id.ts b/apps/as/src/entities/auth/lib/device-id.ts deleted file mode 100644 index ce173b141..000000000 --- a/apps/as/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/as/tsconfig.json b/apps/as/tsconfig.json index c1334095f..2495f810c 100644 --- a/apps/as/tsconfig.json +++ b/apps/as/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -19,9 +23,23 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/cm/package.json b/apps/cm/package.json index 1f9f98815..0440c504c 100644 --- a/apps/cm/package.json +++ b/apps/cm/package.json @@ -47,7 +47,6 @@ "sonner": "^2.0.1", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "zod": "^3.24.2", "zustand": "^5.0.3" }, diff --git a/apps/cm/src/entities/auth/api/auth-service.ts b/apps/cm/src/entities/auth/api/auth-service.ts index 073f95fe2..79d99c1e8 100644 --- a/apps/cm/src/entities/auth/api/auth-service.ts +++ b/apps/cm/src/entities/auth/api/auth-service.ts @@ -5,18 +5,17 @@ import { ENDPOINT, } from "@/shared/config"; import { + clearDeviceId, + getOrCreateDeviceId, type AuthPayload, type AuthService, - getRemainingMs, } from "@common/auth"; import { clearRefreshTimer, setToken, - setupRefreshTimer, tokenManager, } from "../lib/token-manager"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; function completeSession(accessToken: string, tokenId: string): void { setToken(CLAIM_NAME, accessToken); diff --git a/apps/cm/src/entities/auth/lib/device-id.ts b/apps/cm/src/entities/auth/lib/device-id.ts deleted file mode 100644 index 3683c7189..000000000 --- a/apps/cm/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/cm/tsconfig.json b/apps/cm/tsconfig.json index 05cbfce70..6814b6dd3 100644 --- a/apps/cm/tsconfig.json +++ b/apps/cm/tsconfig.json @@ -3,7 +3,11 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,21 +27,23 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, "include": [ - ".eslintrc.*", "next-env.d.ts", "**/*.ts", "**/*.tsx", - "**/*.mts", - "**/*.js", - "**/*.cjs", - "**/*.mjs", - "**/*.jsx", - "**/*.json", ".next/types/**/*.ts" ], - "exclude": ["**/node_modules", "**/.*/*"] + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/common/features/tsconfig.json b/apps/common/features/tsconfig.json index f12491cae..c5460bdc2 100644 --- a/apps/common/features/tsconfig.json +++ b/apps/common/features/tsconfig.json @@ -22,26 +22,17 @@ "name": "next" } ], - "baseUrl": "./src", "paths": { - "@/*": ["./*"], - "@common/assets": ["../../../../packages/common/assets/dist/"], - "@common/components": ["../../../../packages/common/components/dist/"], - "@common/network": ["../../../../packages/common/network/"] + "@/*": ["./src/*"] } }, - "include": [ - ".eslintrc.*", - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.mts", - "**/*.js", - "**/*.cjs", - "**/*.mjs", - "**/*.jsx", - "**/*.json", - ".next/types/**/*.ts" - ], - "exclude": ["**/node_modules", "**/.*/*"] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/cs/package.json b/apps/cs/package.json index 710032627..d362d1178 100644 --- a/apps/cs/package.json +++ b/apps/cs/package.json @@ -45,7 +45,6 @@ "sonner": "^2.0.1", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "zod": "^3.24.2", "zustand": "^5.0.3" }, diff --git a/apps/cs/src/entities/auth/api/auth-service.ts b/apps/cs/src/entities/auth/api/auth-service.ts index 93c0bc0bb..ce148e37d 100644 --- a/apps/cs/src/entities/auth/api/auth-service.ts +++ b/apps/cs/src/entities/auth/api/auth-service.ts @@ -4,9 +4,13 @@ import { CLAIM_NAME, ENDPOINT, } from "@/shared/config"; -import { type AuthPayload, type AuthService } from "@common/auth"; +import { + type AuthPayload, + type AuthService, + clearDeviceId, + getOrCreateDeviceId, +} from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, setToken, diff --git a/apps/cs/src/entities/auth/lib/device-id.ts b/apps/cs/src/entities/auth/lib/device-id.ts deleted file mode 100644 index 3683c7189..000000000 --- a/apps/cs/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/cs/tsconfig.json b/apps/cs/tsconfig.json index 05cbfce70..6814b6dd3 100644 --- a/apps/cs/tsconfig.json +++ b/apps/cs/tsconfig.json @@ -3,7 +3,11 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,21 +27,23 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, "include": [ - ".eslintrc.*", "next-env.d.ts", "**/*.ts", "**/*.tsx", - "**/*.mts", - "**/*.js", - "**/*.cjs", - "**/*.mjs", - "**/*.jsx", - "**/*.json", ".next/types/**/*.ts" ], - "exclude": ["**/node_modules", "**/.*/*"] + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/fo/package.json b/apps/fo/package.json index 84b192049..8116359df 100644 --- a/apps/fo/package.json +++ b/apps/fo/package.json @@ -35,8 +35,7 @@ "react-hook-form": "^7.54.2", "zod": "^3.24.2", "axios": "^1.8.1", - "proj4": "^2.9.0", - "uuid": "^14.0.1" + "proj4": "^2.9.0" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/apps/fo/src/entities/auth/api/auth-service.ts b/apps/fo/src/entities/auth/api/auth-service.ts index 2ea6bab12..3985fca8c 100644 --- a/apps/fo/src/entities/auth/api/auth-service.ts +++ b/apps/fo/src/entities/auth/api/auth-service.ts @@ -1,47 +1,82 @@ import { - ACCESS_TOKEN, - AUTH_USER_ID, - CLAIM_NAME, - ENDPOINT, + ACCESS_TOKEN, + AUTH_USER_ID, + CLAIM_NAME, + ENDPOINT, } from "@/src/shared/config"; import { getCookie } from "@/src/shared/lib"; import { - type AuthPayload, - type AuthService, - getRemainingMs, + clearDeviceId, + getOrCreateDeviceId, + type AuthPayload, + type AuthService, + getRemainingMs, } from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { - clearRefreshTimer, - setToken, - setupRefreshTimer, - tokenManager, + clearRefreshTimer, + setToken, + setupRefreshTimer, + tokenManager, } from "../lib/token-manager"; - import { clearTabStorePersist } from "@/src/store/tab-store"; async function login(payload: AuthPayload): Promise { - const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { - method: "POST", + const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + + const accessToken = data[ACCESS_TOKEN]; + const tokenId = data[AUTH_USER_ID]; + + if (!accessToken || !tokenId) { + throw new Error("인증 토큰을 받지 못했습니다."); + } + + const prevTokenId = getCookie(AUTH_USER_ID); + if (prevTokenId !== undefined && prevTokenId !== tokenId) { + clearTabStorePersist(); + } + + setToken(CLAIM_NAME, accessToken); + setToken(AUTH_USER_ID, tokenId); + + const remainingMs = getRemainingMs(accessToken); + if (remainingMs > 0) { + setupRefreshTimer(silentRefresh, remainingMs); + } +} + +async function silentRefresh(): Promise { + console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); + try { + const response = await fetch( + `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + { + method: "PUT", headers: { - "Content-Type": "application/json", - "X-Device-Id": getOrCreateDeviceId(), + "X-Device-Id": getOrCreateDeviceId(), }, - body: JSON.stringify(payload), - }); + }, + ); const data = await response.json(); + if (!response.ok) { + throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); + } + const accessToken = data[ACCESS_TOKEN]; const tokenId = data[AUTH_USER_ID]; if (!accessToken || !tokenId) { - throw new Error("인증 토큰을 받지 못했습니다."); - } - - const prevTokenId = getCookie(AUTH_USER_ID); - if (prevTokenId !== undefined && prevTokenId !== tokenId) { - clearTabStorePersist(); + throw new Error("갱신된 인증 토큰을 받지 못했습니다."); } setToken(CLAIM_NAME, accessToken); @@ -49,67 +84,32 @@ async function login(payload: AuthPayload): Promise { const remainingMs = getRemainingMs(accessToken); if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); + setupRefreshTimer(silentRefresh, remainingMs); } -} - -async function silentRefresh(): Promise { - console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); - try { - const response = await fetch( - `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { - method: "PUT", - headers: { - "X-Device-Id": getOrCreateDeviceId(), - }, - }, - ); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); - } - - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; - - if (!accessToken || !tokenId) { - throw new Error("갱신된 인증 토큰을 받지 못했습니다."); - } - - setToken(CLAIM_NAME, accessToken); - setToken(AUTH_USER_ID, tokenId); - - const remainingMs = getRemainingMs(accessToken); - if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); - } - } catch (error) { - console.error( - `[silentRefresh] Failed at ${new Date().toISOString()}`, - error, - ); - tokenManager.clearAuthToken(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + } catch (error) { + console.error( + `[silentRefresh] Failed at ${new Date().toISOString()}`, + error, + ); + tokenManager.clearAuthToken(); + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); } + } } export async function logout(): Promise { - tokenManager.clearAuthToken(); - clearDeviceId(); + tokenManager.clearAuthToken(); + clearDeviceId(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); + } } export const authService: AuthService = { - login, - silentRefresh, - declineSessionExtend: clearRefreshTimer, - logout, + login, + silentRefresh, + declineSessionExtend: clearRefreshTimer, + logout, }; diff --git a/apps/fo/src/entities/auth/lib/device-id.ts b/apps/fo/src/entities/auth/lib/device-id.ts deleted file mode 100644 index f844fcbef..000000000 --- a/apps/fo/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/sn/package.json b/apps/sn/package.json index 8cfd172aa..cc7c4e17f 100644 --- a/apps/sn/package.json +++ b/apps/sn/package.json @@ -37,7 +37,6 @@ "react-hook-form": "^7.54.2", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "xlsx": "^0.18.5", "xlsx-populate": "^1.21.0", "zod": "^3.24.2", diff --git a/apps/sn/src/entities/auth/api/auth-service.ts b/apps/sn/src/entities/auth/api/auth-service.ts index 93b158ef9..0320aa474 100644 --- a/apps/sn/src/entities/auth/api/auth-service.ts +++ b/apps/sn/src/entities/auth/api/auth-service.ts @@ -1,47 +1,83 @@ import { - ACCESS_TOKEN, - AUTH_USER_ID, - CLAIM_NAME, - ENDPOINT, + ACCESS_TOKEN, + AUTH_USER_ID, + CLAIM_NAME, + ENDPOINT, } from "@/shared/config"; import { getCookie } from "@/shared/lib"; import { - type AuthPayload, - type AuthService, - getRemainingMs, + clearDeviceId, + getOrCreateDeviceId, + type AuthPayload, + type AuthService, + getRemainingMs, } from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { - clearRefreshTimer, - setToken, - setupRefreshTimer, - tokenManager, + clearRefreshTimer, + setToken, + setupRefreshTimer, + tokenManager, } from "../lib/token-manager"; import { clearTabStorePersist } from "../../../store/tab-store"; async function login(payload: AuthPayload): Promise { - const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { - method: "POST", + const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + + const accessToken = data[ACCESS_TOKEN]; + const tokenId = data[AUTH_USER_ID]; + + if (!accessToken || !tokenId) { + throw new Error("인증 토큰을 받지 못했습니다."); + } + + const prevTokenId = getCookie(AUTH_USER_ID); + if (prevTokenId !== undefined && prevTokenId !== tokenId) { + clearTabStorePersist(); + } + + setToken(CLAIM_NAME, accessToken); + setToken(AUTH_USER_ID, tokenId); + + const remainingMs = getRemainingMs(accessToken); + if (remainingMs > 0) { + setupRefreshTimer(silentRefresh, remainingMs); + } +} + +async function silentRefresh(): Promise { + console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); + try { + const response = await fetch( + `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + { + method: "PUT", headers: { - "Content-Type": "application/json", - "X-Device-Id": getOrCreateDeviceId(), + "X-Device-Id": getOrCreateDeviceId(), }, - body: JSON.stringify(payload), - }); + }, + ); const data = await response.json(); + if (!response.ok) { + throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); + } + const accessToken = data[ACCESS_TOKEN]; const tokenId = data[AUTH_USER_ID]; if (!accessToken || !tokenId) { - throw new Error("인증 토큰을 받지 못했습니다."); - } - - const prevTokenId = getCookie(AUTH_USER_ID); - if (prevTokenId !== undefined && prevTokenId !== tokenId) { - clearTabStorePersist(); + throw new Error("갱신된 인증 토큰을 받지 못했습니다."); } setToken(CLAIM_NAME, accessToken); @@ -49,67 +85,32 @@ async function login(payload: AuthPayload): Promise { const remainingMs = getRemainingMs(accessToken); if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); + setupRefreshTimer(silentRefresh, remainingMs); } -} - -async function silentRefresh(): Promise { - console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); - try { - const response = await fetch( - `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { - method: "PUT", - headers: { - "X-Device-Id": getOrCreateDeviceId(), - }, - }, - ); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); - } - - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; - - if (!accessToken || !tokenId) { - throw new Error("갱신된 인증 토큰을 받지 못했습니다."); - } - - setToken(CLAIM_NAME, accessToken); - setToken(AUTH_USER_ID, tokenId); - - const remainingMs = getRemainingMs(accessToken); - if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); - } - } catch (error) { - console.error( - `[silentRefresh] Failed at ${new Date().toISOString()}`, - error, - ); - tokenManager.clearAuthToken(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + } catch (error) { + console.error( + `[silentRefresh] Failed at ${new Date().toISOString()}`, + error, + ); + tokenManager.clearAuthToken(); + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); } + } } export async function logout(): Promise { - tokenManager.clearAuthToken(); - clearDeviceId(); + tokenManager.clearAuthToken(); + clearDeviceId(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); + } } export const authService: AuthService = { - login, - silentRefresh, - declineSessionExtend: clearRefreshTimer, - logout, + login, + silentRefresh, + declineSessionExtend: clearRefreshTimer, + logout, }; diff --git a/apps/sn/src/entities/auth/lib/device-id.ts b/apps/sn/src/entities/auth/lib/device-id.ts deleted file mode 100644 index f844fcbef..000000000 --- a/apps/sn/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/st/package.json b/apps/st/package.json index 08f5bde6b..7ae6c9f53 100644 --- a/apps/st/package.json +++ b/apps/st/package.json @@ -57,7 +57,6 @@ "swr": "^2.3.7", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "xlsx": "^0.18.5", "zod": "^3.24.2", "zustand": "^5.0.3" diff --git a/apps/st/src/entities/auth/api/auth-service.ts b/apps/st/src/entities/auth/api/auth-service.ts index c79034a70..0c3725d22 100644 --- a/apps/st/src/entities/auth/api/auth-service.ts +++ b/apps/st/src/entities/auth/api/auth-service.ts @@ -5,9 +5,13 @@ import { ENDPOINT, } from "@/shared/config"; import { getCookie } from "@/shared/lib"; -import { type AuthPayload, type AuthService } from "@common/auth"; +import { + type AuthPayload, + type AuthService, + clearDeviceId, + getOrCreateDeviceId, +} from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { clearRefreshTimer, setToken, diff --git a/apps/st/src/entities/auth/lib/device-id.ts b/apps/st/src/entities/auth/lib/device-id.ts deleted file mode 100644 index a3b1aa800..000000000 --- a/apps/st/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/apps/st/tsconfig.json b/apps/st/tsconfig.json index d5062feda..6814b6dd3 100644 --- a/apps/st/tsconfig.json +++ b/apps/st/tsconfig.json @@ -1,43 +1,49 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "verbatimModuleSyntax": true, - "strictNullChecks": true, - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": [ - ".eslintrc.*", - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.mts", - "**/*.js", - "**/*.cjs", - "**/*.mjs", - "**/*.jsx", - "**/*.json", - ".next/types/**/*.ts" + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" ], - "exclude": ["**/node_modules", "**/.*/*"] + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "verbatimModuleSyntax": true, + "strictNullChecks": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ] } diff --git a/apps/wr/package.json b/apps/wr/package.json index 812bc6fa0..3ec9cecfb 100644 --- a/apps/wr/package.json +++ b/apps/wr/package.json @@ -49,7 +49,6 @@ "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", - "uuid": "^14.0.1", "xlsx": "^0.18.5", "xlsx-populate": "^1.21.0", "zod": "^3.24.2", diff --git a/apps/wr/src/entities/auth/api/auth-service.ts b/apps/wr/src/entities/auth/api/auth-service.ts index 2ea6bab12..4a6ac5fc5 100644 --- a/apps/wr/src/entities/auth/api/auth-service.ts +++ b/apps/wr/src/entities/auth/api/auth-service.ts @@ -1,47 +1,83 @@ import { - ACCESS_TOKEN, - AUTH_USER_ID, - CLAIM_NAME, - ENDPOINT, + ACCESS_TOKEN, + AUTH_USER_ID, + CLAIM_NAME, + ENDPOINT, } from "@/src/shared/config"; import { getCookie } from "@/src/shared/lib"; import { - type AuthPayload, - type AuthService, - getRemainingMs, + clearDeviceId, + getOrCreateDeviceId, + type AuthPayload, + type AuthService, + getRemainingMs, } from "@common/auth"; -import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id"; import { - clearRefreshTimer, - setToken, - setupRefreshTimer, - tokenManager, + clearRefreshTimer, + setToken, + setupRefreshTimer, + tokenManager, } from "../lib/token-manager"; import { clearTabStorePersist } from "@/src/store/tab-store"; async function login(payload: AuthPayload): Promise { - const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { - method: "POST", + const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Device-Id": getOrCreateDeviceId(), + }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + + const accessToken = data[ACCESS_TOKEN]; + const tokenId = data[AUTH_USER_ID]; + + if (!accessToken || !tokenId) { + throw new Error("인증 토큰을 받지 못했습니다."); + } + + const prevTokenId = getCookie(AUTH_USER_ID); + if (prevTokenId !== undefined && prevTokenId !== tokenId) { + clearTabStorePersist(); + } + + setToken(CLAIM_NAME, accessToken); + setToken(AUTH_USER_ID, tokenId); + + const remainingMs = getRemainingMs(accessToken); + if (remainingMs > 0) { + setupRefreshTimer(silentRefresh, remainingMs); + } +} + +async function silentRefresh(): Promise { + console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); + try { + const response = await fetch( + `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + { + method: "PUT", headers: { - "Content-Type": "application/json", - "X-Device-Id": getOrCreateDeviceId(), + "X-Device-Id": getOrCreateDeviceId(), }, - body: JSON.stringify(payload), - }); + }, + ); const data = await response.json(); + if (!response.ok) { + throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); + } + const accessToken = data[ACCESS_TOKEN]; const tokenId = data[AUTH_USER_ID]; if (!accessToken || !tokenId) { - throw new Error("인증 토큰을 받지 못했습니다."); - } - - const prevTokenId = getCookie(AUTH_USER_ID); - if (prevTokenId !== undefined && prevTokenId !== tokenId) { - clearTabStorePersist(); + throw new Error("갱신된 인증 토큰을 받지 못했습니다."); } setToken(CLAIM_NAME, accessToken); @@ -49,67 +85,32 @@ async function login(payload: AuthPayload): Promise { const remainingMs = getRemainingMs(accessToken); if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); + setupRefreshTimer(silentRefresh, remainingMs); } -} - -async function silentRefresh(): Promise { - console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); - try { - const response = await fetch( - `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { - method: "PUT", - headers: { - "X-Device-Id": getOrCreateDeviceId(), - }, - }, - ); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); - } - - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; - - if (!accessToken || !tokenId) { - throw new Error("갱신된 인증 토큰을 받지 못했습니다."); - } - - setToken(CLAIM_NAME, accessToken); - setToken(AUTH_USER_ID, tokenId); - - const remainingMs = getRemainingMs(accessToken); - if (remainingMs > 0) { - setupRefreshTimer(silentRefresh, remainingMs); - } - } catch (error) { - console.error( - `[silentRefresh] Failed at ${new Date().toISOString()}`, - error, - ); - tokenManager.clearAuthToken(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + } catch (error) { + console.error( + `[silentRefresh] Failed at ${new Date().toISOString()}`, + error, + ); + tokenManager.clearAuthToken(); + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); } + } } export async function logout(): Promise { - tokenManager.clearAuthToken(); - clearDeviceId(); + tokenManager.clearAuthToken(); + clearDeviceId(); - if (typeof window !== "undefined") { - window.dispatchEvent(new CustomEvent("auth:logout")); - } + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); + } } export const authService: AuthService = { - login, - silentRefresh, - declineSessionExtend: clearRefreshTimer, - logout, + login, + silentRefresh, + declineSessionExtend: clearRefreshTimer, + logout, }; diff --git a/apps/wr/src/entities/auth/lib/device-id.ts b/apps/wr/src/entities/auth/lib/device-id.ts deleted file mode 100644 index f844fcbef..000000000 --- a/apps/wr/src/entities/auth/lib/device-id.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -const DEVICE_ID_KEY = "x-device-id"; - -export function getOrCreateDeviceId(): string { - if (typeof window === "undefined") return ""; - - const existing = localStorage.getItem(DEVICE_ID_KEY); - if (existing) return existing; - - const id = uuidv4(); - localStorage.setItem(DEVICE_ID_KEY, id); - return id; -} - -export function clearDeviceId(): void { - if (typeof window === "undefined") return; - - localStorage.removeItem(DEVICE_ID_KEY); -} diff --git a/packages/common/assets/tsconfig.json b/packages/common/assets/tsconfig.json index 54ce63ce4..5b03d6cf4 100644 --- a/packages/common/assets/tsconfig.json +++ b/packages/common/assets/tsconfig.json @@ -1,6 +1,12 @@ { "include": ["."], - "exclude": ["dist", "build", "node_modules"], + "exclude": [ + "dist", + "build", + "node_modules", + "coverage", + "storybook-static" + ], "compilerOptions": { "moduleResolution": "node", "baseUrl": "." diff --git a/packages/common/auth/README.md b/packages/common/auth/README.md index fbc8742c5..fd0305dc7 100644 --- a/packages/common/auth/README.md +++ b/packages/common/auth/README.md @@ -19,6 +19,7 @@ packages/common/auth/ ├── types.ts # 타입 정의 ├── token-manager.ts # 토큰 관리 로직 ├── cookie.ts # 쿠키 유틸리티 +├── device-id.ts # 기기 ID (쿠키) ├── jwt-utils.ts # JWT 유틸리티 └── README.md # 문서 ``` diff --git a/packages/common/auth/device-id.ts b/packages/common/auth/device-id.ts new file mode 100644 index 000000000..ffdc1eead --- /dev/null +++ b/packages/common/auth/device-id.ts @@ -0,0 +1,27 @@ +import { v4 as uuidv4 } from "uuid"; +import { getCookie, removeCookie, setCookie } from "./cookie"; + +const DEVICE_ID_KEY = "x-device-id"; +const COOKIE_DOMAIN = process.env.NEXT_PUBLIC_COOKIE_DOMAIN; + +export function getOrCreateDeviceId(): string { + const existing = getCookie(DEVICE_ID_KEY); + if (existing) return existing; + + const id = uuidv4(); + const isHttps = + typeof window !== "undefined" && window.location.protocol === "https:"; + setCookie(DEVICE_ID_KEY, id, { + secure: isHttps, + sameSite: "lax", + domain: COOKIE_DOMAIN, + path: "/", + expires: 365, + }); + + return id; +} + +export function clearDeviceId(): void { + removeCookie(DEVICE_ID_KEY, { path: "/" }); +} diff --git a/packages/common/auth/index.ts b/packages/common/auth/index.ts index a3d7024da..ac5547ed5 100644 --- a/packages/common/auth/index.ts +++ b/packages/common/auth/index.ts @@ -2,6 +2,7 @@ export { createTokenManager } from "./token-manager"; export type { TokenManager } from "./token-manager"; export { getCookie, setCookie, removeCookie } from "./cookie"; +export { clearDeviceId, getOrCreateDeviceId } from "./device-id"; export { getJwtExpiry, getRemainingMs, diff --git a/packages/common/auth/package.json b/packages/common/auth/package.json index 4baa26d35..aa45f6a99 100644 --- a/packages/common/auth/package.json +++ b/packages/common/auth/package.json @@ -8,7 +8,8 @@ ".": "./index.ts" }, "dependencies": { - "js-cookie": "^3.0.5" + "js-cookie": "^3.0.5", + "uuid": "^14.0.1" }, "devDependencies": { "@types/js-cookie": "^3.0.6", diff --git a/packages/common/auth/tsconfig.json b/packages/common/auth/tsconfig.json index 04a0208c5..9483f7caa 100644 --- a/packages/common/auth/tsconfig.json +++ b/packages/common/auth/tsconfig.json @@ -3,6 +3,7 @@ "extends": "../../../tsconfig.base.json", "compilerOptions": { "target": "ES2017", + "types": ["node"], "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, @@ -19,5 +20,10 @@ "incremental": true }, "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": [ + "node_modules", + "dist", + "coverage", + "storybook-static" + ] } diff --git a/packages/common/components/src/index.ts b/packages/common/components/src/index.ts index 40118e9ca..93686ba1f 100644 --- a/packages/common/components/src/index.ts +++ b/packages/common/components/src/index.ts @@ -1,2 +1,4 @@ +export * from "./api"; +export * from "./config"; export * from "./lib"; export * from "./ui"; diff --git a/packages/common/components/tsconfig.json b/packages/common/components/tsconfig.json index 9fa9e88ee..e5eeffcad 100644 --- a/packages/common/components/tsconfig.json +++ b/packages/common/components/tsconfig.json @@ -12,21 +12,22 @@ "noEmit": false, "incremental": true, "strict": true, - "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["./src/*"] } }, - "exclude": ["**/node_modules", "**/.*/*", "dist"], + "exclude": [ + "node_modules", + ".next", + "dist", + "coverage", + "storybook-static", + "public" + ], "include": [ - ".eslintrc.*", "**/*.ts", "**/*.tsx", "**/*.mts", - "**/*.cjs", - "**/*.mjs", - "**/*.jsx", - "**/*.json", ".storybook/env.ts" ] } diff --git a/packages/common/config/tsconfig.json b/packages/common/config/tsconfig.json index 20cbe8801..95f0c3842 100644 --- a/packages/common/config/tsconfig.json +++ b/packages/common/config/tsconfig.json @@ -9,5 +9,10 @@ "incremental": false }, "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "dist"] + "exclude": [ + "node_modules", + "dist", + "coverage", + "storybook-static" + ] } diff --git a/packages/common/network/client.ts b/packages/common/network/client.ts index 6b39a33fa..f222bff3f 100644 --- a/packages/common/network/client.ts +++ b/packages/common/network/client.ts @@ -9,6 +9,8 @@ interface ClientConfig { siteId: string; } +const DEVICE_ID_KEY = "x-device-id"; + class Client { private static instance: Client; private axiosInstance: AxiosInstance; @@ -26,6 +28,7 @@ class Client { this.axiosInstance.interceptors.request.use((config) => { const token = Cookies.get(CLAIM_NAME); const userId = Cookies.get(AUTH_USER_ID); + const deviceId = Cookies.get(DEVICE_ID_KEY); if (!token || !userId) { return config; @@ -33,6 +36,7 @@ class Client { config.headers[CLAIM_NAME] = token; config.headers[AUTH_USER_ID] = userId; + config.headers["X-Device-Id"] = deviceId; return config; }); diff --git a/packages/common/network/tsconfig.json b/packages/common/network/tsconfig.json index 7f4dcf889..0468315bf 100644 --- a/packages/common/network/tsconfig.json +++ b/packages/common/network/tsconfig.json @@ -2,9 +2,14 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../../tsconfig.base.json", "compilerOptions": { - "baseUrl": ".", - "module": "ESNext" + "module": "ESNext", + "types": ["node"] }, "include": ["."], - "exclude": ["node_modules", "dist"] + "exclude": [ + "node_modules", + "dist", + "coverage", + "storybook-static" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 214b99a21..4b95e9068 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,9 +195,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 zod: specifier: ^3.24.2 version: 3.25.76 @@ -352,9 +349,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 zod: specifier: ^3.24.2 version: 3.25.76 @@ -639,9 +633,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 zod: specifier: ^3.24.2 version: 3.25.76 @@ -951,9 +942,6 @@ importers: select: specifier: ^1.1.2 version: 1.1.2 - uuid: - specifier: ^14.0.1 - version: 14.0.1 webpack: specifier: ^5.98.0 version: 5.105.4 @@ -1078,9 +1066,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -1280,9 +1265,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -1440,9 +1422,6 @@ importers: tus-js-client: specifier: ^4.3.1 version: 4.3.1 - uuid: - specifier: ^14.0.1 - version: 14.0.1 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -1495,6 +1474,9 @@ importers: js-cookie: specifier: ^3.0.5 version: 3.0.5 + uuid: + specifier: ^14.0.1 + version: 14.0.1 devDependencies: '@types/js-cookie': specifier: ^3.0.6 diff --git a/tsconfig.base.json b/tsconfig.base.json index 254e01445..55754237d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,5 +18,12 @@ "incremental": true, "newLine": "lf" }, - "exclude": ["**/node_modules", "**/.*/"] + "exclude": [ + "**/node_modules", + "**/.*/", + "**/dist", + "**/coverage", + "**/storybook-static", + "**/.next" + ] }