Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/components/Course/hooks/useSelectedCourses.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import { useMemo, useState } from 'react';
import { PreCourse } from 'api/course/entity';
import { isomorphicSessionStorage } from 'utils/ts/env';

const SELECTED_COURSES_KEY = 'selected-courses';

const getStoredCourses = (): PreCourse[] => {
try {
const stored = sessionStorage.getItem(SELECTED_COURSES_KEY);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
return isomorphicSessionStorage.getJSONItem<PreCourse[]>(SELECTED_COURSES_KEY, []);
};

const storeCourses = (courses: PreCourse[]) => {
try {
sessionStorage.setItem(SELECTED_COURSES_KEY, JSON.stringify(courses));
} catch {
// sessionStorage 용량 초과 등 무시
}
isomorphicSessionStorage.setJSONItem(SELECTED_COURSES_KEY, courses);
};

export const getCourseKey = (course: PreCourse) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/IndexComponents/IndexCafeteria/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ function IndexCafeteria() {
};

const handleTooltipContentButtonClick = () => {
localStorage.setItem('cafeteria-tooltip', 'used');
isomorphicLocalStorage.setItem('cafeteria-tooltip', 'used');
handleMoreClick();
};

const handleTooltipCloseButtonClick = () => {
localStorage.setItem('cafeteria-tooltip', 'used');
isomorphicLocalStorage.setItem('cafeteria-tooltip', 'used');
closeTooltip();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import useStoreDetail from 'components/Store/StoreDetailPage/hooks/useStoreDetai
import useLogger from 'utils/hooks/analytics/useLogger';
import useModalPortal from 'utils/hooks/layout/useModalPortal';
import { useUser } from 'utils/hooks/state/useUser';
import { isomorphicSessionStorage } from 'utils/ts/env';
import styles from './index.module.scss';

export const REVEIW_LOGIN = ['리뷰를 작성하기 ', '리뷰 작성은 회원만 사용 가능합니다.'];
Expand Down Expand Up @@ -46,7 +47,7 @@ export default function ReviewButton({ id, goReviewPage }: { id: string; goRevie
event_label: 'shop_detail_view_review_write',
value: storeDetail.name,
});
sessionStorage.setItem('enterReview', new Date().getTime().toString());
isomorphicSessionStorage.setItem('enterReview', new Date().getTime().toString());
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Suspense, useEffect } from 'react';
import { useRouter } from 'next/router';
import { DropdownProvider } from 'components/Store/StoreDetailPage/hooks/useDropdown';
import ROUTES from 'static/routes';
import { isomorphicSessionStorage } from 'utils/ts/env';
import AverageRating from './components/AverageRating/AverageRating';
import ReviewButton from './components/ReviewButton';
import ReviewList from './components/ReviewList/ReviewList';
Expand All @@ -12,7 +13,7 @@ export default function ReviewPage({ id }: { id: string }) {

useEffect(() => {
// 리뷰 페이지 진입 시간 기록
sessionStorage.setItem('enterReviewPage', new Date().getTime().toString());
isomorphicSessionStorage.setItem('enterReviewPage', new Date().getTime().toString());
}, []);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ROUTES from 'static/routes';
import useLogger from 'utils/hooks/analytics/useLogger';
import useImageUpload from 'utils/hooks/ui/useImageUpload';
import useScrollToTop from 'utils/hooks/ui/useScrollToTop';
import { isomorphicSessionStorage } from 'utils/ts/env';
import uuidv4 from 'utils/ts/uuidGenerater';
import styles from './ReviewForm.module.scss';

Expand Down Expand Up @@ -65,7 +66,8 @@ function ReviewForm({ storeDetail, mutate, initialData = {} }: Props) {
};

const reviewSuccessLogging = () => {
const getReviewDurationTime = (new Date().getTime() - Number(sessionStorage.getItem('enterReview'))) / 1000;
const getReviewDurationTime =
(new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterReview'))) / 1000;
logger.actionEventClick({
team: 'BUSINESS',
event_label: 'shop_detail_view_review_write_done',
Expand Down
6 changes: 4 additions & 2 deletions src/components/Store/utils/durationTime.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { isomorphicSessionStorage } from 'utils/ts/env';

const MAIN_ENTRY_TIME = 'main-entry-time';
const CATEGORY_ENTRY_TIME = 'category-entry-time';

const initializeTime = (key: string) => {
const currentTime = new Date().getTime();
sessionStorage.setItem(key, currentTime.toString());
isomorphicSessionStorage.setItem(key, currentTime.toString());
};

export const initializeMainEntryTime = () => initializeTime(MAIN_ENTRY_TIME);
export const initializeCategoryEntryTime = () => initializeTime(CATEGORY_ENTRY_TIME);

const getTime = (key: string) => {
const currentTime = new Date().getTime();
const mainEntryTime = Number(sessionStorage.getItem(key)) || currentTime;
const mainEntryTime = Number(isomorphicSessionStorage.getItem(key)) || currentTime;
return (currentTime - mainEntryTime) / 1000;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MainTimetable from 'components/TimetablePage/components/MainTimetable';
import TimetableList from 'components/TimetablePage/components/TimetableList';
import ROUTES from 'static/routes';
import useLogger from 'utils/hooks/analytics/useLogger';
import { isomorphicSessionStorage } from 'utils/ts/env';
import styles from './DefaultPage.module.scss';

interface DefaultPageProps {
Expand All @@ -19,14 +20,14 @@ export default function DefaultPage({ timetableFrameId, setCurrentFrameId }: Def
const logger = useLogger();
const handlePopState = React.useCallback(() => {
// swipe로 뒤로가기 시
if (sessionStorage.getItem('swipeToBack') === 'true') {
if (isomorphicSessionStorage.getItem('swipeToBack') === 'true') {
logger.actionEventSwipe({
team: 'USER',
event_label: 'timetable_back',
value: 'OS스와이프',
previous_page: '시간표',
current_page: '메인',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
history.back();
return;
Expand All @@ -38,7 +39,7 @@ export default function DefaultPage({ timetableFrameId, setCurrentFrameId }: Def
value: '뒤로가기버튼',
previous_page: '시간표',
current_page: '메인',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
}, [logger]);

Expand All @@ -51,11 +52,11 @@ export default function DefaultPage({ timetableFrameId, setCurrentFrameId }: Def
}, []);

React.useEffect(() => {
sessionStorage.setItem('swipeToBack', 'false');
isomorphicSessionStorage.setItem('swipeToBack', 'false');
const handleWheel = (e: WheelEvent) => {
// 한 번에 최소 100px만큼 스크롤(드래그) 시 swipe했다고 간주
if (e.deltaX < -100) {
sessionStorage.setItem('swipeToBack', 'true');
isomorphicSessionStorage.setItem('swipeToBack', 'true');
}
};
window.addEventListener('wheel', handleWheel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ROUTES from 'static/routes';
import useLogger from 'utils/hooks/analytics/useLogger';
import useBooleanState from 'utils/hooks/state/useBooleanState';
import useTokenState from 'utils/hooks/state/useTokenState';
import { isomorphicSessionStorage } from 'utils/ts/env';
import { useSemester } from 'utils/zustand/semester';
import DownloadTimetableModal from './DownloadTimetableModal';
import styles from './MyLectureTimetable.module.scss';
Expand Down Expand Up @@ -143,7 +144,7 @@ function ValidMainTimetable({ timetableFrameId }: { readonly timetableFrameId: n
team: 'USER',
event_label: 'timetable',
value: '이미지저장',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
openModal();
}
Expand Down
7 changes: 5 additions & 2 deletions src/components/TimetablePage/hooks/useTimetableMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'api/timetable/entity';
import useToast from 'components/feedback/Toast/useToast';
import useTokenState from 'utils/hooks/state/useTokenState';
import { isomorphicSessionStorage } from 'utils/ts/env';
import showToast from 'utils/ts/showToast';
import { useLecturesAction } from 'utils/zustand/myLectures';
import { useSemester } from 'utils/zustand/semester';
Expand Down Expand Up @@ -67,7 +68,9 @@ export default function useTimetableMutation(timetableFrameId: number) {

// 강의 복원
const restoreLecture = (id: number[]) => {
const restoredLecture = JSON.parse(sessionStorage.getItem('restoreLecture')!);
const restoredLecture = isomorphicSessionStorage.getJSONItem<Lecture | MyLectureInfo | null>('restoreLecture', null);
if (!restoredLecture || typeof restoredLecture !== 'object') return;

if ('name' in restoredLecture) {
addLectureFromLocalStorage(restoredLecture, `${semester?.year}${semester?.term}`);
} else {
Expand Down Expand Up @@ -107,7 +110,7 @@ export default function useTimetableMutation(timetableFrameId: number) {

const removeMyLecture = useMutation({
mutationFn: async ({ clickedLecture, id }: RemoveMyLectureProps & { disableRecoverButton?: boolean }) => {
sessionStorage.setItem('restoreLecture', JSON.stringify(clickedLecture));
isomorphicSessionStorage.setJSONItem('restoreLecture', clickedLecture);
if (clickedLecture && 'name' in clickedLecture) {
return Promise.resolve(removeLectureFromLocalStorage(clickedLecture, `${semester?.year}${semester?.term}`));
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/layout/Header/MobileHeader/Panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useTokenState from 'utils/hooks/state/useTokenState';
import { useUser } from 'utils/hooks/state/useUser';
import { useBodyScrollLock } from 'utils/hooks/ui/useBodyScrollLock';
import { useEscapeKeyDown } from 'utils/hooks/ui/useEscapeKeyDown';
import { isomorphicSessionStorage } from 'utils/ts/env';
import { useMobileSidebar } from 'utils/zustand/mobileSidebar';
import type { Portal } from 'components/modal/Modal/PortalProvider';
import styles from './Panel.module.scss';
Expand Down Expand Up @@ -62,7 +63,7 @@ export default function Panel({ openModal }: PanelProps) {
value: '햄버거',
previous_page: '시간표',
current_page: title,
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
Comment on lines +66 to 67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

duration_time 기본값 방어가 필요합니다.

Line 66에서 storage 값이 없거나 접근이 막히면 Number(null)이 0이 되어 비정상적으로 큰 duration_time이 기록됩니다. fallback 계산을 분리해 주세요.

제안 코드
+      const enteredAt = Number(isomorphicSessionStorage.getItem('enterTimetablePage'));
+      const durationTime = Number.isFinite(enteredAt) && enteredAt > 0 ? (Date.now() - enteredAt) / 1000 : 0;
       logger.actionEventClick({
         team: 'USER',
         event_label: 'timetable_back',
         value: '햄버거',
         previous_page: '시간표',
         current_page: title,
-        duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
+        duration_time: durationTime,
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Header/MobileHeader/Panel/index.tsx` around lines 66 -
67, The duration_time calculation in the Panel component unconditionally does
(new Date().getTime() -
Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000 which
yields a wrong value when the storage item is missing; instead, read the raw
value from isomorphicSessionStorage.getItem('enterTimetablePage'), parse/convert
it to a Number with a guard (check for null/undefined and isFinite/!isNaN and
positive), and only compute (Date.now() - parsed) / 1000 when valid; otherwise
set duration_time to a safe fallback (e.g., 0 or null). Ensure you update the
assignment where duration_time is set so it uses the guarded parsed value rather
than Number(...) directly.

}
};
Expand Down
7 changes: 4 additions & 3 deletions src/components/layout/Header/MobileHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ROUTES from 'static/routes';
import useLogger from 'utils/hooks/analytics/useLogger';
import { useResetHeaderButton } from 'utils/hooks/layout/useResetHeaderButton';
import useParamsHandler from 'utils/hooks/routing/useParamsHandler';
import { isomorphicSessionStorage } from 'utils/ts/env';
import { backButtonTapped } from 'utils/ts/iosBridge';
import { useHeaderTitle } from 'utils/zustand/customTitle';
import { useHeaderButtonStore } from 'utils/zustand/headerButtonStore';
Expand Down Expand Up @@ -46,8 +47,8 @@ export default function MobileHeader({ openModal }: MobileHeaderProps) {
event_label: 'shop_detail_view_back',
value: response.name,
event_category: 'click',
current_page: sessionStorage.getItem('cameFrom') || '',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enter_storeDetail'))) / 1000,
current_page: isomorphicSessionStorage.getItem('cameFrom') || '',
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enter_storeDetail'))) / 1000,
}); // 상점 내 뒤로가기 버튼 로깅
router.back();
return;
Expand All @@ -59,7 +60,7 @@ export default function MobileHeader({ openModal }: MobileHeaderProps) {
value: '뒤로가기버튼',
previous_page: '시간표',
current_page: '메인',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
}

Expand Down
7 changes: 4 additions & 3 deletions src/components/layout/Header/PCHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useSessionLogger } from 'utils/hooks/analytics/useSessionLogger';
import { useLogout } from 'utils/hooks/auth/useLogout';
import useModalPortal from 'utils/hooks/layout/useModalPortal';
import useTokenState from 'utils/hooks/state/useTokenState';
import { isomorphicSessionStorage } from 'utils/ts/env';
import type { Portal } from 'components/modal/Modal/PortalProvider';
import styles from './PCHeader.module.scss';

Expand Down Expand Up @@ -133,7 +134,7 @@ export default function PCHeader({ openModal }: PCHeaderProps) {
value: '로고',
previous_page: '시간표',
current_page: '메인',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enterTimetablePage'))) / 1000,
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enterTimetablePage'))) / 1000,
});
}
if (pathname.includes(ROUTES.Store()) && search.includes('state')) {
Expand All @@ -144,8 +145,8 @@ export default function PCHeader({ openModal }: PCHeaderProps) {
event_label: 'shop_detail_view_back',
value: shopName.name,
event_category: 'logo',
current_page: sessionStorage.getItem('cameFrom') || '전체보기',
duration_time: (new Date().getTime() - Number(sessionStorage.getItem('enter_storeDetail'))) / 1000,
current_page: isomorphicSessionStorage.getItem('cameFrom') || '전체보기',
duration_time: (new Date().getTime() - Number(isomorphicSessionStorage.getItem('enter_storeDetail'))) / 1000,
});
}
if (pathname.includes(ROUTES.GraduationCalculator())) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/ui/IntroToolTip/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CloseIcon from 'assets/svg/tooltip-close-icon.svg';
import { isomorphicLocalStorage } from 'utils/ts/env';
import styles from './IntroToolTip.module.scss';

interface IntroToolTipProps {
Expand All @@ -8,7 +9,7 @@ interface IntroToolTipProps {

export default function IntroToolTip({ content, closeTooltip }: IntroToolTipProps) {
const handleTooltipCloseButtonClick = () => {
localStorage.setItem('store-review-tooltip', 'used');
isomorphicLocalStorage.setItem('store-review-tooltip', 'used');
closeTooltip();
};
return (
Expand Down
3 changes: 2 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { COOKIE_KEY } from 'static/url';
import useAutoLogin from 'utils/hooks/auth/useAutoLogin';
import useMount from 'utils/hooks/state/useMount';
import { getCookie } from 'utils/ts/cookie';
import { isomorphicLocalStorage } from 'utils/ts/env';
import { requestTokensFromNative, setTokensFromNative } from 'utils/ts/iosBridge';
import { useServerStateStore } from 'utils/zustand/serverState';

Expand Down Expand Up @@ -103,7 +104,7 @@ export default function App({ Component, pageProps }: AppPropsWithAuth) {
}
// 로깅을 위한 userId 전달 및 gtag 함수 정의
if (typeof window !== 'undefined') {
const userId = localStorage.getItem('uuid') || '';
const userId = isomorphicLocalStorage.getItem('uuid') || '';

window.dataLayer = window.dataLayer || [];

Expand Down
3 changes: 2 additions & 1 deletion src/pages/auth/modifyinfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import useModalPortal from 'utils/hooks/layout/useModalPortal';
import useBooleanState from 'utils/hooks/state/useBooleanState';
import useTokenState from 'utils/hooks/state/useTokenState';
import { useUser } from 'utils/hooks/state/useUser';
import { isomorphicLocalStorage } from 'utils/ts/env';
import showToast from 'utils/ts/showToast';
import { isStudentUser } from 'utils/ts/userTypeGuards';
import { useTokenStore } from 'utils/zustand/auth';
Expand Down Expand Up @@ -1132,7 +1133,7 @@ const useModifyInfoForm = () => {
const router = useRouter();
const isMobile = useMediaQuery();
const onSuccess = () => {
localStorage.setItem(STORAGE_KEY.USER_INFO_COMPLETION, COMPLETION_STATUS.COMPLETED);
isomorphicLocalStorage.setItem(STORAGE_KEY.USER_INFO_COMPLETION, COMPLETION_STATUS.COMPLETED);
router.push(ROUTES.Main());
showToast('success', '성공적으로 정보를 수정하였습니다.');
queryClient.invalidateQueries({ queryKey: authQueryKeys.all });
Expand Down
7 changes: 4 additions & 3 deletions src/pages/graduation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useModalPortal from 'utils/hooks/layout/useModalPortal';
import useBooleanState from 'utils/hooks/state/useBooleanState';
import useTokenState from 'utils/hooks/state/useTokenState';
import { useScrollLock } from 'utils/hooks/ui/useScrollLock';
import { isomorphicSessionStorage } from 'utils/ts/env';
import { useSemester } from 'utils/zustand/semester';
import styles from './GraduationCalculatorPage.module.scss';

Expand Down Expand Up @@ -59,18 +60,18 @@ function GraduationCalculatorComponent() {
useEffect(() => {
if (!token) return;

const isFirstVisit = sessionStorage.getItem('visitedGraduationPage');
const isFirstVisit = isomorphicSessionStorage.getItem('visitedGraduationPage');

if (!isFirstVisit) {
sessionStorage.setItem('visitedGraduationPage', 'true');
isomorphicSessionStorage.setItem('visitedGraduationPage', 'true');

portalManager.open(() => <CalculatorHelpModal closeInfo={closeInfo} />);
}
}, [token]);

const logger = useLogger();
const handlePopState = React.useCallback(() => {
if (sessionStorage.getItem('swipeToBack') === 'true') {
if (isomorphicSessionStorage.getItem('swipeToBack') === 'true') {
logger.actionEventSwipe({
team: 'USER',
event_label: 'graduation_calculator_back',
Expand Down
Loading
Loading