본문으로 건너뛰기

학습이력 재생, 꼭 무거운 비디오로 바꿔야 할까?

2026년 8월 30일
7 views
MathCanvas
주니어 개발
학습기록
HLS

들어가며

과정중심평가 이야기가 나왔습니다. 학생이 답을 맞혔는지만 보는 게 아니라, 거기까지 어떻게 갔는지를 보고 싶다는 거였습니다. 어디서 망설였고, 무엇을 지웠다 다시 그렸는지 같은 것들이요.

그래서 "화면을 통째로 비디오로 녹화하자"는 말이 나왔습니다. 저는 좀 걸렸습니다. 비디오는 일단 무겁습니다. 답안 하나가 지금의 수천 배로 불어나고, 픽셀 덩어리라 지금은 되는 자동 채점이나 검색이 막히거든요. 꼭 비디오여야 할까? 이 질문을 붙들고 "그래도 비디오로 간다면 긴 영상은 어떻게 재생하지?"까지 따라가다가 HLS를 처음 만났습니다.

저는 스트리밍 프로토콜을 다뤄본 적이 없습니다. 그래서 이 글은 "HLS란 이런 것이다" 하는 설명이 아니라, 낯선 걸 만난 제가 이미 아는 것에 자꾸 대보면서 더듬더듬 이해한 기록입니다.

내가 서 있던 자리, 지금 우리가 저장하는 방식

새 개념을 이해하려면 발 디딜 데가 필요했습니다. 저한테 그건 지금 우리 서비스의 저장 방식이었습니다.

우리는 학생 조작을 이벤트 로그가 아니라 커맨드와 전후 상태 diff로 저장합니다. 조작이 하나 끝날 때마다 { type, data: { before, after }, timestamp } 노드가 쌓이고, before와 after는 영향받은 요소의 상태를 통째로 복사한 거예요. 저장하기 직전엔 델타와 gzip으로 눌러서, 무거운 답안도 실측 200배 넘게 줄어듭니다. 3,755KB짜리가 9.3KB가 되는 식으로요. (이 압축 이야기는 따로 글로 정리해 뒀습니다: 완성된 요리 말고 레시피만 보내자)

여기서 제가 몸으로 익힌 감각이 두 개 있습니다. 나중에 HLS를 보면서 계속 이 둘에 대보게 됩니다.

하나는 "통짜로 들고 있지 말고 바뀐 조각만"입니다. 델타 압축이 그렇죠. 전체 스냅샷이 아니라 달라진 부분만 저장합니다. 다른 하나는 "긴 흐름을 시간 붙은 조각으로 흘려보낸다"입니다. 발표자 미러링에 쓰는 스트림이 그런데, 긴 조작 타임라인을 waypoint(시간 타임스탬프가 붙은 지점)로 잘게 나눠 흘려보냅니다.

이 두 감각을 가진 채로 HLS를 봤습니다.

처음엔 이렇게 오해했습니다

HLS를 찾아보기 전에, 저는 "왜 통짜 mp4 파일 하나면 안 되지?"에 이렇게 답했습니다.

"통짜 파일은 다 받아질 때까지 특정 지점부터 못 보니까."

그런데 틀린 이해였습니다. 찾아보니 mp4도 HTTP Range 요청에 faststart(인덱스를 파일 앞으로 빼두는 것)를 쓰면 전부 안 받고도 탐색이 됩니다. 제가 "안 된다"고 생각한 게 사실은 "조건이 맞으면 된다"였던 거죠.

이걸 알고 나서야 진짜 질문이 생겼습니다. 그럼 통짜 파일이 진짜로 불리한 건 어디지?

대보면서 하나씩 짚은, 통짜가 불리한 이유

찾은 걸 제 말로 정리하니 네 가지였는데, 하나씩 볼 때마다 "아 이건 내가 아는 그거네" 싶은 순간이 있었습니다.

첫째, 탐색이 조건부입니다. mp4 인덱스가 파일 끝에 있으면 그걸 먼저 받아야 하고, 중간으로 점프하려면 서버가 Range를 지원해야 하고 그것도 앞쪽 키프레임부터 받아야 합니다. 되긴 되는데 전제가 여러 개죠.

둘째, 화질이 하나로 고정됩니다. 이게 가장 크게 와닿았어요. 통짜는 한 화질이라, 느린 학생을 위해 전체를 저화질로 깔든가 아니면 고화질로 깔고 버퍼링을 견디게 하든가 둘 중 하나입니다. HLS는 화질을 여러 벌 만들어 두고 플레이어가 대역폭을 재서 중간에 갈아탄다는데, 이 대목에서 델타 압축 때 하던 생각이 겹쳤습니다. 상황에 맞게 필요한 만큼만.

셋째, 시작 지연이나 버퍼링 단위가 큽니다. 통짜는 한 번 끊기면 큰 버퍼를 다시 채워야 합니다.

넷째, 캐싱 단위가 거칩니다. 100MB 통짜는 CDN에 하나로 얹히는데, 작은 조각이면 실제로 본 부분만 캐시된다고 합니다.

그래서 HLS가 하는 일을, 이해한 만큼

정리하면 HLS는 원본을 여러 화질과 여러 세그먼트로 쪼개는 것 같습니다. 세그먼트는 6초 안팎의, 키프레임으로 시작하는 자립적인 조각이고요. m3u8이라는 목록 파일이 화질 종류와 세그먼트 위치를 담고, 플레이어는 그걸 읽어서 대역폭에 맞는 화질의 세그먼트를 순서대로 받아 틉니다.

탐색은 "몇 초 지점이 몇 번째 세그먼트인지" 계산해서 그 조각만 받는 겁니다. 세그먼트마다 키프레임으로 시작하니까 아무 데서나 재생을 시작할 수 있고요. 이게 부분 재생의 원리라고 이해했습니다.

여기서 또 아는 것에 대보게 됐습니다. 우리 미러링 스트림이 긴 조작 타임라인을 시간 붙은 조각으로 흘려보내는 것과, HLS가 긴 영상을 시간 경계로 잘라 필요한 세그먼트만 받는 것. 똑같진 않아도 "긴 걸 통짜로 주지 말고 경계로 나눠 필요한 만큼만"이라는 발상은 닮아 있었어요. 낯선 개념인데 아주 처음은 아닌 느낌이 든 게 이 지점이었습니다.

세그먼트를 왜 하필 6초쯤 두는지도 찾아봤습니다. 너무 짧으면 요청이 잦아지고 무거운 키프레임이 자주 껴서 낭비고, 너무 길면 화질 전환이나 탐색이 굼떠서 2~10초가 균형이라고 하네요. 이건 아직 "그렇다더라" 수준으로만 압니다.

그런데 이번 건엔 안 썼습니다

여기까지 이해하고 원래 질문으로 돌아왔습니다. 우리가 정말 HLS가 필요한가?

앞에서 비디오 자체를 접었으니까요. 비디오로 가면 답안 하나가 10KB 안팎에서 50~100MB로 늘어납니다. 자릿수가 서너 개 뛰는 거죠. 무엇보다 지금은 요소 단위라 되던 자동 채점, 검색, 썸네일이 전부 불가능해집니다. 과정을 "분석"하겠다는 건데 분석이 안 되는 포맷으로 저장하는 셈이라, 목적하고 수단이 어긋났습니다.

HLS의 이득인 부분 재생이나 적응형, 캐싱은 미디어가 길고 많을수록 커지는데 학생 답안은 대개 몇 분입니다. 그래서 HLS는 원리는 이해했지만 이 문제에 쓸 도구는 아니었습니다. 배운 걸 안 쓰기로 하는 것도 하나의 결론이라고 생각해요.

지금 모델이 못 담는 건 딱 세 가지입니다. 드래그 궤적, 펜 속도와 멈춤, 그리고 실제로 기다린 시간. 이건 비디오까지 안 가도 지금 요소 모델에 시간 붙은 waypoint를 얹으면 훨씬 싸게 얻을 수 있을 것 같습니다. 마침 미러링 스트림에 그 조각이 이미 있고요. 이 방향은 다음 글에서 더 파보려 합니다.

남는 것

HLS는 이번에 처음 배웠고, 아직 문서를 따라 읽으며 "그렇다더라"인 부분이 많습니다. 코드에 붙여본 적도 없고요. 그래도 낯선 걸 통째로 삼키는 대신 제가 아는 것, 그러니까 델타 압축이나 미러링 스트림, before/after 모델에 하나씩 대보니까 "왜 이런 구조가 있고 언제 쓰는지"까지는 손에 잡혔습니다.