- [CSS] 로딩 스피너 뒤에서도 드래그 이벤트 활성화하기2025년 10월 22일 16시 37분 13초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
로딩 스피너 뒤에서도 드래그 이벤트 활성화하기
애플리케이션에서 로딩 스피너를 전체 화면에 표시할 때, 사용자가 뒤쪽 요소들과 상호작용 가능해야 하는 경우가 있다. 이 글에서는 로딩 중에도 드래그 이벤트 등을 가능하게 하는 방법을 다룬다.
먼저 CSS 스피너 애니메이션을 위한 스타일을 추가해야 한다.
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }1. 문제 상황
일반적인 로딩 컴포넌트는 다음과 같이 구현된다.
function Loading() { return ( <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000, }} > <div style={{ width: "40px", height: "40px", border: "4px solid rgba(0, 0, 0, 0.1)", borderTop: "4px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> </div> ); }이 코드의 문제점은
position: fixed로 전체 화면을 덮고 있어서 마우스 이벤트가 뒤쪽 요소로 전달되지 않는다는 것이다.2. 해결 방법
2-1. pointer-events: none 사용 (권장)
가장 간단하고 효과적인 방법은 CSS의
pointer-events: none속성을 사용하는 것이다.function Loading() { return ( <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000, pointerEvents: "none", // 핵심! }} > <div style={{ width: "40px", height: "40px", border: "4px solid rgba(0, 0, 0, 0.1)", borderTop: "4px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> </div> ); }pointer-events: none을 추가하면:- 마우스 클릭, 드래그, 호버 등의 이벤트가 뒤쪽 요소로 전달된다.
- 로딩 스피너는 여전히 화면에 표시된다.
- 사용자는 로딩 중에도 뒤쪽 요소들과 상호작용할 수 있다.
2-2. 스피너 위치 조정
전체 오버레이 대신 스피너만 특정 위치에 표시하는 방법이다.
function Loading() { return ( <div style={{ position: "fixed", top: "20px", right: "20px", zIndex: 1000, }} > <div style={{ width: "30px", height: "30px", border: "3px solid rgba(0, 0, 0, 0.1)", borderTop: "3px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> </div> ); }2-3. 반투명 배경과 함께 사용
반투명 배경이 필요한 경우, 배경과 스피너를 분리하여 구현한다.
function Loading() { return ( <> {/* 반투명 배경 */} <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, background: "rgba(0, 0, 0, 0.1)", zIndex: 999, pointerEvents: "none", }} /> {/* 스피너 */} <div style={{ position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", zIndex: 1000, pointerEvents: "none", }} > <div style={{ width: "40px", height: "40px", border: "4px solid rgba(0, 0, 0, 0.1)", borderTop: "4px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> </div> </> ); }3. pointer-events 속성의 다른 옵션들
CSS의
pointer-events속성은none외에도 다양한 값을 가질 수 있다.3-1. 기본 값들
auto(기본값): 요소가 마우스 이벤트의 대상이 될 수 있다.none: 요소가 마우스 이벤트의 대상이 되지 않으며, 이벤트가 뒤쪽 요소로 전달된다.
.loading-overlay { pointer-events: auto; /* 기본 동작 */ pointer-events: none; /* 이벤트 무시 */ }3-2. SVG 전용 값들
SVG 요소에서는 더 세밀한 제어가 가능하다. SVG는 HTML 요소와 달리 fill(채우기)과 stroke(테두리)를 구분하여 이벤트를 처리할 수 있다.
3-2-1. SVG 구조적 특성
SVG 요소는 다음 두 가지 주요 속성을 가진다:
- fill: 도형의 내부 채우기
- stroke: 도형의 테두리 선
3-2-2. 각 값의 상세 동작
visiblePainted(기본값): 요소가 보이고 채우기나 선이 있을 때만 이벤트 대상이 된다
<circle cx="50" cy="50" r="40" fill="blue" stroke="red" style="pointer-events: visiblePainted;" /> <!-- 파란 채우기와 빨간 테두리가 있으므로 클릭 가능 --> <circle cx="150" cy="50" r="40" fill="none" stroke="none" style="pointer-events: visiblePainted;" /> <!-- 채우기도 선도 없으므로 클릭 불가능 -->visibleFill: 요소가 보이고 채우기가 있을 때만 이벤트 대상이다
<circle cx="50" cy="50" r="40" fill="blue" stroke="red" stroke-width="10" style="pointer-events: visibleFill;" /> <!-- 파란 채우기 부분만 클릭 가능, 빨간 테두리는 클릭 불가 -->visibleStroke: 요소가 보이고 선이 있을 때만 이벤트 대상이다
<circle cx="50" cy="50" r="40" fill="blue" stroke="red" stroke-width="10" style="pointer-events: visibleStroke;" /> <!-- 빨간 테두리 부분만 클릭 가능, 파란 채우기는 클릭 불가 -->visible: 요소가 보이기만 하면 채우기나 선의 유무와 관계없이 전체 영역이 이벤트 대상이다
<circle cx="50" cy="50" r="40" fill="none" stroke="none" style="pointer-events: visible;" /> <!-- 투명하지만 전체 원 영역이 클릭 가능 -->painted: visibility 속성과 관계없이 채우기나 선이 있으면 이벤트 대상이다fill: visibility와 관계없이 채우기가 있는 부분만 이벤트 대상이다stroke: visibility와 관계없이 선이 있는 부분만 이벤트 대상이다all: 모든 조건을 무시하고 항상 이벤트 대상이다
3-2-3. SVG 활용 예제
도넛 차트에서 내부 구멍 클릭 방지:
<svg width="200" height="200"> <!-- 외부 원: 클릭 가능 --> <circle cx="100" cy="100" r="80" fill="lightblue" style="pointer-events: visibleFill;" /> <!-- 내부 원: 클릭으로 구멍 뚫기 --> <circle cx="100" cy="100" r="40" fill="white" style="pointer-events: none;" /> </svg>복잡한 그래프에서 선만 클릭 가능하게 하는 방법:
<svg width="300" height="200"> <!-- 배경 영역: 클릭 불가 --> <rect width="300" height="200" fill="lightgray" style="pointer-events: none;" /> <!-- 그래프 선: 클릭 가능 --> <path d="M10,100 L50,50 L100,80 L150,30 L200,60" fill="none" stroke="blue" stroke-width="3" style="pointer-events: visibleStroke;" /> </svg>3-2-4. HTML 요소와의 차이점
HTML 요소에서는 이런 세밀한 제어가 불가능하다:
/* HTML에서는 전체 요소 단위로만 제어 */ .html-element { pointer-events: auto; /* 전체 요소 */ pointer-events: none; /* 전체 요소 무시 */ }반면 SVG에서는 부분별 제어가 가능하다:
/* SVG에서는 세밀한 제어 가능 */ .svg-element { pointer-events: visibleFill; /* 채우기만 */ pointer-events: visibleStroke; /* 테두리만 */ pointer-events: visible; /* 전체 영역 */ }3-3. 실제 활용 예시
// 조건부 pointer-events 적용 function Loading({ allowInteraction = false }) { return ( <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000, pointerEvents: allowInteraction ? "none" : "auto", }} > <div style={{ width: "40px", height: "40px", border: "4px solid rgba(0, 0, 0, 0.1)", borderTop: "4px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> </div> ); }3-4. 부분적 상호작용 허용
특정 요소만 클릭 가능하게 하고 싶을 때의 방법이다.
function Loading() { return ( <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000, pointerEvents: "none", // 전체적으로 이벤트 무시 }} > <div style={{ pointerEvents: "auto", textAlign: "center" }}> {/* 이 부분만 클릭 가능 */} <div style={{ width: "40px", height: "40px", border: "4px solid rgba(0, 0, 0, 0.1)", borderTop: "4px solid rgba(0, 0, 0, 0.3)", borderRadius: "50%", animation: "spin 1s linear infinite", margin: "0 auto 20px", }} /> <button onClick={() => console.log("취소")}>로딩 취소</button> </div> </div> ); }4. 주의사항
pointer-events: none을 사용할 때는 스피너 자체도 클릭할 수 없게 된다.- 로딩 중에 사용자 상호작용을 완전히 차단하고 싶다면 이 방법을 사용하지 않아야 한다.
- 접근성을 고려하여 로딩 상태를 스크린 리더에게 알려주는 것도 중요하다.
- SVG 관련 값들은 일반 HTML 요소에서는
auto나none과 동일하게 동작한다.
5. 결론
pointer-events: none은 로딩 스피너 뒤에서도 사용자 상호작용을 가능하게 하는 간단하고 효과적인 방법이다. 사용자 경험을 개선하면서도 로딩 상태를 명확히 표시할 수 있어 많은 상황에서 유용하게 활용할 수 있다.728x90반응형'언어·프레임워크 > HTML·CSS' 카테고리의 다른 글
웹 접근성, 왜 중요한가? WCAG 2.1 AA와 고대비 모드 지원 (0) 2025.09.12 속성을 의미하는 Attribute와 Property의 차이점 (1) 2025.07.31 [HTML] `autocomplete` 속성: 웹 개발자가 알아야 할 모든 것 (2) 2025.06.27 [CSS] 선택자: 공백 하나로 의미가 달라지는 마법 (0) 2025.06.24 [CSS] CSS를 활용한 요소의 확장 기준점 제어하기 (0) 2025.06.15 다음글이 없습니다.이전글이 없습니다.댓글