
동아리 피드에 비디오 업로드 기능을 추가하는 과정에서, 예상보다 복잡한 기술적 도전들을 마주하게 되었습니다. 이번 글에서는 VOD 기능 개발 과정에서 겪은 문제들과, 이를 해결하기 위해 Server-Sent Events(SSE)를 도입하게 된 이유, 그리고 실제 구현 과정을 공유하고자 합니다.
기존에는 동아리 피드에 사진만 업로드할 수 있었지만, 보다 생생한 홍보 콘텐츠를 원하는 목소리가 꾸준히 있었습니다. 실제로 설문조사를 통해 일반 학우들뿐만 아니라 동아리 운영진들 역시 영상 콘텐츠를 활용한 홍보 방식에 대한 니즈가 높음을 확인할 수 있었습니다.
이에 따라 운영진(ADMIN)이 피드에 영상을 업로드할 수 있는 기능을 제공하여, 동아리 홍보 콘텐츠의 다양성을 확대하고자 했습니다. 이를 통해 추후 개발 예정인 동아리 지원 프로세스와 결합되어 지원률을 높이는데도 긍정적인 영향을 줄 것으로 기대했습니다.
하지만 영상 업로드 기능을 구현하려고 보니, 단순한 이미지 업로드와는 다른 복잡한 문제들이 있었습니다. 대표적인 문제는 다음과 같았습니다.

또, 동영상을 S3에 업로드하는 데 걸리는 평균 시간을 측정해보았는데요. (네트워크 지연이 없다는 가정하에 측정했습니다.) 3분 영상의 경우 해상도에 따라 100-300MB 정도의 용량을 가지므로, 사용자는 최소 20초에서 최대 1분까지 기다려야 합니다. 이런 긴 처리 시간 동안 진행 상황을 알 수 없다면, 업로드가 실패했다고 오해하거나 페이지를 떠날 가능성이 높아집니다.
이처럼 동영상 업로드는 단순히 파일을 서버에 저장하는 수준을 넘어, 업로드 → 변환 → 저장 → 스트리밍 준비까지의 전체 파이프라인을 고려해야 했고, 각 단계마다 사용자에게 적절한 피드백을 제공하는 것이 핵심 과제였습니다.
띵동 서버팀은 다양한 포맷 지원과 HLS 변환 문제를 해결하기 위해 AWS MediaConverter를 도입했습니다.
AWS MediaConverter는 다양한 입력 형식의 미디어 파일을 웹에서 재생 가능한 형식으로 변환해주는 서비스로, HLS나 MPEG-DASH와 같은 스트리밍 포맷을 지원하며, 변환 결과물을 Amazon S3 버킷에 저장해줍니다.
하지만 MediaConverter는 비동기적으로 작동하기 때문에 변환이 완료되기까지 시간이 소요되며, 변환 결과를 실시간으로 확인하기 어렵다는 단점이 있습니다.
이러한 문제를 해결하기 위해 업로드 상태를 실시간으로 알릴 수 있는 시스템이 필요하다고 판단했고, 이를 바탕으로 기능의 범위(Goals)와 제외 범위(Non-Goals)를 명확히 정의하여 개발의 방향성을 설정했습니다.
앞서 언급했듯, 영상 업로드는 단순 저장을 넘어 변환과 스트리밍 준비까지 포함된 다단계 비동기 처리가 필요합니다. 특히 AWS MediaConverter는 변환이 완료될 때까지 시간이 걸리며, 이 과정을 사용자가 직접 확인할 수 없습니다.
사용자는 이 기다림 동안 어떤 일이 진행되고 있는지 실시간 피드백을 받을 수 있어야 하고, 서버는 업로드 성공/실패 여부를 즉시 사용자에게 알려야 합니다.
이처럼 서버에서 발생한 상태 변화를 즉시 클라이언트에 전달해야 했기 때문에, 비연결성을 특징(요청과 응답이 한 번 이루어진 후에는 연결이 종료)으로 갖고 있는 HTTP는 한계가 있었습니다. 따라서 서버에서 클라이언트로 업로드 완료 여부를 실시간으로 알리기 위해서는 별도의 통신 방식이 필요했습니다.
이에 따라, 지속적인 연결을 통해 서버에서 클라이언트로 실시간 알림을 푸시할 수 있는 통신 방식이 필요했고, 이를 해결하기 위해 어떤 기술을 사용할지 고민하게 되었습니다.
아래는 이러한 요구사항을 해결하기 위해 고려했던 주요 통신 방식들의 특징과 차이점을 정리한 것입니다.

Polling은 클라이언트가 일정 시간 간격으로 서버에 요청을 보내 변화가 있는지 상태를 확인하는 방식입니다. 예를 들어, 3초에 한 번씩 서버에 “업로드 완료됐나요?“라고 계속 묻는 구조입니다.

WebSocket은 클라이언트와 서버 사이에 하나의 TCP 연결을 지속적으로 유지하면서 양방향 통신이 가능한 프로토콜입니다. 연결이 한 번 수립되면, 서버와 클라이언트는 서로 자유롭게 메시지를 주고받을 수 있습니다.

SSE는 클라이언트가 서버에 연결을 맺고, 서버가 필요한 정보를 단방향으로 클라이언트에 푸시하는 방식입니다. HTTP 기반으로 동작하며, 클라이언트에서 EventSource 객체를 사용해 쉽게 구현할 수 있습니다.
text/event-stream 형식으로 응답만 주면 됩니다.동영상 업로드 기능은 다음과 같은 시나리오를 갖고 있습니다.
이 상황에서 필요한 것은 다음과 같은 요구사항을 만족하는 통신 방식입니다.
즉, 양방향 통신(WebSocket)은 오버엔지니어링이며, 단방향 푸시 기반의 실시간 전송만 필요한 상황이었습니다. 이러한 요구사항을 가장 효율적으로 충족하는 기술이 바아로 SSE였습니다.
결론적으로 이번 기능에서는 복잡한 설정 없이, 클라이언트에서 서버 상태를 실시간으로 전달할 수 있는 방식이 필요했고, SSE는 기술적 요구사항를 고려했을 때 가장 합리적인 선택이었습니다.
EventSource는 브라우저에서 SSE를 사용할 때 쓰는 JavaScript API입니다.
SSE는 서버로부터 데이터를 지속적으로 받아야 하는데, 이때 브라우저가 서버와 스트림을 유지하기 위해 사용하는 객체가 EventSource입니다.
const eventSource = new EventSource(url)
서버와의 SSE 연결을 생성합니다. 서버는 해당 url을 통해 인코딩 진행 상태 또는 완료 여부를 스트리밍합니다.
eventSource.addEventListener('connect', (event) => {
toast.loading('비디오 업로드 중입니다.', { id: toastId });
// ..
});
connect 는 서버에서 보내는 커스텀 이벤트 이름입니다. 초기 연결에 성공했을 때 해당 이벤트를 클라이언트로 보낼 수 있습니다. 해당 이벤트를 통해 클라이언트에서는 최초 연결 상태를 확인하거나 초기 메시지를 받을 수 있습니다.
eventSource.addEventListener('sse', (event) => {
const parsedData = JSON.parse(event.data);
// 변환 완료 처리
if (parsedData.data.convertJobStatus === 'COMPLETE') {
toast.success('피드가 생성되었어요.', { id: toastId });
// ..
eventSource.close();
}
// 변환 실패 처리
if (parsedData.data.convertJobStatus === 'ERROR') {
toast.success('비디오 업로드 중 문제가 발생했습니다.', {
id: toastId,
duration: 5000,
});
// ..
eventSource.close();
}
});
sse 역시 서버에서 정의한 커스텀 이벤트 이름입니다. event.data는 문자열 형태로 전달되므로, JSON.parse()를 사용해 객체로 변환해야 합니다. 변환이 완료되거나 오류가 발생하면 연결을 종료하기 위해 eventSource.close()를 호출합니다.
eventSource.onerror = () => {
toast( `동영상 처리에 시간이 조금 더 필요합니다.\n 곧 완료되니 잠시만 기다려 주세요!`);
// ..
};
SSE 연결 중 에러가 발생하면 자동으로 호출됩니다. 예외 상황에 대해 사용자에게 알릴 수 있습니다.
SSE(Server-Sent Events)를 처음 구현하면서 가장 헷갈렸던 부분 중 하나가 바로 이벤트 이름과 데이터 포맷이었어요.
친절한 백엔드 팀원 덕분에 서버에서 어떤 이벤트 이름으로 어떤 형식의 데이터를 보내는지 명확히 전달받았기 때문에, 훨씬 수월하게 클라이언트 로직을 구현할 수 있었습니다.. 😊

위 사진에서처럼 event는 서버에서 설정한 커스텀 이벤트 이름이며, data는 전달된 실제 상태 정보(JSON)입니다.
결과적으로 15초 가까이 걸리는 동영상 처리 시간 동안에도 사용자는 자유롭게 서비스를 이용할 수 있으면서, 동시에 처리 결과를 놓치지 않고 실시간으로 확인할 수 있는 매끄러운 사용자 경험을 제공할 수 있게 되었습니다.

SSE 구현을 통해 다양한 문제를 마주하고 하나하나 해결해 나가는 과정은 쉽지만은 않았습니다. 하지만 실제로 제가 다양한 서비스들에서 비디오를 업로드하면서 느꼈던 답답함과 불편함을, 제 서비스를 사용하는 사용자들은 겪지 않기를 바라는 마음으로 개발에 임했기에 더 몰입할 수 있었던 것 같아요.
기술은 결국 사람을 위한 것이라는 점을 다시금 느꼈고, 앞으로도 사용자 입장에서 고민하며 더 나은 경험을 만들기 위해 노력하고 싶습니다. 😊
구현한지는 5달이 지났지만, 이제야 글을 쓰네요. 반성합니다..