[FIX] 버튼 중복 입력 및 알림창 터치 관통 방지#66
Merged
Merged
Conversation
sunm2n
reviewed
May 26, 2026
| const handleRetry = useCallback(() => { | ||
| startLoadNotices(); | ||
| }, [startLoadNotices]); | ||
| const guardedOpenNotice = useGuardedPress((id: number) => |
Contributor
There was a problem hiding this comment.
notices.tsx - useCallback deps 누락 가능성
const guardedOpenNotice = useGuardedPress((id: number) =>
router.push({ ... }),
);
const renderNotice = useCallback(
({ item }) => <TouchableOpacity onPress={() => guardedOpenNotice?.(item.id)}
/>,
[guardedOpenNotice], // ← 올바르게 포함됨 ✓
);
deps 포함은 맞는거 같습니다. 다만 guardedOpenNotice가 매 렌더마다 새로 생성되는 인라인
함수((id: number) => router.push(...))에 의존하므로, 렌더마다 useCallback이 무효화될 수 있습니다.
해당 함수를 컴포넌트 밖으로 분리하거나 useCallback으로 감싸면 최적화가 되지 않을까 합니다.
Contributor
Author
There was a problem hiding this comment.
피드백 감사합니다!
확인해보니 renderNotice의 dependency에 guardedOpenNotice를 포함한 것은 맞지만, useGuardedPress에 전달하는 handler가 인라인 함수라 매 렌더마다 새로 생성되고 있었습니다.
useGuardedPress 내부에서도 handler를 dependency로 사용하는 구조라, 결과적으로 guardedOpenNotice와 renderNotice가 불필요하게 재생성될 수 있다고 판단했습니다.
동작상의 문제는 아니지만 FlatList renderItem에 연결되는 콜백인 만큼, 가독성과 렌더 최적화를 위해 공지 상세 이동 handler를 useCallback으로 분리해 useGuardedPress에 전달하도록 수정하겠습니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #56
개요
앱 전반에서 버튼을 빠르게 여러 번 누를 때 동일한 액션이 중복 실행되는 문제를 방지했습니다.
기존에는 저장, 삭제, 수정, 폴더 생성/수정/삭제, 화면 이동 버튼 등을 빠르게 연속 터치하면 동일한 API 요청이나 네비게이션이 여러 번 실행될 수 있었습니다. 예를 들어 링크 저장 버튼을 연속으로 누르면 저장 요청이 중복으로 발생할 수 있고, 삭제 확인창의 삭제 버튼을 빠르게 누르면 동일 삭제 요청이 반복 실행될 여지가 있었습니다. 화면 이동 버튼 역시 짧은 시간 안에 여러 번 입력되면 같은 화면이 중복으로 push될 수 있는 구조였습니다.
이번 작업에서는 이런 문제를 개별 화면에서만 막는 방식이 아니라, 공통 버튼과 주요 액션 컴포넌트에 재사용 가능한 press guard를 적용하는 방식으로 정리했습니다. 버튼을 한 번 누른 직후 짧은 시간 동안 추가 입력을 무시하고, 비동기 요청을 반환하는 액션은 요청이 끝날 때까지 같은 액션이 다시 실행되지 않도록 했습니다. 여기에 저장, 삭제, 수정, 폴더 생성처럼 실제 API 요청을 발생시키는 흐름에는 별도의 in-flight 상태를 함께 적용해 렌더링 타이밍 사이에 발생할 수 있는 재진입도 방지했습니다.
또한
Alert.alert확인창이 표시된 상태에서 뒤쪽 화면의 버튼이 함께 눌리지 않도록 보완했습니다. Alert 표시 중에는 앱 루트에 투명한 터치 차단 레이어를 올려 뒤 화면으로 터치가 관통하지 않게 하고, 확인창의 확인/삭제/취소 버튼 역시 빠르게 연속으로 눌러도 동일 콜백이 반복 실행되지 않도록showAlert래퍼를 통해 관리했습니다.이 과정에서 검사 시작 버튼처럼 화면 이동 직후 잠금 상태가 남을 수 있는 케이스도 함께 확인했습니다. 검사 화면으로 이동한 뒤 뒤로가기로 입력 화면에 돌아왔을 때 버튼이 계속 비활성화되지 않도록, 화면이 다시 포커스될 때 네비게이션 잠금을 해제하도록 수정했습니다.
주요 구현 내용
utils/press-guard.ts추가utils/guarded-alert.ts추가PressBlocker추가PressBlocker연결useGuardedPress적용Button,AppIcon,ActionIconButton,AddFolderButton,ScanButton중복 입력 차단Alert.alert직접 호출을showAlert기반 흐름으로 정리파일별 역할
utils/press-guard.ts: 버튼/Pressable 액션의 짧은 연속 입력 및 in-flight 중복 실행 차단utils/guarded-alert.ts: Alert 표시 중 전역 터치 차단 상태를 시작/해제하고 Alert 버튼 중복 실행 방지components/ui/press-blocker.tsx: Alert 표시 중 뒤 화면 터치를 막는 투명 차단 레이어app/_layout.tsx: 앱 루트에PressBlocker연결components/ui/button.tsx: 공통 Button의disabled/loading상태 및 press guard 적용components/ui/app-icon.tsx: 공통 아이콘 버튼 press guard 적용components/ui/action-icon-button.tsx: 삭제/지우기 액션 버튼 press guard 적용components/ui/add-folder-button.tsx: 폴더 추가 버튼 press guard 적용components/ui/scan-button.tsx: 검사 시작 버튼 press guard 적용components/ui/card-link.tsx: 링크 카드, 북마크, 더보기 버튼 중복 입력 차단components/ui/link-save-modal.tsx: 링크 저장 모달의 저장/취소 버튼 중복 입력 차단components/ui/title-edit-modal.tsx: 제목 수정 모달의 저장/취소 버튼 중복 입력 차단components/ui/folder-card.tsx: 폴더 카드 및 더보기 버튼 중복 입력 차단components/ui/folder-context-menu.tsx: 컨텍스트 메뉴 항목 중복 실행 차단app/(tabs)/(home)/scan-result.tsx: safe 결과 링크 저장 중복 요청 방지app/(tabs)/(home)/scan-result-caution.tsx: caution 결과 링크 저장 중복 요청 방지app/(tabs)/(home)/add-link.tsx: 검사 시작 중복 네비게이션 방지 및 뒤로가기 복귀 시 잠금 해제app/(tabs)/(home)/index.tsx: 홈 최근 저장 링크 삭제/북마크/제목 수정 중복 실행 방지app/(tabs)/(home)/saved-links.tsx: 저장 링크 목록 삭제/북마크/제목 수정 중복 실행 방지app/(tabs)/(folder)/index.tsx: 폴더명 수정/삭제 중복 요청 방지app/(tabs)/(folder)/folder-name.tsx: 폴더 생성 다음 화면 이동 중복 방지 및 복귀 시 잠금 해제app/(tabs)/(folder)/folder-url-select.tsx: 폴더 생성 요청 중복 방지app/(tabs)/(folder)/folder-add-url.tsx: 폴더 URL 추가 요청 중복 방지app/(tabs)/(folder)/[id].tsx: 폴더에서 링크 제외 요청 중복 방지app/(tabs)/(home)/settings.tsx: 로그아웃/회원탈퇴 확인창 및 요청 중복 실행 방지해결한 이슈 목록
Button컴포넌트의loading또는disabled상태 중복 입력 차단 확인AppIcon,ActionIconButton,AddFolderButton등 공통 액션 버튼 중복 입력 차단 적용disabled또는 in-flight 상태 적용Alert.alert확인창 표시 중 뒤쪽 화면 버튼 터치 차단체크 사항
참고
북마크 버튼의 API 호출 간격 조정 또는 별도 디바운스 정책은 이슈 설명에 따라 이번 PR 범위에서 제외했습니다.
이번 PR에서는 버튼 중복 입력 방지와 Alert 표시 중 터치 관통 방지를 우선 처리했습니다.