라이브러리 없이 드래그로 크기 조절되는 분할 패널 만들기
분할 패널 라이브러리 대신 포인터 이벤트 3개로 드래그 크기 조절을 직접 붙인 과정과 놓치기 쉬운 마무리를 정리했어요.
화면을 둘로 나눠 한쪽엔 목록, 다른 쪽엔 상세를 띄우는 UI가 있어요. 그런데 사람마다 넓게 보고 싶은 영역이 달라요. 혹시 "이 경계선을 마우스로 끌어서 넓히면 좋겠는데" 싶었던 적 없나요?
플랜엘(plan-l)은 법령과 판례를 검색하는 도구예요. 왼쪽에 검색 결과 목록, 오른쪽에 선택한 조문 원문을 나란히 보여줘요. 조문을 길게 읽을 땐 오른쪽을 넓히고, 목록을 훑을 땐 왼쪽을 넓히고 싶더라고요. 이걸 라이브러리 없이 직접 붙여 봤어요.
왜 라이브러리를 안 썼나
분할 패널 라이브러리는 많아요. 그런데 이 기능 하나 때문에 의존성을 또 얹는 게 아깝더라고요. 핵심 동작만 놓고 보면 코드가 30줄 안쪽이라, 번들에 0KB 더 얹는 쪽을 택했어요.
원리는 이벤트 3개가 전부
가운데 경계선(divider)에 포인터 이벤트 세 개만 걸면 돼요.
- pointerdown: 경계선을 누르는 순간 드래그 시작
- pointermove: 커서가 움직인 만큼 왼쪽 패널 너비를 다시 계산
- pointerup: 손을 떼면 종료
여기서 native API 하나가 큰 역할을 해요. pointerdown 때 setPointerCapture를 호출하면, 커서를 빠르게 움직여 경계선 밖으로 벗어나도 이벤트가 계속 그 경계선으로 들어와요. 이게 없으면 빨리 끌 때 드래그가 뚝뚝 끊기더라고요. 직접 테스트해 보면 차이가 확 느껴져요.
너비는 컨테이너 기준 커서 x좌표에서 뽑아요. 그대로 쓰면 패널이 0까지 줄거나 화면을 넘길 수 있어서, 최소 200px에 최대는 컨테이너 안쪽으로 clamp를 걸었어요.
divider.addEventListener('pointerdown', (e) => {
divider.setPointerCapture(e.pointerId);
const move = (ev) => {
const raw = ev.clientX - container.offsetLeft;
const w = Math.min(Math.max(raw, 200), container.clientWidth - 200);
left.style.width = `${w}px`;
};
divider.addEventListener('pointermove', move);
divider.addEventListener('pointerup', () => {
divider.removeEventListener('pointermove', move);
}, { once: true });
});놓치기 쉬운 마무리
돌려 보니 손볼 곳이 몇 군데 있었어요.
- cursor col-resize
- 드래그 중 user-select 잠그기
- 최소
- 최대 clamp
- setPointerCapture
- 경계선에
cursor: col-resize를 줘야 "여기 끌 수 있어요"가 전달돼요. - 드래그 중 텍스트가 파랗게 선택되는 걸 막으려면 잡는 동안
user-select: none을 켜요. - 모바일에서도 같은 코드가 도는데, 경계선이 너무 얇으면 손가락으로 못 잡아요. 터치 영역을 조금 넓게 잡아 봤더니 훨씬 편했어요.
정리
라이브러리 없이도 이벤트 3개에 clamp 한 줄이면 끌어서 조절되는 분할 패널이 돼요. 적용해 봤더니 10분 정도로 붙었고, 플랜엘(plan-l)처럼 나란히 읽는 화면에선 체감이 꽤 달라요. 작은 도구를 혼자 만들다 보면 "라이브러리를 부를까, 몇 줄 쓸까"를 자주 저울질하게 돼요. 이번 건은 몇 줄 쓰는 쪽이 맞았어요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.