슬라이더 드래그랑 텍스트 입력, 두 가지로 같은 값을 조작하게 만들다가 NaN이 슬라이더를 망가뜨리는 걸 겪고 나서야 이 구조를 제대로 나눴어요. media-crop 저장소의 MediaCropEditor.tsx 얘기예요. FFmpeg.wasm으로 영상 자르는 방법에서 다룬 -c copy가 실제로 어떤 구간 값을 받는지, 그 구간을 정하는 UI 쪽을 볼게요.
같은 구간을 두 가지 방식으로 조작해요
이 도구는 자를 구간을 슬라이더로도, 00:00:00 형식 텍스트 입력으로도 지정할 수 있어요. 두 조작 방식은 결국 하나의 range 상태([시작 초, 끝 초])로 수렴해야 하니까, 어느 쪽에서 값이 바뀌든 다른 쪽 표시도 같이 갱신해줘야 했어요.
슬라이더가 바뀌면 텍스트 입력을 다시 계산해요
useEffect(() => {
setStartTimeInput(formatTime(range[0]));
setEndTimeInput(formatTime(range[1]));
}, [range]);
const formatTime = (seconds: number) => {
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return [hrs, mins, secs].map((v) => v.toString().padStart(2, '0')).join(':');
};
range가 바뀔 때마다(슬라이더 움직일 때) useEffect가 초 단위 숫자를 HH:MM:SS 문자열로 바꿔서 텍스트 입력 상태 두 개에 반영해요. 이 방향은 항상 자동으로 잘 돌아가더라고요.
텍스트 입력이 바뀌면 확정 시점에만 range를 갱신해요
const parseTimeToSeconds = (timeStr: string) => {
const parts = timeStr.split('.')[0].split(':').map(Number);
let h = 0, m = 0, s = 0;
if (parts.length === 3) { [h, m, s] = parts; }
else if (parts.length === 2) { [m, s] = parts; }
else if (parts.length === 1) { [s] = parts; }
return h * 3600 + m * 60 + s;
};
const applyManualTime = (type: 'start' | 'end') => {
const seconds = parseTimeToSeconds(type === 'start' ? startTimeInput : endTimeInput);
if (isNaN(seconds)) return;
const clampedSeconds = Math.max(0, Math.min(duration, seconds));
if (type === 'start') {
setRange([clampedSeconds, Math.max(clampedSeconds, range[1])]);
} else {
setRange([Math.min(clampedSeconds, range[0]), clampedSeconds]);
}
};
반대 방향은 타이핑할 때마다 바로 반영 안 해요. handleManualTimeChange는 입력 중인 문자열만 startTimeInput/endTimeInput에 담아두고, 따로 applyManualTime이 호출돼야 비로소 parseTimeToSeconds로 초 단위 값을 계산해서 range에 반영해요. 타이핑 중간의 불완전한 문자열("00:0" 같은)마다 range를 갱신하려 들면 NaN이 슬라이더 위치를 깨뜨릴 수 있어서 이렇게 나눴어요.
경계값은 항상 반대쪽 값 기준으로 잘라요
Math.max(0, Math.min(duration, seconds))로 값을 영상 길이 범위 안에 강제로 가두고, 시작 시각 바꿀 땐 Math.max(clampedSeconds, range[1])로 끝 시각보다 앞서게, 끝 시각 바꿀 땐 반대로 시작 시각보다 뒤서게 묶었어요. 이 두 방향 clamp가 없었다면 텍스트 입력만으로 시작이 끝보다 늦은, 뒤집힌 구간 값이 만들어질 뻔했어요.