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
3 changes: 3 additions & 0 deletions src/pages/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useQuery } from '@tanstack/react-query';
import { addDays, format } from 'date-fns';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { handleScrollLock } from '@/shared/utils/scroll-lock';

const Home = () => {
const { activeType, changeTab, isSingle, isGroup } = useTabState();
Expand All @@ -39,6 +40,8 @@ const Home = () => {

const { needsMatchingSetup } = useAuth();

handleScrollLock(needsMatchingSetup);

useEffect(() => {
const from = needsMatchingSetup ? 'onboarding' : 'return_user';
gaEvent('home_enter', { from });
Expand Down
3 changes: 3 additions & 0 deletions src/shared/components/bottom-sheet/bottom-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from '@libs/cn';
import { AnimatePresence, motion } from 'framer-motion';
import { useRef } from 'react';
import { createPortal } from 'react-dom';
import { handleScrollLock } from '@/shared/utils/scroll-lock';

interface BottomSheetProps {
isOpen: boolean;
Expand All @@ -25,6 +26,8 @@ const BottomSheet = ({
const sheetRef = useRef<HTMLDivElement>(null);
useOutsideClick(sheetRef, onClose);

handleScrollLock(isOpen);

Comment on lines +29 to +30
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

렌더 중 body 스타일 변경 제거 — 이펙트+클린업으로 전환

handleScrollLock(isOpen)을 렌더에서 호출하면 리렌더마다 DOM을 건드립니다. 이펙트로 감싸고 언마운트 시 원복해 주세요.

-  handleScrollLock(isOpen);
+  useEffect(() => {
+    if (!isOpen) return;
+    handleScrollLock(true);
+    return () => handleScrollLock(false);
+  }, [isOpen]);

추가 수정(파일 상단 import):

// 기존
import { useRef } from 'react';
// 변경
import { useEffect, useRef } from 'react';
🤖 Prompt for AI Agents
In src/shared/components/bottom-sheet/bottom-sheet.tsx around lines 29-30,
remove the direct call to handleScrollLock(isOpen) from the render path and
instead add a useEffect that calls handleScrollLock(isOpen) when isOpen changes
and returns a cleanup that restores the body styles (i.e., calls
handleScrollLock(false) or reverts whatever changes were applied); also update
the imports at the top to include useEffect alongside useRef. Ensure the effect
runs only when isOpen changes and that cleanup runs on unmount to restore scroll
behavior.

return createPortal(
<AnimatePresence>
{isOpen && (
Expand Down
9 changes: 9 additions & 0 deletions src/shared/utils/scroll-lock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const handleScrollLock = (isOpen: boolean) => {
if (isOpen) {
document.body.style.overflow = 'hidden';
document.body.style.touchAction = 'none';
} else {
document.body.style.removeProperty('overflow');
document.body.style.removeProperty('touch-action');
}
};
Comment on lines +1 to +9
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

동시 오버레이에서 잠금이 풀릴 수 있는 경쟁 상태 — ref-count와 복원 로직으로 교체 필요

Home/BottomSheet가 각각 토글하면 한 쪽이 닫힐 때 다른 쪽이 열려 있어도 스크롤 잠금이 해제될 수 있습니다. SSR/초기 렌더에서도 안전 가드가 필요합니다.

아래처럼 참조 카운트, 이전 스타일 복원, 환경 가드를 추가해 주세요.

-export const handleScrollLock = (isOpen: boolean) => {
-  if (isOpen) {
-    document.body.style.overflow = 'hidden';
-    document.body.style.touchAction = 'none';
-  } else {
-    document.body.style.removeProperty('overflow');
-    document.body.style.removeProperty('touch-action');
-  }
-};
+let scrollLockCount = 0;
+let prevOverflow: string | null = null;
+let prevTouchAction: string | null = null;
+
+export const handleScrollLock = (lock: boolean) => {
+  if (typeof document === 'undefined' || !document.body) return;
+  const style = document.body.style;
+
+  if (lock) {
+    if (scrollLockCount === 0) {
+      prevOverflow = style.overflow || '';
+      prevTouchAction = style.touchAction || '';
+      style.overflow = 'hidden';
+      style.touchAction = 'none';
+    }
+    scrollLockCount += 1;
+  } else {
+    scrollLockCount = Math.max(0, scrollLockCount - 1);
+    if (scrollLockCount === 0) {
+      if (prevOverflow) style.overflow = prevOverflow; else style.removeProperty('overflow');
+      if (prevTouchAction) style.touchAction = prevTouchAction; else style.removeProperty('touch-action');
+      prevOverflow = null;
+      prevTouchAction = null;
+    }
+  }
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const handleScrollLock = (isOpen: boolean) => {
if (isOpen) {
document.body.style.overflow = 'hidden';
document.body.style.touchAction = 'none';
} else {
document.body.style.removeProperty('overflow');
document.body.style.removeProperty('touch-action');
}
};
let scrollLockCount = 0;
let prevOverflow: string | null = null;
let prevTouchAction: string | null = null;
export const handleScrollLock = (lock: boolean) => {
if (typeof document === 'undefined' || !document.body) return;
const style = document.body.style;
if (lock) {
if (scrollLockCount === 0) {
prevOverflow = style.overflow || '';
prevTouchAction = style.touchAction || '';
style.overflow = 'hidden';
style.touchAction = 'none';
}
scrollLockCount += 1;
} else {
scrollLockCount = Math.max(0, scrollLockCount - 1);
if (scrollLockCount === 0) {
if (prevOverflow) style.overflow = prevOverflow;
else style.removeProperty('overflow');
if (prevTouchAction) style.touchAction = prevTouchAction;
else style.removeProperty('touch-action');
prevOverflow = null;
prevTouchAction = null;
}
}
};
🤖 Prompt for AI Agents
In src/shared/utils/scroll-lock.ts around lines 1 to 9, the current simple
toggle causes a race where multiple overlays can remove the scroll lock when one
closes; replace it with a reference-counted lock that increments on open and
decrements on close, only restoring styles when the count reaches zero; store
and restore previous body style values (overflow and touch-action) rather than
blindly removing properties; add environment guards so the logic no-ops during
SSR or when document/body is unavailable; ensure the exported API exposes
functions to acquire/release the lock (or keeps the same handleScrollLock
boolean API but internally manages the ref count and restoration).