인터랙션

[Swipe to Delete] 왼쪽으로 밀면 보관/삭제가 나오는 iOS식 리스트

행을 손가락으로 밀어 숨은 액션을 꺼내고, 끝까지 밀면 바로 삭제. 드래그 중 리렌더 없이 ref로 직접 움직이는 Pointer Events 스와이프 리스트입니다.

ReactPointer EventsuseRefuseLayoutEffectCSS transform/transitionTailwind CSS
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약 - 리스트 한 줄을 왼쪽으로 밀면 뒤에 숨어 있던 보관/삭제 버튼이 드러나고, 끝까지 밀면 손을 떼는 순간 바로 사라지는 모바일식 리스트입니다.

이럴 때 필요해요

모바일에서 항목마다 삭제 버튼을 늘 보이게 두면 화면이 지저분하고 실수로 누르기도 쉽습니다. 평소엔 깔끔하게 내용만 보여주다가, 사용자가 의도적으로 밀었을 때만 액션을 꺼내주면 화면도 단정하고 오작동도 줄어듭니다. 받은편지함, 알림 목록, 할 일 리스트처럼 항목을 빠르게 정리하는 화면에 잘 맞아요.

어떻게 동작하나

  1. 한 줄을 뒤(액션 버튼)와 앞(내용 카드) 두 겹으로 겹쳐 둡니다. 평소엔 앞 카드가 뒤를 완전히 덮고 있어요.

  2. 손가락을 올리고 움직이면 처음 몇 px로 가로로 미는 중인지 세로로 스크롤하려는 건지를 먼저 판정합니다. 세로면 스와이프를 포기하고 페이지 스크롤에 양보합니다 (안 그러면 스크롤이 먹통이 돼요).

  3. 가로로 확정되면 앞 카드만 손가락을 따라 왼쪽으로 밀려 뒤 버튼이 드러납니다. 손을 떼는 위치에 따라 조금이면 닫고, 절반쯤이면 열린 채 고정, 끝까지면 그대로 날려 삭제 셋 중 하나로 스냅됩니다.

핵심은 이거예요

미는 동안에는 React 상태를 바꾸지 않고 ref로 DOM transform을 직접 갱신해 매 프레임 리렌더를 0으로 만듭니다. 손을 떼는 순간에만 CSS transition을 켜서 스냅이 부드럽게 보이게 하는 것 - 드래그 중엔 transition off + 직접 transform, 놓을 때 transition on 조합이 모든 스와이프 인터랙션의 기본기예요.

놓치기 쉬운 것

  • 방향 판정을 안 하면 세로 스크롤이 막혀 모바일에서 답답해집니다. 처음 작은 움직임으로 가로/세로를 먼저 가르는 게 필수.

  • 포인터 캡처(setPointerCapture)는 가로로 확정된 뒤에 걸어야 합니다. 너무 일찍 걸면 세로 스크롤까지 가로채요.

  • 마우스만 쓰는 사람과 스크린리더 사용자를 위해 Delete 키 삭제와 명확한 aria-label 같은 대체 수단을 꼭 같이 둡니다.

이런 곳에 써요

  • iOS 메일/Gmail의 밀어서 보관/삭제, 카카오톡 채팅 목록 밀기

  • 알림 센터, 장바구니, 할 일 앱처럼 항목을 빠르게 치우는 리스트

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { SwipeToDeleteDemo } from "./-components/SwipeToDeleteDemo";

export const metadata: Metadata = {
  title: "스와이프 삭제 리스트 (데모)",
  description:
    "행을 왼쪽으로 밀면 숨은 액션(보관/삭제)이 드러나고, 끝까지 밀면 바로 삭제되는 iOS식 스와이프 리스트. 되돌리기 + 키보드 조작 지원.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main className="flex min-h-[100dvh] items-center justify-center bg-white">
      <SwipeToDeleteDemo />
    </main>
  );
}
18조회수

댓글