Preview
"지금, 같이 볼래?"라는 서비스를 만들고 있습니다. 낯선 사람이 유튜브 링크를 띄우면 다른 누군가가 그걸 눌러 1:1로 같이 보는 건데, 만들면서 제일 골치 아팠던 게 "두 사람의 영상을 같은 지점에 맞추는 것" 이었어요. 같은 고민을 하는 분이 있을 것 같아 삽질 과정을 공유합니다.
1. "그냥 currentTime 맞추면 되지 않나?" — 안 됩니다
처음엔 단순하게 생각했어요. 호스트의 재생 위치(currentTime)를 게스트한테 보내서 seekTo 하면 끝 아닌가?
문제는 네트워크 지연입니다. 호스트가 "지금 2분 30초"라고 보내도, 그 메시지가 게스트한테 도착할 땐 이미 2분 30.4초가 지나있어요. 그대로 맞추면 게스트가 항상 살짝 뒤처집니다. 그리고 seekTo를 자꾸 호출하면 영상이 계속 버벅여요(매번 버퍼링).
2. 서버를 '기준 시계'로 삼기
그래서 서버 시간을 기준점으로 잡았습니다. 클라이언트끼리 시간을 맞추려 하지 않고, 둘 다 서버 시간에 맞추는 거죠.
문제는 클라이언트 시계가 제각각이라는 것(누군 30초 빠르고 누군 느리고). 그래서 NTP 비슷하게 RTT(왕복 지연)를 재서 보정했어요:
offset = serverTs - clientTs - rtt/2여기에 EMA(지수이동평균, alpha 0.3)를 씌워서 한 번 튄 값에 휘둘리지 않게 했습니다. 핑이 갑자기 200ms 튀어도 추정 오프셋이 출렁이지 않아요.
3. 보정은 '3단계'로 — 사용자가 못 느끼게
여기가 핵심인데, drift(예상 위치 - 실제 위치)에 따라 다르게 대응합니다:
0.5초 미만: 그냥 무시. 사람은 0.5초 차이 못 느낍니다. 여기서 seek 걸면 오히려 거슬려요.
0.5초 ~ 2초: seek 대신 재생 속도를 0.95배 또는 1.05배로 슬쩍 조정. 앞서면 느리게, 뒤처지면 빠르게. 몇 초 지나면 자연스럽게 따라잡고, 사용자는 눈치 못 챕니다.
2초 초과: 그제야 seekTo로 강제 점프. (광고 보는 중엔 이 보정 전부 멈춤 — 광고 흐름 깨면 안 되니까)
코드로는 이게 전부예요:
function decideDrift(local, expected) {
const drift = expected - local;
const abs = Math.abs(drift);
if (abs < 0.5) return { kind: 'ignore' };
if (abs <= 2) return { kind: 'rate', rate: drift > 0 ? 1.05 : 0.95 };
return { kind: 'seek', positionSec: expected };
}"seek로 다 맞추면 되지"가 아니라, 언제 seek를 안 할지가 더 중요했어요.
4. 진짜 지옥은 라이브 스트림이었습니다
VOD는 위 방식으로 잘 됐는데, 유튜브 라이브가 저를 며칠 잡았어요. 커밋 로그가 그대로 증거입니다:
stop re-buffering loop on live (loading→play→loading)— 보정하려고 seek를 걸면 다시 로딩, 로딩 끝나면 또 어긋나서 또 seek… 무한 루프break the loading→play→loading echo loop introduced with forceSeek— 위 문제 고치려고 넣은 forceSeek가 또 다른 에코 루프를 만듦translate live-edge intent across viewers' DVR windows— 라이브는 사람마다 DVR 윈도우(되감기 가능 구간)가 달라서, 한쪽의 "라이브 최신 지점"이 다른 쪽엔 존재하지도 않음
결론은 라이브에선 seekTo를 포기했어요. 라이브는 PLAY/PAUSE만 맞추고, 위치 동기화는 안 합니다. 각자의 DVR 윈도우가 다르니 억지로 맞추면 둘 다 깨져요. "완벽한 동기화"를 포기하는 게 정답일 때가 있더라고요.
배운 것
클라이언트끼리 맞추지 말고 공통 기준(서버)에 맞춰라. 2-body 문제를 1-body로 바꾸는 게 훨씬 쉽습니다.
보정은 "언제 할지"보다 "언제 안 할지"가 중요하다. 0.5초 무시 / 배속 보정이 seek 남발보다 체감이 훨씬 좋습니다.
완벽한 동기화에 집착하지 마라. 라이브는 PLAY/PAUSE만 맞추는 게 오히려 사용자 경험이 낫습니다.
혹시 같이보기나 실시간 협업 같은 거 만드시는 분 계시면 도움이 됐으면 합니다. 직접 써보고 싶으시면 → https://momentbrand.site (피드백 환영합니다 🙏)
유튜브 같이보기
알고리즘이 아닌 누군가 보낸 진짜 주파수. 익명 둘이 함께 보는 유튜브 동시 시청.