From dcbfc4ad976071c323dd76ddea22f357562108c2 Mon Sep 17 00:00:00 2001 From: mandelina Date: Sun, 3 Aug 2025 16:13:15 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8:=20?= =?UTF-8?q?formatDate=20=EB=B0=8F=20formatISOtoDate=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _tests_/utils/formatDate.test.ts | 123 +++++++++++++++++++++++++++++++ utils/formatDate.ts | 37 ++++++++-- 2 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 _tests_/utils/formatDate.test.ts diff --git a/_tests_/utils/formatDate.test.ts b/_tests_/utils/formatDate.test.ts new file mode 100644 index 00000000..4c20d295 --- /dev/null +++ b/_tests_/utils/formatDate.test.ts @@ -0,0 +1,123 @@ +import { formatDate, formatISOtoDate } from '@/utils/formatDate'; + +describe('formatDate', () => { + it('기본 YYYY-MM-DD 형식을 YYYY.MM.DD로 변환해야 한다', () => { + const input = '2024-01-15'; + const result = formatDate(input); + expect(result).toBe('2024.01.15'); + }); + + it('다양한 날짜 형식을 올바르게 변환해야 한다', () => { + const testCases = [ + { input: '2023-12-31', expected: '2023.12.31' }, + { input: '2024-02-29', expected: '2024.02.29' }, + { input: '2025-06-15', expected: '2025.06.15' }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = formatDate(input); + expect(result).toBe(expected); + }); + }); + + it('한 자리 월/일을 두 자리로 패딩해야 한다', () => { + const testCases = [ + { input: '2024-1-15', expected: '2024.01.15' }, + { input: '2024-01-1', expected: '2024.01.01' }, + { input: '2024-1-1', expected: '2024.01.01' }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = formatDate(input); + expect(result).toBe(expected); + }); + }); + + it('빈 문자열이 입력되면 null을 반환해야 한다', () => { + const input = ''; + const result = formatDate(input); + expect(result).toBeNull(); + }); + + it('하이픈이 없는 문자열은 null을 반환해야 한다', () => { + const input = '20240115'; + const result = formatDate(input); + expect(result).toBeNull(); + }); + + it('공백이 포함된 날짜도 처리해야 한다', () => { + const input = ' 2024-01-15 '; + const result = formatDate(input); + expect(result).toBe('2024.01.15'); + }); +}); + +describe('formatISOtoDate', () => { + it('기본 YYYY-MM-DD HH:mm:ss 형식을 YYYY.MM.DD HH:mm로 변환해야 한다', () => { + const input = '2024-01-15 14:30:25'; + const result = formatISOtoDate(input); + expect(result).toBe('2024.01.15 14:30'); + }); + + it('다양한 시간 형식을 올바르게 변환해야 한다', () => { + const testCases = [ + { input: '2023-12-31 23:59:59', expected: '2023.12.31 23:59' }, + { input: '2024-06-15 09:30:00', expected: '2024.06.15 09:30' }, + { input: '2025-02-14 12:00:30', expected: '2025.02.14 12:00' }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = formatISOtoDate(input); + expect(result).toBe(expected); + }); + }); + + it('한 자리 시간/분을 두 자리로 패딩해야 한다', () => { + const testCases = [ + { input: '2024-3-5 9:5:00', expected: '2024.03.05 09:05' }, + { input: '2024-12-1 15:30:05', expected: '2024.12.01 15:30' }, + { input: '2024-1-9 8:45:30', expected: '2024.01.09 08:45' }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = formatISOtoDate(input); + expect(result).toBe(expected); + }); + }); + + it('초 부분이 제거되어야 한다', () => { + const testCases = [ + { input: '2024-01-15 14:30:45', expected: '2024.01.15 14:30' }, + { input: '2024-01-15 14:30:00', expected: '2024.01.15 14:30' }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = formatISOtoDate(input); + expect(result).toBe(expected); + }); + }); + + it('빈 문자열이 입력되면 null을 반환해야 한다', () => { + const input = ''; + const result = formatISOtoDate(input); + expect(result).toBeNull(); + }); + + it('시간 부분이 없는 날짜도 처리해야 한다', () => { + const input = '2024-01-15'; + const result = formatISOtoDate(input); + expect(result).toBe('2024.01.15'); + }); + + it('시간만 있는 형식도 처리해야 한다', () => { + const input = '14:30:25'; + const result = formatISOtoDate(input); + expect(result).toBe('14:30'); + }); + + it('밀리초가 포함된 형식도 처리해야 한다', () => { + const input = '2024-01-15 14:30:25.123'; + const result = formatISOtoDate(input); + expect(result).toBe('2024.01.15 14:30'); + }); +}); diff --git a/utils/formatDate.ts b/utils/formatDate.ts index c01c3c31..ad06caf7 100644 --- a/utils/formatDate.ts +++ b/utils/formatDate.ts @@ -1,13 +1,38 @@ /** 서버에서 주는 YYYY-MM-DD 형식을 YYYY.MM.DD로 바꾸는 함수 */ + export const formatDate = (dateString: string) => { - const [year, month, day] = dateString.split('-'); - return `${year}.${month}.${day}`; + if (!dateString?.trim()) return null; + + const trimmedString = dateString.trim(); + if (!trimmedString.includes('-')) return null; + + const [year, month, day] = trimmedString.split('-'); + if (!year || !month || !day) return null; + + return `${year}.${month.padStart(2, '0')}.${day.padStart(2, '0')}`; }; /** 서버에서 주는 YYYY-MM-DD HH:mm:ss 형식을 YYYY.MM.DD HH:mm로 바꾸는 함수 */ export const formatISOtoDate = (dateString: string) => { - const [date, time] = dateString.split(' '); - const formattedDate = date.replace(/-/g, '.'); - const formattedTime = time.slice(0, 5); // HH:mm:ss에서 HH:mm만 가져오기 - return `${formattedDate} ${formattedTime}`; + if (!dateString?.trim()) return null; + + const trimmedString = dateString.trim(); + const [date, time] = trimmedString.split(' '); + + if (!time) { + if (trimmedString.includes(':')) { + const [hours, minutes] = trimmedString.split(':'); + if (!hours || !minutes) return null; + return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`; + } + return formatDate(trimmedString); + } + + const formattedDate = formatDate(date); + if (!formattedDate) return null; + + const [hours, minutes] = time.split(':'); + if (!hours || !minutes) return formattedDate; + + return `${formattedDate} ${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`; }; From 285e22740cc4d50402a7e3834ce6048ab5430a8e Mon Sep 17 00:00:00 2001 From: mandelina Date: Sun, 3 Aug 2025 17:22:27 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8:=20?= =?UTF-8?q?getCookie,=20checkLogin,=20getGA=20=ED=95=A8=EC=88=98=EC=97=90?= =?UTF-8?q?=20=EB=8C=80=ED=95=9C=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _tests_/utils/getCookie.test.ts | 254 ++++++++++++++++++++++++++++++++ utils/getCookie.tsx | 12 +- 2 files changed, 260 insertions(+), 6 deletions(-) create mode 100644 _tests_/utils/getCookie.test.ts diff --git a/_tests_/utils/getCookie.test.ts b/_tests_/utils/getCookie.test.ts new file mode 100644 index 00000000..cd3fff8e --- /dev/null +++ b/_tests_/utils/getCookie.test.ts @@ -0,0 +1,254 @@ +import { getCookie, checkLogin, getGA } from '@/utils/getCookie'; + +const setMockCookies = (cookies: string) => { + Object.defineProperty(document, 'cookie', { + writable: true, + value: cookies, + }); +}; + +describe('getCookie', () => { + beforeEach(() => { + setMockCookies(''); + }); + + it('존재하는 쿠키의 값을 반환해야 한다', () => { + setMockCookies('testCookie=testValue; otherCookie=otherValue'); + + const result = getCookie('testCookie'); + + expect(result).toBe('testValue'); + }); + + it('존재하지 않는 쿠키는 null을 반환해야 한다', () => { + setMockCookies('testCookie=testValue'); + + const result = getCookie('nonexistentCookie'); + + expect(result).toBeNull(); + }); + + it('URL 인코딩된 쿠키 값을 올바르게 디코딩해야 한다', () => { + setMockCookies('encodedCookie=test%40example.com'); + + const result = getCookie('encodedCookie'); + + expect(result).toBe('test@example.com'); + }); + + it('공백이 있는 쿠키도 올바르게 처리해야 한다', () => { + setMockCookies(' spacedCookie = spacedValue '); + + const result = getCookie('spacedCookie'); + + expect(result).toBe('spacedValue'); + }); + + it('빈 쿠키 문자열에서도 null을 반환해야 한다', () => { + setMockCookies(''); + + const result = getCookie('anyCookie'); + + expect(result).toBeNull(); + }); + + it('여러 쿠키 중에서 정확한 쿠키를 찾아야 한다', () => { + setMockCookies('cookie1=value1; cookie2=value2; cookie3=value3'); + + const result = getCookie('cookie2'); + + expect(result).toBe('value2'); + }); + + it('쿠키 값에 특수문자가 포함되어도 올바르게 처리해야 한다', () => { + setMockCookies('specialCookie=value%20with%20spaces%26symbols'); + + const result = getCookie('specialCookie'); + + expect(result).toBe('value with spaces&symbols'); + }); + + it('쿠키 값이 숫자여도 문자열로 반환해야 한다', () => { + setMockCookies('numberCookie=12345'); + + const result = getCookie('numberCookie'); + + expect(result).toBe('12345'); + }); + + it('쿠키 값이 JSON 형태여도 올바르게 처리해야 한다', () => { + setMockCookies('jsonCookie=%7B%22name%22%3A%22test%22%7D'); + + const result = getCookie('jsonCookie'); + + expect(result).toBe('{"name":"test"}'); + }); + + it('쿠키 이름에 대소문자를 구분해야 한다', () => { + setMockCookies('TestCookie=value; testcookie=otherValue'); + + const result = getCookie('TestCookie'); + + expect(result).toBe('value'); + }); + + it('빈 값의 쿠키도 올바르게 처리해야 한다', () => { + setMockCookies('emptyCookie=; otherCookie=value'); + + const result = getCookie('emptyCookie'); + + expect(result).toBe(''); + }); +}); + +describe('checkLogin', () => { + beforeEach(() => { + setMockCookies(''); + }); + + it('DEVDEVDEV_LOGIN_STATUS가 active이면 active를 반환해야 한다', () => { + setMockCookies('DEVDEVDEV_LOGIN_STATUS=active'); + + const result = checkLogin(); + + expect(result).toBe('active'); + }); + + it('DEVDEVDEV_LOGIN_STATUS가 active가 아니면 checking을 반환해야 한다', () => { + setMockCookies('DEVDEVDEV_LOGIN_STATUS=inactive'); + + const result = checkLogin(); + + expect(result).toBe('checking'); + }); + + it('DEVDEVDEV_LOGIN_STATUS 쿠키가 없으면 checking을 반환해야 한다', () => { + setMockCookies('otherCookie=value'); + + const result = checkLogin(); + + expect(result).toBe('checking'); + }); + + it('DEVDEVDEV_LOGIN_STATUS가 빈 값이면 checking을 반환해야 한다', () => { + setMockCookies('DEVDEVDEV_LOGIN_STATUS='); + + const result = checkLogin(); + + expect(result).toBe('checking'); + }); + + it('DEVDEVDEV_LOGIN_STATUS가 대소문자 구분 없이 active이면 active를 반환해야 한다', () => { + setMockCookies('DEVDEVDEV_LOGIN_STATUS=ACTIVE'); + + const result = checkLogin(); + + expect(result).toBe('active'); + }); + + it('다른 로그인 상태 값들도 checking을 반환해야 한다', () => { + const testCases = ['pending', 'failed', 'expired', 'unknown']; + + testCases.forEach((status) => { + setMockCookies(`DEVDEVDEV_LOGIN_STATUS=${status}`); + + const result = checkLogin(); + + expect(result).toBe('checking'); + }); + }); +}); + +describe('getGA', () => { + beforeEach(() => { + setMockCookies(''); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('_ga 쿠키가 있으면 해당 값을 반환해야 한다', async () => { + setMockCookies('_ga=GA1.2.123456789.1234567890'); + + const resultPromise = getGA(); + + const result = await resultPromise; + expect(result).toBe('GA1.2.123456789.1234567890'); + }); + + it('_ga 쿠키가 없으면 undefined를 반환해야 한다', async () => { + setMockCookies('otherCookie=value'); + + const resultPromise = getGA(); + + await jest.runAllTimersAsync(); + + const result = await resultPromise; + expect(result).toBeUndefined(); + }); + + it('최대 재시도 횟수만큼 시도해야 한다', async () => { + setMockCookies('otherCookie=value'); + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + + const resultPromise = getGA(); + + await jest.runAllTimersAsync(); + + await resultPromise; + expect(consoleSpy).toHaveBeenCalledWith('Failed to get GA cookie after maximum retries'); + + consoleSpy.mockRestore(); + }); + + it('재시도 중에 쿠키가 생기면 즉시 반환해야 한다', async () => { + setMockCookies('otherCookie=value'); + + const resultPromise = getGA(); + + jest.advanceTimersByTime(1000); + setMockCookies('_ga=GA1.2.123456789.1234567890; otherCookie=value'); + + const result = await resultPromise; + expect(result).toBe('GA1.2.123456789.1234567890'); + }); + + it('다양한 GA 쿠키 형식을 올바르게 처리해야 한다', async () => { + const testCases = [ + 'GA1.1.123456789.1234567890', + 'GA1.2.987654321.0987654321', + 'GA1.3.111111111.2222222222', + ]; + + for (const gaCookie of testCases) { + setMockCookies(`_ga=${gaCookie}`); + + const resultPromise = getGA(); + + const result = await resultPromise; + expect(result).toBe(gaCookie); + } + }); + + it('GA 쿠키가 URL 인코딩되어 있어도 올바르게 처리해야 한다', async () => { + setMockCookies('_ga=GA1.2.123456789.1234567890%3B'); + + const resultPromise = getGA(); + + const result = await resultPromise; + expect(result).toBe('GA1.2.123456789.1234567890;'); + }); + + it('빈 GA 쿠키 값도 undefined로 처리해야 한다', async () => { + setMockCookies('_ga='); + + const resultPromise = getGA(); + + await jest.runAllTimersAsync(); + + const result = await resultPromise; + expect(result).toBeUndefined(); + }); +}); diff --git a/utils/getCookie.tsx b/utils/getCookie.tsx index a7435954..fd725b81 100644 --- a/utils/getCookie.tsx +++ b/utils/getCookie.tsx @@ -8,8 +8,8 @@ export function getCookie(key: string) { const cookie = cookies[i].trim(); const [cookieName, cookieValue] = cookie.split('='); - if (cookieName === key) { - return decodeURIComponent(cookieValue); + if (cookieName?.trim() === key) { + return decodeURIComponent(cookieValue?.trim() || ''); } } @@ -23,8 +23,8 @@ export function getCookie(key: string) { */ export const checkLogin = (): 'checking' | 'active' => { const loginSuccess = getCookie('DEVDEVDEV_LOGIN_STATUS'); - if (loginSuccess === 'active') { - return loginSuccess; + if (loginSuccess?.toLowerCase() === 'active') { + return 'active'; } return 'checking'; }; @@ -40,9 +40,9 @@ export const getGA = async (): Promise => { return GA; } - await wait(RETRY_INTERVAL); // 시간지연을 위함 + await wait(RETRY_INTERVAL); } console.error('Failed to get GA cookie after maximum retries'); - return undefined; // 적절한 실패 처리 + return undefined; }; From 38dbfd37d4232ebc00cb42f8847e0cc90ade61f6 Mon Sep 17 00:00:00 2001 From: mandelina Date: Sun, 3 Aug 2025 17:29:56 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8:=20?= =?UTF-8?q?getMaskedEmail=20=ED=95=A8=EC=88=98=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _tests_/utils/getUserInfo.test.ts | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 _tests_/utils/getUserInfo.test.ts diff --git a/_tests_/utils/getUserInfo.test.ts b/_tests_/utils/getUserInfo.test.ts new file mode 100644 index 00000000..52c5c21e --- /dev/null +++ b/_tests_/utils/getUserInfo.test.ts @@ -0,0 +1,67 @@ +import { getMaskedEmail } from '@/utils/getUserInfo'; + +describe('getMaskedEmail', () => { + it('이메일의 @ 앞부분을 마스킹 처리해야 한다', () => { + const email = 'test@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('tes*'); + }); + + it('긴 이메일 주소도 올바르게 마스킹해야 한다', () => { + const email = 'verylongemail@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('ver**********'); + }); + + it('짧은 이메일 주소도 올바르게 처리해야 한다', () => { + const email = 'ab@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('ab'); + }); + + it('3글자 이메일 주소는 마스킹 없이 반환해야 한다', () => { + const email = 'abc@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('abc'); + }); + + it('4글자 이메일 주소는 1글자만 마스킹해야 한다', () => { + const email = 'abcd@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('abc*'); + }); + + it('다양한 도메인도 올바르게 처리해야 한다', () => { + const email = 'user@gmail.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('use*'); + }); + + it('특수문자가 포함된 이메일도 처리해야 한다', () => { + const email = 'user.name@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('use******'); + }); + + it('숫자가 포함된 이메일도 처리해야 한다', () => { + const email = 'user123@example.com'; + + const result = getMaskedEmail(email); + + expect(result).toBe('use****'); + }); +}); From fd72620f602b58c542e82cdbca4bc1e990351132 Mon Sep 17 00:00:00 2001 From: mandelina Date: Sun, 3 Aug 2025 17:57:56 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8:=20?= =?UTF-8?q?isNavigationActive=20=ED=95=A8=EC=88=98=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _tests_/utils/headerUtils.test.ts | 76 +++++++++++++++++++++++++++++++ utils/headerUtils.ts | 13 ++---- 2 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 _tests_/utils/headerUtils.test.ts diff --git a/_tests_/utils/headerUtils.test.ts b/_tests_/utils/headerUtils.test.ts new file mode 100644 index 00000000..f3a7bee1 --- /dev/null +++ b/_tests_/utils/headerUtils.test.ts @@ -0,0 +1,76 @@ +import { isNavigationActive } from '@/utils/headerUtils'; + +describe('isNavigationActive', () => { + it('정확히 일치하는 경로에서 true를 반환해야 한다', () => { + const testCases: Array<{ link: '/pickpickpick' | '/techblog' | '/myinfo'; pathname: string }> = + [ + { link: '/pickpickpick', pathname: '/pickpickpick' }, + { link: '/techblog', pathname: '/techblog' }, + { link: '/myinfo', pathname: '/myinfo' }, + ]; + + testCases.forEach(({ link, pathname }) => { + const result = isNavigationActive(link, pathname); + expect(result).toBe(true); + }); + }); + + it('하위 경로에서도 true를 반환해야 한다', () => { + const testCases: Array<{ link: '/pickpickpick' | '/techblog' | '/myinfo'; pathname: string }> = + [ + { link: '/pickpickpick', pathname: '/pickpickpick/detail' }, + { link: '/techblog', pathname: '/techblog/post/123' }, + { link: '/myinfo', pathname: '/myinfo/settings' }, + ]; + + testCases.forEach(({ link, pathname }) => { + const result = isNavigationActive(link, pathname); + expect(result).toBe(true); + }); + }); + + it('다른 경로에서는 false를 반환해야 한다', () => { + const testCases: Array<{ link: '/pickpickpick' | '/techblog' | '/myinfo'; pathname: string }> = + [ + { link: '/pickpickpick', pathname: '/techblog' }, + { link: '/techblog', pathname: '/myinfo' }, + { link: '/myinfo', pathname: '/pickpickpick' }, + ]; + + testCases.forEach(({ link, pathname }) => { + const result = isNavigationActive(link, pathname); + expect(result).toBe(false); + }); + }); + + it('비슷한 경로명에서도 정확히 구분해야 한다', () => { + const testCases: Array<{ link: '/pickpickpick' | '/techblog' | '/myinfo'; pathname: string }> = + [ + { link: '/myinfo', pathname: '/myinfosettings' }, + { link: '/techblog', pathname: '/techblogpost' }, + { link: '/pickpickpick', pathname: '/pickpickpickdetail' }, + ]; + + testCases.forEach(({ link, pathname }) => { + const result = isNavigationActive(link, pathname); + expect(result).toBe(false); + }); + }); + + it('경계 케이스에서 올바르게 동작해야 한다', () => { + const testCases: Array<{ + link: '/pickpickpick' | '/techblog' | '/myinfo'; + pathname: string; + expected: boolean; + }> = [ + { link: '/pickpickpick', pathname: '', expected: false }, + { link: '/techblog', pathname: '/', expected: false }, + { link: '/myinfo', pathname: '/my', expected: false }, + ]; + + testCases.forEach(({ link, pathname, expected }) => { + const result = isNavigationActive(link, pathname); + expect(result).toBe(expected); + }); + }); +}); diff --git a/utils/headerUtils.ts b/utils/headerUtils.ts index 05bfbedb..65ebc1c6 100644 --- a/utils/headerUtils.ts +++ b/utils/headerUtils.ts @@ -1,10 +1,7 @@ -import { ROUTES } from '@/constants/routes'; - /** 현재 페이지가 클릭한 nav와 일치하는지 확인하는 함수 */ -export const isActive = (link: '/pickpickpick' | '/techblog' | '/myinfo', pathname: string) => { - const { MY_INFO, PICKPICKPICK, TECH_BLOG } = ROUTES; - if ([MY_INFO.PREFIX, PICKPICKPICK.MAIN, TECH_BLOG].includes(link)) { - return pathname.startsWith(link); - } - return pathname === link; +export const isNavigationActive = ( + link: '/pickpickpick' | '/techblog' | '/myinfo', + pathname: string, +) => { + return pathname === link || pathname.startsWith(link + '/'); }; From ee20d28a5064ca905d18bb06b3ca9aa99f40bcb5 Mon Sep 17 00:00:00 2001 From: mandelina Date: Sun, 3 Aug 2025 18:01:05 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=20Fix=20:=20isActive=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=20isNavigationActive=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/common/header/header.tsx | 6 +++--- components/common/header/mobileHeader.tsx | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/components/common/header/header.tsx b/components/common/header/header.tsx index 368fd607..d5af8340 100644 --- a/components/common/header/header.tsx +++ b/components/common/header/header.tsx @@ -4,7 +4,7 @@ import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { isActive } from '@utils/headerUtils'; +import { isNavigationActive } from '@utils/headerUtils'; import { useLoginStatusStore } from '@stores/loginStore'; import { useLoginModalStore } from '@stores/modalStore'; @@ -50,7 +50,7 @@ export default function Header() {
    {MENU_LISTS.map((list) => (
  • - {isActive(list.route, pathname) && ( + {isNavigationActive(list.route, pathname) && (
    )}
  • - {isActive('/myinfo', pathname) && ( + {isNavigationActive('/myinfo', pathname) && (
    )} {MENU_LISTS.map((list) => (
  • - {isActive(list.route, pathname) &&
    } + {isNavigationActive(list.route, pathname) && ( +
    + )} handleRefreshLinkClick(list.route)} @@ -103,7 +105,9 @@ export default function MobileHeader() { {loginStatus === 'login' && (
  • - {isActive('/myinfo', pathname) &&
    } + {isNavigationActive('/myinfo', pathname) && ( +
    + )} handleRefreshLinkClick(MY_INFO.PREFIX)}