From 22d94a5dc1bd3591ea2e6430eb48fe5e15657be2 Mon Sep 17 00:00:00 2001 From: six-standard Date: Mon, 16 Jun 2025 14:09:07 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor:=20import=20=EC=95=8C=ED=8C=8C?= =?UTF-8?q?=EB=B2=B3=20=EC=A0=95=EB=A0=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 5 ++++- src/__test__/login.test.tsx | 6 +++--- src/__test__/main.test.tsx | 4 ++-- src/apis/dashboard.request.ts | 2 +- src/apis/instance.request.ts | 11 +++++------ src/apis/leaderboard.request.ts | 2 +- src/apis/user.request.ts | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e0b0fb0..0dd0aea 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,7 +28,10 @@ module.exports = { ], rules: { 'prettier/prettier': ['error', { printWidth: 100 }], - 'import/order': ['error', { groups: ['builtin', 'external', 'internal'] }], + 'import/order': [ + 'error', + { groups: ['builtin', 'external', 'internal'], alphabetize: { order: 'asc' } }, + ], 'no-restricted-imports': ['warn', { patterns: ['../../*'] }], 'react/react-in-jsx-scope': 'off', 'testing-library/no-container': 'warn', diff --git a/src/__test__/login.test.tsx b/src/__test__/login.test.tsx index f9794fb..d45aacb 100644 --- a/src/__test__/login.test.tsx +++ b/src/__test__/login.test.tsx @@ -1,9 +1,9 @@ -import { userEvent } from '@testing-library/user-event'; import { act, screen } from '@testing-library/react'; -import { ToastContainer } from 'react-toastify'; +import { userEvent } from '@testing-library/user-event'; import { useRouter } from 'next/navigation'; -import { renderWithQueryClient } from '@/utils/componentUtil'; +import { ToastContainer } from 'react-toastify'; import { default as Login } from '@/app/(login)/page'; +import { renderWithQueryClient } from './instance.test'; jest.mock('next/navigation', () => ({ useRouter: jest.fn(), diff --git a/src/__test__/main.test.tsx b/src/__test__/main.test.tsx index f400fb1..cc3c2d1 100644 --- a/src/__test__/main.test.tsx +++ b/src/__test__/main.test.tsx @@ -1,7 +1,7 @@ import { screen, waitFor } from '@testing-library/react'; -import { renderWithQueryClient } from '@/utils/componentUtil'; import { Content } from '@/app/(auth-required)/main/Content'; -import { Header } from '@/app/(auth-required)/components/header'; +import { Header } from '@/app/components/Header'; +import { renderWithQueryClient } from './instance.test'; jest.mock('next/navigation', () => ({ useSearchParams: () => ({ diff --git a/src/apis/dashboard.request.ts b/src/apis/dashboard.request.ts index 2119158..2008750 100644 --- a/src/apis/dashboard.request.ts +++ b/src/apis/dashboard.request.ts @@ -1,5 +1,5 @@ -import { PostDetailDto, PostListDto, PostSummaryDto, TotalStatsDto } from '@/types'; import { PATHS, SidebarIdType } from '@/constants'; +import { PostDetailDto, PostListDto, PostSummaryDto, TotalStatsDto } from '@/types'; import { instance } from './instance.request'; diff --git a/src/apis/instance.request.ts b/src/apis/instance.request.ts index 8313a03..394c51a 100644 --- a/src/apis/instance.request.ts +++ b/src/apis/instance.request.ts @@ -1,8 +1,7 @@ -import returnFetch, { FetchArgs } from 'return-fetch'; - import { captureException, setContext } from '@sentry/nextjs'; +import returnFetch, { FetchArgs } from 'return-fetch'; +import { ENVS } from '@/constants'; import { ServerNotRespondingError } from '@/errors'; -import { env } from '@/constants'; type ErrorType = { code: string; @@ -27,7 +26,7 @@ const abortPolyfill = (ms: number) => { }; const fetch = returnFetch({ - baseUrl: env.BASE_URL, + baseUrl: ENVS.BASE_URL, headers: { Accept: 'application/json', 'Content-Type': 'application/json', @@ -66,8 +65,8 @@ export const instance = async ( : init?.headers, body: init?.body ? JSON.stringify(init.body) : undefined, signal: AbortSignal.timeout - ? AbortSignal.timeout(Number(env.ABORT_MS)) - : abortPolyfill(Number(env.ABORT_MS)), + ? AbortSignal.timeout(Number(ENVS.ABORT_MS)) + : abortPolyfill(Number(ENVS.ABORT_MS)), credentials: 'include', cache: 'no-store', }); diff --git a/src/apis/leaderboard.request.ts b/src/apis/leaderboard.request.ts index 32433f8..3d0d37f 100644 --- a/src/apis/leaderboard.request.ts +++ b/src/apis/leaderboard.request.ts @@ -1,5 +1,5 @@ -import { LeaderboardListDto } from '@/types/leaderboard.type'; import { PATHS } from '@/constants'; +import { LeaderboardListDto } from '@/types/leaderboard.type'; import { instance } from './instance.request'; export const leaderboardList = async ({ diff --git a/src/apis/user.request.ts b/src/apis/user.request.ts index d8125d0..dafceaa 100644 --- a/src/apis/user.request.ts +++ b/src/apis/user.request.ts @@ -1,5 +1,5 @@ -import { NotFoundError } from '@/errors'; import { PATHS } from '@/constants'; +import { NotFoundError } from '@/errors'; import { LoginVo, UserDto } from '@/types'; import { instance } from './instance.request'; From 95445e350a33386ffd8f11e013595fbc8e9aaf61 Mon Sep 17 00:00:00 2001 From: six-standard Date: Mon, 16 Jun 2025 14:10:12 +0900 Subject: [PATCH 02/16] =?UTF-8?q?refactor:=20=EC=83=81=EC=88=98=EA=B0=92?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sentry.client.config.ts | 7 ++--- sentry.edge.config.ts | 6 ++--- sentry.server.config.ts | 6 ++--- src/constants/colors.constant.ts | 28 ------------------- src/constants/env.constant.ts | 7 ++--- src/constants/fonts.constant.ts | 17 ------------ src/constants/graph.constant.ts | 18 +++---------- src/constants/index.ts | 6 ++--- src/constants/screens.constant.ts | 5 ---- src/constants/sizes.constant.ts | 8 ------ src/constants/styles.constant.ts | 45 +++++++++++++++++++++++++++++++ src/constants/urls.constant.ts | 7 +++++ tailwind.config.ts | 27 +++++++------------ 13 files changed, 79 insertions(+), 108 deletions(-) delete mode 100644 src/constants/colors.constant.ts delete mode 100644 src/constants/fonts.constant.ts delete mode 100644 src/constants/screens.constant.ts delete mode 100644 src/constants/sizes.constant.ts create mode 100644 src/constants/styles.constant.ts create mode 100644 src/constants/urls.constant.ts diff --git a/sentry.client.config.ts b/sentry.client.config.ts index 8dfe0e2..73202d3 100644 --- a/sentry.client.config.ts +++ b/sentry.client.config.ts @@ -3,10 +3,11 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from '@sentry/nextjs'; -import { env } from '@/constants'; + +import { ENVS } from '@/constants'; Sentry.init({ - dsn: env.SENTRY_DSN, + dsn: ENVS.SENTRY_DSN, release: 'production', // Add optional integrations for additional features @@ -25,5 +26,5 @@ Sentry.init({ // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, - enabled: process.env.NODE_ENV === 'production', + enabled: ENVS.NODE_ENV === 'production', }); diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts index 78238d0..dce874e 100644 --- a/sentry.edge.config.ts +++ b/sentry.edge.config.ts @@ -4,10 +4,10 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from '@sentry/nextjs'; -import { env } from '@/constants'; +import { ENVS } from '@/constants'; Sentry.init({ - dsn: env.SENTRY_DSN, + dsn: ENVS.SENTRY_DSN, release: 'production', // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. @@ -15,5 +15,5 @@ Sentry.init({ // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, - enabled: process.env.NODE_ENV === 'production', + enabled: ENVS.NODE_ENV === 'production', }); diff --git a/sentry.server.config.ts b/sentry.server.config.ts index 91dfd02..c28d8e0 100644 --- a/sentry.server.config.ts +++ b/sentry.server.config.ts @@ -3,10 +3,10 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from '@sentry/nextjs'; -import { env } from '@/constants'; +import { ENVS } from '@/constants'; Sentry.init({ - dsn: env.SENTRY_DSN, + dsn: ENVS.SENTRY_DSN, release: 'production', // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. @@ -14,5 +14,5 @@ Sentry.init({ // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, - enabled: process.env.NODE_ENV === 'production', + enabled: ENVS.NODE_ENV === 'production', }); diff --git a/src/constants/colors.constant.ts b/src/constants/colors.constant.ts deleted file mode 100644 index b0d9c0d..0000000 --- a/src/constants/colors.constant.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const COLORS = { - BG: { - MAIN: '#121212', - SUB: '#1E1E1E', - ALT: '#252525', - }, - TEXT: { - MAIN: '#ECECEC', - SUB: '#D9D9D9', - ALT: '#ACACAC', - }, - BORDER: { - MAIN: '#2A2A2A', - SUB: '#4D4D4D', - ALT: '#E0E0E0', - }, - PRIMARY: { - MAIN: '#96F2D7', - SUB: '#63E6BE', - }, - DESTRUCTIVE: { - MAIN: '#FFC9C9', - SUB: '#FFA8A8', - }, - TRANSPARENT: '#00000000', -}; - -export type ColorType = Record; diff --git a/src/constants/env.constant.ts b/src/constants/env.constant.ts index ed1cfb7..67a1cfd 100644 --- a/src/constants/env.constant.ts +++ b/src/constants/env.constant.ts @@ -1,17 +1,14 @@ -import { EnvNotFoundError } from '../errors/fetch.error'; +import { EnvNotFoundError } from '../errors'; -export const env = (() => { +export const ENVS = (() => { const env = { NODE_ENV: process.env.NODE_ENV, BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, ABORT_MS: process.env.NEXT_PUBLIC_ABORT_MS, - EVENT_LOG: process.env.NEXT_PUBLIC_EVENT_LOG, - VELOG_URL: process.env.NEXT_PUBLIC_VELOG_URL, CHANNELTALK_PLUGIN_KEY: process.env.NEXT_PUBLIC_CHANNELTALK_PLUGIN_KEY, GA_ID: process.env.NEXT_PUBLIC_GA_ID, SENTRY_AUTH_TOKEN: process.env.NEXT_PUBLIC_SENTRY_AUTH_TOKEN, SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, - ARCADE_URL: process.env.NEXT_PUBLIC_ARCADE_URL, } as const; if (env.NODE_ENV) { diff --git a/src/constants/fonts.constant.ts b/src/constants/fonts.constant.ts deleted file mode 100644 index 0b205a3..0000000 --- a/src/constants/fonts.constant.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const FONTS: Record = { - T1: ['32px', { lineHeight: '46.3px', fontWeight: 700 }], - T2: ['28px', { lineHeight: '40.5px', fontWeight: 700 }], - T3: ['24px', { lineHeight: '34.8px', fontWeight: 700 }], - T4: ['20px', { lineHeight: '29px', fontWeight: 700 }], - T5: ['16px', { lineHeight: '23.2px', fontWeight: 700 }], - T6: ['12px', { lineHeight: '17.4px', fontWeight: 700 }], - ST1: ['30px', { lineHeight: '43.4px', fontWeight: 500 }], - ST2: ['26px', { lineHeight: '37.6px', fontWeight: 500 }], - ST3: ['22px', { lineHeight: '31.9px', fontWeight: 500 }], - ST4: ['18px', { lineHeight: '26.1px', fontWeight: 500 }], - ST5: ['14px', { lineHeight: '20.3px', fontWeight: 500 }], - I1: ['16px', { lineHeight: '23.2px', fontWeight: 700 }], - I2: ['16px', { lineHeight: '23.2px', fontWeight: 400 }], - I3: ['14px', { lineHeight: '20.3px', fontWeight: 700 }], - I4: ['14px', { lineHeight: '20.3px', fontWeight: 400 }], -}; diff --git a/src/constants/graph.constant.ts b/src/constants/graph.constant.ts index 60c727e..ff90cf7 100644 --- a/src/constants/graph.constant.ts +++ b/src/constants/graph.constant.ts @@ -1,18 +1,11 @@ -import { COLORS } from './colors.constant'; +import { COLORS } from './styles.constant'; -export const graphOptions = { +export const GRAPH_OPTIONS = { responsive: true, maintainAspectRatio: false, animation: false, interaction: { mode: 'nearest', intersect: false }, - layout: { - padding: { - top: 40, - bottom: 10, - left: 15, - right: 15, - }, - }, + layout: { padding: { top: 40, bottom: 10, left: 15, right: 15 } }, plugins: { legend: { display: false }, tooltip: { enabled: false }, @@ -24,10 +17,7 @@ export const graphOptions = { borderWidth: 1, borderRadius: 4, padding: 4, - font: { - size: 12, - weight: 'normal', - }, + font: { size: 12, weight: 'normal' }, formatter: (value: number) => value.toString(), anchor: 'end', align: 'top', diff --git a/src/constants/index.ts b/src/constants/index.ts index 47bc0e1..f6492fe 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,9 +1,7 @@ export * from './searchParams.constant'; export * from './sidebar.constant'; -export * from './screens.constant'; -export * from './colors.constant'; -export * from './sizes.constant'; +export * from './styles.constant'; export * from './paths.constant'; -export * from './fonts.constant'; export * from './graph.constant'; +export * from './urls.constant'; export * from './env.constant'; diff --git a/src/constants/screens.constant.ts b/src/constants/screens.constant.ts deleted file mode 100644 index e89dd74..0000000 --- a/src/constants/screens.constant.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const SCREENS = { - DSK: 1512, - TBL: 1194, - MBI: 834, -}; diff --git a/src/constants/sizes.constant.ts b/src/constants/sizes.constant.ts deleted file mode 100644 index 6d5a6f6..0000000 --- a/src/constants/sizes.constant.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const SIZES = { - FULL: 'w-[100%]', - LARGE: 'w-[400px]', - MEDIUM: 'w-[200px]', - SMALL: 'w-[100px]', -}; - -export type SizeType = keyof typeof SIZES; diff --git a/src/constants/styles.constant.ts b/src/constants/styles.constant.ts new file mode 100644 index 0000000..fa97647 --- /dev/null +++ b/src/constants/styles.constant.ts @@ -0,0 +1,45 @@ +// 인터랙션 너비 값 +export const SIZES = { + FULL: 'w-[100%]', + LARGE: 'w-[400px]', + MEDIUM: 'w-[200px]', + SMALL: 'w-[100px]', +}; + +// 각 디스플레이별 최소 사이즈 값 +export const SCREENS = { DSK: 1512, TBL: 1194, MBI: 834 }; + +// Tailwindcss 폰트 팔레트 +export const FONTS = { + TITLE: { + '1': ['32px', { lineHeight: '46.3px', fontWeight: 700 }], + '2': ['28px', { lineHeight: '40.5px', fontWeight: 700 }], + '3': ['24px', { lineHeight: '34.8px', fontWeight: 700 }], + '4': ['20px', { lineHeight: '29px', fontWeight: 700 }], + '5': ['16px', { lineHeight: '23.2px', fontWeight: 700 }], + '6': ['12px', { lineHeight: '17.4px', fontWeight: 700 }], + }, + SUBTITLE: { + '1': ['30px', { lineHeight: '43.4px', fontWeight: 500 }], + '2': ['26px', { lineHeight: '37.6px', fontWeight: 500 }], + '3': ['22px', { lineHeight: '31.9px', fontWeight: 500 }], + '4': ['18px', { lineHeight: '26.1px', fontWeight: 500 }], + '5': ['14px', { lineHeight: '20.3px', fontWeight: 500 }], + }, + INPUT: { + '1': ['16px', { lineHeight: '23.2px', fontWeight: 700 }], + '2': ['16px', { lineHeight: '23.2px', fontWeight: 400 }], + '3': ['14px', { lineHeight: '20.3px', fontWeight: 700 }], + '4': ['14px', { lineHeight: '20.3px', fontWeight: 400 }], + }, +} as const; + +// Tailwindcss 컬러 팔레트 +export const COLORS = { + BG: { MAIN: '#121212', SUB: '#1E1E1E', ALT: '#252525' }, + TEXT: { MAIN: '#ECECEC', SUB: '#D9D9D9', ALT: '#ACACAC' }, + BORDER: { MAIN: '#2A2A2A', SUB: '#4D4D4D', ALT: '#E0E0E0' }, + PRIMARY: { MAIN: '#96F2D7', SUB: '#63E6BE' }, + DESTRUCTIVE: { MAIN: '#FFC9C9', SUB: '#FFA8A8' }, + TRANSPARENT: '#00000000', +} as const; diff --git a/src/constants/urls.constant.ts b/src/constants/urls.constant.ts new file mode 100644 index 0000000..00782c8 --- /dev/null +++ b/src/constants/urls.constant.ts @@ -0,0 +1,7 @@ +export const URLS = { + ARCADE: 'https://app.arcade.software/share/8LEqkpUQfEK0p0QpQDLF', + VELOG: 'https://velog.io', + VELOG_DASHBOARD: 'https://velog-dashboard.kro.kr/', + TERMS_OF_SERVICE: 'https://nuung.notion.site/terms-of-service', + PRIVACY_POLICY: 'https://nuung.notion.site/privacy-policy', +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index 6b8d575..f337114 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,27 +1,18 @@ -import type { Config } from 'tailwindcss'; import typograpy from '@tailwindcss/typography'; -import { - COLORS, - SIZES, - SCREENS as CUSTOMSCREENS, - FONTS, -} from './src/constants'; +import type { Config } from 'tailwindcss'; +import { COLORS, SIZES, SCREENS as CUSTOMSCREENS, FONTS as CUSTOMFONTS } from './src/constants'; const SCREENS: Record = {}; +const FONTS: Record = {}; -Object.entries(CUSTOMSCREENS).forEach((i) => (SCREENS[i[0]] = i[1] + 'px')); +Object.entries(CUSTOMSCREENS).forEach(([key, value]) => (SCREENS[key] = value + 'px')); +Object.entries(CUSTOMFONTS).map(([section, items]) => { + Object.entries(items).forEach(([key, value]) => (FONTS[`${section}-${key}`] = value)); +}); const config: Config = { - content: [ - './src/pages/**/*.{js,ts,jsx,tsx,mdx}', - './src/components/**/*.{js,ts,jsx,tsx,mdx}', - './src/app/**/*.{js,ts,jsx,tsx,mdx}', - ], - theme: { - colors: COLORS, - screens: SCREENS, - fontSize: FONTS, - }, + content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], + theme: { colors: COLORS, screens: SCREENS, fontSize: FONTS }, plugins: [typograpy()], safelist: Object.values(SIZES), }; From 18989577f5bbcb6f983915c48b20dc6b50ae8af6 Mon Sep 17 00:00:00 2001 From: six-standard Date: Mon, 16 Jun 2025 14:11:02 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refactor:=20=EC=9C=A0=ED=8B=B8=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=ED=8C=8C=EC=9D=BC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/{revalidateUtil.ts => cache.util.ts} | 8 +++++++- src/utils/componentUtil.tsx | 6 ------ src/utils/cookieUtil.tsx | 7 ------- src/utils/{dateUtil.ts => datetime.util.ts} | 7 +++++++ src/utils/index.ts | 4 ++++ src/utils/number.util.ts | 9 +++++++++ src/utils/numberUtil.ts | 2 -- src/utils/{queryUtil.ts => query.util.ts} | 18 +++++++++++------- 8 files changed, 38 insertions(+), 23 deletions(-) rename src/utils/{revalidateUtil.ts => cache.util.ts} (54%) delete mode 100644 src/utils/componentUtil.tsx delete mode 100644 src/utils/cookieUtil.tsx rename src/utils/{dateUtil.ts => datetime.util.ts} (91%) create mode 100644 src/utils/index.ts create mode 100644 src/utils/number.util.ts delete mode 100644 src/utils/numberUtil.ts rename src/utils/{queryUtil.ts => query.util.ts} (68%) diff --git a/src/utils/revalidateUtil.ts b/src/utils/cache.util.ts similarity index 54% rename from src/utils/revalidateUtil.ts rename to src/utils/cache.util.ts index 375a030..e5b8e40 100644 --- a/src/utils/revalidateUtil.ts +++ b/src/utils/cache.util.ts @@ -1,8 +1,14 @@ 'use server'; - import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; +/** + * **서버 액션** + * + * 현재 남아있는 유저 정보 캐싱 데이터를 초기화함. + * NextJS Client-Side Cache 해결을 위해 제작됨. + */ + export async function revalidate() { revalidatePath('/', 'layout'); redirect('/'); diff --git a/src/utils/componentUtil.tsx b/src/utils/componentUtil.tsx deleted file mode 100644 index dc930d5..0000000 --- a/src/utils/componentUtil.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { render, RenderResult } from '@testing-library/react'; -import { ReactElement } from 'react'; -import { QueryProvider } from '@/components'; - -export const renderWithQueryClient = (element: ReactElement): RenderResult => - render({element}); diff --git a/src/utils/cookieUtil.tsx b/src/utils/cookieUtil.tsx deleted file mode 100644 index fc2c5a9..0000000 --- a/src/utils/cookieUtil.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies'; - -export const getCookieForAuth = (cookies: () => ReadonlyRequestCookies, keys: string[]) => { - const cookie = { headers: {} as Record }; - keys.forEach((i) => (cookie.headers[i] = cookies().get(i)?.value || '')); - return cookie; -}; diff --git a/src/utils/dateUtil.ts b/src/utils/datetime.util.ts similarity index 91% rename from src/utils/dateUtil.ts rename to src/utils/datetime.util.ts index 4acbbab..13a4f4e 100644 --- a/src/utils/dateUtil.ts +++ b/src/utils/datetime.util.ts @@ -47,6 +47,13 @@ export const convertDateToKST = (date?: string): KSTDateFormat | undefined => { }; }; +/** + * 주어진 초 정수를 'N분 M초' 형태로 변환함. + * + * @param {number} [time] - 변환할 초 정수 (예: 360, 6분 0초를 의미함) + * @returns {string} + */ + export const formatTimeToMMSS = (time: number) => { const minute = Math.floor(time / 60) .toString() diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..f9a8396 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,4 @@ +export * from './datetime.util'; +export * from './number.util'; +export * from './cache.util'; +export * from './query.util'; diff --git a/src/utils/number.util.ts b/src/utils/number.util.ts new file mode 100644 index 0000000..16121a2 --- /dev/null +++ b/src/utils/number.util.ts @@ -0,0 +1,9 @@ +/** + * 주어진 숫자에 천 단위로 콤마를 찍어 반환함. + * + * @param {number} [item] - 변환할 정수 + * @returns {string} + */ + +export const parseNumber = (item: number) => + item ? item.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : '0'; diff --git a/src/utils/numberUtil.ts b/src/utils/numberUtil.ts deleted file mode 100644 index 35b0c78..0000000 --- a/src/utils/numberUtil.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const parseNumber = (item: number) => - item ? item.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : '0'; diff --git a/src/utils/queryUtil.ts b/src/utils/query.util.ts similarity index 68% rename from src/utils/queryUtil.ts rename to src/utils/query.util.ts index ad54707..2293195 100644 --- a/src/utils/queryUtil.ts +++ b/src/utils/query.util.ts @@ -1,10 +1,11 @@ import { QueryClient } from '@tanstack/react-query'; import { toast } from 'react-toastify'; -let localQueryClient: QueryClient | undefined; const STALE_TIME = 1000 * 60 * 3; const GC_TIME = 1000 * 60 * 20; +let localQueryClient: QueryClient | undefined; + const createQueryClient = () => new QueryClient({ defaultOptions: { @@ -19,11 +20,14 @@ const createQueryClient = () => }, }); +/** + * 현재 상태에 맞는 Tanstack-Query Client 인스턴스 반환 + * + * @returns QueryClient + */ + export const getQueryClient = () => { - if (typeof window === 'undefined') { - return createQueryClient(); - } else { - if (!localQueryClient) localQueryClient = createQueryClient(); - return localQueryClient; - } + if (typeof window === 'undefined') return createQueryClient(); + if (!localQueryClient) localQueryClient = createQueryClient(); + return localQueryClient; }; From f430e05ca41ea8f044146b1ad8a728f72cf9dcf2 Mon Sep 17 00:00:00 2001 From: six-standard Date: Mon, 16 Jun 2025 14:12:05 +0900 Subject: [PATCH 04/16] =?UTF-8?q?refactor:=20src=20=EB=A3=A8=ED=8A=B8=20co?= =?UTF-8?q?mponents=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=8B=A8=EC=88=9C=20=EA=B3=B5=EC=9C=A0=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A7=8C=20=ED=8F=AC=ED=95=A8?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B4=80=EA=B3=84=EB=A1=9C,=20shared?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EC=98=80=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ArriveSoon.tsx | 8 -------- .../Providers/ChannelTalkProvider.tsx | 15 -------------- src/components/Providers/ModalProvider.tsx | 9 --------- src/components/Providers/QueryProvider.tsx | 20 ------------------- src/components/Providers/index.ts | 3 --- src/components/index.ts | 8 -------- src/{components => shared}/Button.tsx | 12 ++++++++--- src/{components => shared}/Check.tsx | 12 +++++++++-- src/{components => shared}/Dropdown.tsx | 0 src/{components => shared}/Icon/SvgrMock.tsx | 0 .../Icon/icons/Analytics.svg | 0 .../Icon/icons/Arrow.svg | 0 .../Icon/icons/Close.svg | 0 .../Icon/icons/Door.svg | 0 .../Icon/icons/Leaderboards.svg | 0 .../Icon/icons/Like.svg | 0 .../Icon/icons/Loudspeaker.svg | 0 .../Icon/icons/Shortcut.svg | 0 .../Icon/icons/Video.svg | 0 .../Icon/icons/index.ts | 0 src/{components => shared}/Icon/index.tsx | 0 src/{components => shared}/Input.tsx | 12 ++++++++--- src/{components => shared}/Modal.tsx | 6 ++---- src/shared/index.ts | 7 +++++++ 24 files changed, 37 insertions(+), 75 deletions(-) delete mode 100644 src/components/ArriveSoon.tsx delete mode 100644 src/components/Providers/ChannelTalkProvider.tsx delete mode 100644 src/components/Providers/ModalProvider.tsx delete mode 100644 src/components/Providers/QueryProvider.tsx delete mode 100644 src/components/Providers/index.ts delete mode 100644 src/components/index.ts rename src/{components => shared}/Button.tsx (51%) rename src/{components => shared}/Check.tsx (60%) rename src/{components => shared}/Dropdown.tsx (100%) rename src/{components => shared}/Icon/SvgrMock.tsx (100%) rename src/{components => shared}/Icon/icons/Analytics.svg (100%) rename src/{components => shared}/Icon/icons/Arrow.svg (100%) rename src/{components => shared}/Icon/icons/Close.svg (100%) rename src/{components => shared}/Icon/icons/Door.svg (100%) rename src/{components => shared}/Icon/icons/Leaderboards.svg (100%) rename src/{components => shared}/Icon/icons/Like.svg (100%) rename src/{components => shared}/Icon/icons/Loudspeaker.svg (100%) rename src/{components => shared}/Icon/icons/Shortcut.svg (100%) rename src/{components => shared}/Icon/icons/Video.svg (100%) rename src/{components => shared}/Icon/icons/index.ts (100%) rename src/{components => shared}/Icon/index.tsx (100%) rename src/{components => shared}/Input.tsx (62%) rename src/{components => shared}/Modal.tsx (91%) create mode 100644 src/shared/index.ts diff --git a/src/components/ArriveSoon.tsx b/src/components/ArriveSoon.tsx deleted file mode 100644 index bf8cd1c..0000000 --- a/src/components/ArriveSoon.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export const ArriveSoon = () => { - return ( -
- 곧 만나뵙겠습니다! - (열심히 개발하고 있습니다) -
- ); -}; diff --git a/src/components/Providers/ChannelTalkProvider.tsx b/src/components/Providers/ChannelTalkProvider.tsx deleted file mode 100644 index 7cc2d5f..0000000 --- a/src/components/Providers/ChannelTalkProvider.tsx +++ /dev/null @@ -1,15 +0,0 @@ -'use client'; - -import * as ChannelService from '@channel.io/channel-web-sdk-loader'; -import { useEffect } from 'react'; -import { env } from '@/constants'; - -const ChannelTalkServiceLoader = () => { - ChannelService.loadScript(); - ChannelService.boot({ pluginKey: env.CHANNELTALK_PLUGIN_KEY }); -}; - -export const ChannelTalkProvider = ({ children }: { children: React.ReactNode }) => { - useEffect(() => ChannelTalkServiceLoader(), []); - return <>{children}; -}; diff --git a/src/components/Providers/ModalProvider.tsx b/src/components/Providers/ModalProvider.tsx deleted file mode 100644 index ccec614..0000000 --- a/src/components/Providers/ModalProvider.tsx +++ /dev/null @@ -1,9 +0,0 @@ -'use client'; - -import { useModal } from '@/hooks/useModal'; - -export const ModalProvider = (): React.ReactNode => { - const { Modal } = useModal(); - - if (typeof window !== 'undefined') return Modal ? Modal : <>; -}; diff --git a/src/components/Providers/QueryProvider.tsx b/src/components/Providers/QueryProvider.tsx deleted file mode 100644 index 481acb4..0000000 --- a/src/components/Providers/QueryProvider.tsx +++ /dev/null @@ -1,20 +0,0 @@ -'use client'; - -import { QueryClientProvider } from '@tanstack/react-query'; -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import { getQueryClient } from '@/utils/queryUtil'; - -interface IProp { - children: React.ReactNode | React.ReactNode[]; -} - -export const QueryProvider = ({ children }: IProp) => { - const client = getQueryClient(); - - return ( - - - {children} - - ); -}; diff --git a/src/components/Providers/index.ts b/src/components/Providers/index.ts deleted file mode 100644 index 9a6dfc4..0000000 --- a/src/components/Providers/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './ChannelTalkProvider'; -export * from './QueryProvider'; -export * from './ModalProvider'; diff --git a/src/components/index.ts b/src/components/index.ts deleted file mode 100644 index f219efd..0000000 --- a/src/components/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './ArriveSoon'; -export * from './Providers'; -export * from './Dropdown'; -export * from './Button'; -export * from './Input'; -export * from './Check'; -export * from './Modal'; -export * from './Icon'; diff --git a/src/components/Button.tsx b/src/shared/Button.tsx similarity index 51% rename from src/components/Button.tsx rename to src/shared/Button.tsx index a43c846..37bd967 100644 --- a/src/components/Button.tsx +++ b/src/shared/Button.tsx @@ -1,8 +1,9 @@ -import { SIZES, SizeType } from '@/constants'; +import { twMerge } from 'tailwind-merge'; +import { SIZES } from '@/constants'; interface IProp extends React.ButtonHTMLAttributes { form?: keyof typeof FORMS; - size: SizeType; + size: keyof typeof SIZES; } const FORMS = { @@ -13,7 +14,12 @@ const FORMS = { export const Button = ({ form = 'SMALL', size, children, ...rest }: IProp) => ( diff --git a/src/components/Check.tsx b/src/shared/Check.tsx similarity index 60% rename from src/components/Check.tsx rename to src/shared/Check.tsx index d99ae35..76ef6b8 100644 --- a/src/components/Check.tsx +++ b/src/shared/Check.tsx @@ -1,3 +1,5 @@ +import { twJoin } from 'tailwind-merge'; + interface IProp extends React.HTMLAttributes { checked: boolean; label?: string; @@ -8,12 +10,18 @@ interface IProp extends React.HTMLAttributes { export const Check = ({ checked, label, onChange, direction = 'left', ...rest }: IProp) => { return (