진행하던 프로젝트에서 가격 범위를 설정하는 기능을 구현하여야 했습니다. 이 경우 흔히 양방향 슬라이더를 이용하여 구현합니다. 일반적인 양방향 슬라이더는 구글링으로 쉽게 구현할 수 있습니다.
하지만 추가적인 기능 요청이 들어옵니다. 슬라이더의 핸들이 오른쪽으로 갈수록 값이 변하는 속도가 빨라지게 하는 것이었습니다. 즉, 슬라이더의 핸들이 왼쪽에 있으면 값을 세밀하게 조절할 수 있도록 하고, 오른쪽에 있으면 큰 단위로 조절할 수 있도록 하는 것이었습니다.
구글링을 통해 쉽게 찾을 수 있으므로, 이 글에서는 양방향 슬라이더를 구현하는 방법에 대해서는 자세히 다루지 않겠습니다. 대신 핸들의 위치에 따라 값의 변화 속도를 달리하는 방법에 대해서 주로 다루어 보도록 하겠습니다.
이차함수의 그래프
저는 처음 이 기능 요청을 듣고 이차함수를 떠올렸습니다. 생뚱맞게 왜 이차함수가 떠올랐을까요? 요청받은 기능과 이차함수가 무슨 상관이 있을까요?
중학교 때 배운 이차함수의 그래프 모양을 떠올려봅시다. 기본적인 이차함수는 아래로 볼록한 포물선 모양의 그래프를 갖습니다. 이 모양이 갖는 중요한 특징이 있습니다.
그래프의 왼쪽 아래를 확대한 모습입니다. 가로축의 값이 0부터 0.3까지 증가할 동안 세로축의 값은 0부터 0.09까지 증가했습니다. 가로축의 값이 0.3만큼 커질 동안 세로축의 값은 0.09만큼 커졌네요.
이번에는 그래프의 오른쪽 윗부분입니다. 가로축의 값이 0.7부터 1까지 증가할 동안 세로축의 값은 0.49부터 1까지 증가했습니다. 가로축의 값이 0.3만큼 커질동안 세로축의 값은 0.51만큼 커졌네요.
두 경우 모두 가로축의 값은 0.3만큼 커졌습니다. 하지만 첫 번째 경우에서는 세로축의 값이 0.09 만큼밖에 안 커졌지만, 두 번째 경우에서는 0.51만큼이나 커졌습니다. 이게 의미하는 게 뭘까요?
기본적인 이차함수에서는 가로축의 값이 작을수록, 세로축의 값이 변하는 폭이 좁습니다. 마찬가지로 가로축의 값이 클수록, 그 폭은 넓습니다. 이차함수의 그래프 모양을 생각해 보면 어쩌면 당연한 성질입니다. 아래로 볼록한 모양이니까요.
우리가 구현하고자 하는 기능과 유사한 부분이 있는 것 같지 않나요? 슬라이더의 값이 작을수록 값이 변하는 속도가 작아야 하고, 이차함수의 가로축 값이 작을수록 세로축이 변하는 폭이 좁습니다. 공통점이 분명히 있죠.
계산 식 구하기
<input type="range" min="0" max="100" step="0.1" />
다시 슬라이더로 돌아가봅시다. HTML에서 슬라이더는 위 코드처럼 range 타입의 input을 통해 만들 수 있습니다. 속성으로는 최솟값을 의미하는 min, 최댓값을 의미하는 max, 값 변화 단위를 의미하는 step 등이 있습니다. 이 기본 슬라이더만으로는 우리가 구현하고자 하는 기능을 구현할 수 없습니다.
그래서 저는 이 슬라이더의 값을 그대로 사용하지 않고, 이 값에 이차함수를 씌워 얻은 값을 사용하고자 했습니다. 즉 슬라이더의 값은 앞선 이차함수 그래프에서의 가로축 값을 의미하게 되는 것이고, 최종 값은 세로축 값을 의미하게 되는 겁니다. (앞으로 이렇게 색으로 구분하겠습니다.)
이때 슬라이더의 속성은 min 값을 0, max 값을 100, step 값을 0.1로 고정한 상태로 가정하겠습니다. 이렇게 하면 핸들이 가장 왼쪽에 있을 때의 값은 0, 가장 오른쪽에 있을 때의 값은 100이 됩니다. 계산을 용이하게 하기 위한 값으로, 크게 의미가 있지는 않습니다.
const ratio = visualValue / 100;
먼저 슬라이더의 값을 100으로 나누어 전체 범위에 대한 슬라이더의 값의 비율 ratio을 알아냅니다. 이때 visualValue는 슬라이더의 값을 의미합니다. 비율이기 때문에 핸들이 가장 왼쪽에 있을 때 ratio 값은 0이고, 가장 오른쪽에 있을 때는 1이 됩니다.
return ratio * ratio * (max - min) + min;
그 다음 최종 값을 계산하여 반환해 줍니다. 왜 이런 계산식이 나왔을까요? (여기서의 max와 min은 위 슬라이더의 속성 0, 100을 의미하는 게 아니라, 실제 슬라이더의 최종 값이 가질 수 있는 최솟값과 최댓값을 의미한다는 점에 유의하세요!)
일단 ratio * ratio는 이해가 됩니다. 이차함수니까요. 그런데 맨 뒤에 더해진 min은 뭐죠? 곱해진 (max - min)은 뭔가요? ratio * ratio부터 시작하여 차근차근 알아봅시다.
핸들이 가장 왼쪽에 있는 상황을 가정해 보세요. 그때 슬라이더의 최종 값은 무슨 값이 되어야 하나요? min입니다. 말 그대로 최솟값이니까요. 이 상황에서는 위에서 말했다시피 ratio 값이 0입니다. 따라서 ratio * ratio = 0 * 0 = 0이죠. 그래서 뒤에 min을 더한 겁니다. ratio * ratio + min = 0 * 0 + min = min이 되기 때문입니다. 이제 뒤에 붙은 min은 이해했습니다.
반대로 핸들이 가장 오른쪽에 있는 상황을 가정해봅시다. 그때 슬라이더의 최종 값은 max가 되어야죠. 이 상황에서는 ratio 값이 1입니다. 따라서 ratio * ratio + min = 1 * 1 + min = 1 + min이죠. 이 값을 max가 되게 하려면 어떡해야 할까요? 두 가지 방법이 떠오릅니다.
1. (1 + min)을 빼고 max를 더한다.
ratio 값이 1일 때 계산 결과가 1 + min이니까 이걸 빼고 max를 더하게 하는 방법입니다. 그러면 계산 식은 ratio * ratio + min - (1 + min) + max가 되겠네요. 그런데 ratio에 0을 넣어보면 0 * 0 + min - (1 + min) + max = -1 + max가 됩니다. ratio 값이 0일 때에는 min이 나와야 되는데 말이죠. 이 방법은 아닌 것 같습니다.
2. ratio * ratio 항에 (max - min)을 곱한다.
계산 식은 ratio * ratio * (max - min) + min이 됩니다. 이렇게 하면 ratio 값이 0일 때 min이 되고, ratio 값이 1일 때에도 max를 잘 반환합니다. 올바른 식인 것 같죠. 실제로 이게 정답입니다.
계산 식은 잘 이해한 것 같습니다.
이제 남은 건 이 함수를 실제로 슬라이더와 연결하고, 슬라이더 아래에 표시되는 가이드 마커를 구현하는 것입니다. 하지만 글이 너무 길어진 관계로 다음 글에서 다루어보도록 하겠습니다. 그래서 다음 글은 코드 위주일 것 같습니다. 읽어주셔서 감사합니다.