카테고리 없음

실시간 스트리밍 서비스 프로젝트

bonschicken 2025. 5. 20. 11:51
728x90

RTMP 서버 기반 실시간 스트리밍 시스템 구축기

실시간 방송, 어떻게 구현할까요?

요즘 라이브 스트리밍은 유튜브나 트위치만의 전유물이 아니잖아요? 팀 내부 프로젝트나 소규모 방송 플랫폼에서도 충분히 활용할 수 있어요. 그래서 이번에 우리가 만든 시스템은 복잡한 WebRTC 없이, 상대적으로 구성이 단순하면서도 확장성이 좋은 RTMP → HLS 기반 스트리밍 서버입니다.

목차

전체 아키텍처 개요

전체 흐름은 다음과 같아요. 송출자가 RTMP로 방송을 전송 → NGINX RTMP 서버가 수신 → FFmpeg로 실시간 변환 → NestJS 서버에서 스트리밍 및 채팅 제공 → 사용자 시청!

RTMP 서버 구성: NGINX로 방송 받기

nginx-rtmp-module을 사용해서 RTMP 스트림을 받았습니다. OBS에서 rtmp://서버주소/live 형식으로 전송하면 서버에서 받아줘요.

FFmpeg로 RTMP → HLS 변환하기

웹에서는 RTMP를 직접 재생하기 어렵기 때문에, HLS로 변환해주는 작업이 필요해요. 여기서 FFmpeg가 등장합니다.
변환 지연은 평균적으로 2~5초 이내라서, 사용자 경험에도 문제 없었어요.

  1. RTMP 스트림 수신
  2. FFmpeg로 m3u8/ts 생성
  3. 지정 폴더에 파일 저장
  4. NestJS에서 정적 파일로 제공

NestJS에서 HLS 스트리밍 제공

ServeStaticModule을 이용해 HLS 파일들을 정적으로 서빙합니다. 브라우저에서는 /videos/output.m3u8 같은 주소로 접근하면 됩니다.

어떻게 테스트했는지?

OBS로 직접 송출 테스트

  • Video.js + hls.js 조합으로 스트리밍 브라우저에서 재생 확인

  • Postman으로 API 호출 테스트

  • 실제로도 꽤 안정적으로 동작했고, 시청자 50명 이상 붙여도 문제 없었습니다

나중에 붙일만한 기능들

다중 비트레이트 출력 (480p, 720p 등)

  • CDN 연동 (CloudFront, Fastly 등)

  • 스트림 키 만료 기능

  • 녹화 파일 .mp4 자동 저장

  • 관리자 페이지 대시보드

구현 후 느낀 점과 개선 방향

처음엔 막막했지만, 하나하나 연결되면서 동작할 때 정말 짜릿했어요. 아직 부족한 부분도 있죠. 예를 들면:

  • 지연 최소화를 위한 세그먼트 설정
  • CDN 연동 고려 (CloudFront 등)
  • JWT 기반 인증 강화

자주 묻는 질문 (FAQ)

Q. 왜 WebRTC는 안 썼냐고?

WebRTC는 극저지연에 특화돼 있긴 하지만, 그만큼 구현 난이도가 꽤 높아요. NAT 우회, TURN 서버 세팅, 시그널링 등 손볼 게 많고, 일대다 방송을 제대로 하려면 추가적인 미디어 서버도 붙여야 하죠.

반면 HLS는 설정이 비교적 단순하면서도 웹 브라우저 호환성이 뛰어나고, 정적 파일 형태라 CDN이랑 연동하기도 쉬워요. 물론 5~10초 정도의 지연은 있지만, 대부분의 콘텐츠에는 이 정도 딜레이가 큰 문제가 되지 않더라고요.

Q. FFmpeg 실행 시 옵션은요?

  • 스트리밍 특성상 저지연 + 안정성 + HLS 호환을 고려해 최적화된 옵션을 사용합니다.
    ffmpeg -i rtmp://localhost/live/$name \
    -c:v libx264 -preset veryfast -tune zerolatency \
    -c:a aac -ar 44100 -ac 2 \
    -f hls \
    -hls_time 2 \
    -hls_list_size 5 \
    -hls_flags delete_segments+append_list \
    -hls_segment_filename /hls/$name/segment_%03d.ts \
    /hls/$name/index.m3u8
옵션 설명
-preset veryfast 인코딩 속도를 빠르게 하여 지연을 최소화합니다.
-tune zerolatency 실시간 스트리밍용으로 인코딩 버퍼링을 줄입니다.
-c:v libx264 비디오 코덱을 H.264(x264)로 설정합니다.
-c:a aac 오디오 코덱을 AAC로 설정합니다.
-ar 44100 오디오 샘플레이트를 44.1kHz로 설정합니다.
-ac 2 오디오 채널을 스테레오(2채널)로 설정합니다.
-f hls 출력 포맷을 HLS(HTTP Live Streaming)로 지정합니다.
-hls_time 2 각 .ts 세그먼트를 2초 간격으로 분할합니다.
-hls_list_size 5 m3u8 재생 목록에 최신 세그먼트 5개만 유지합니다.
-hls_flags delete_segments+append_list 오래된 세그먼트를 자동 삭제하고 목록을 누적합니다.
-hls_segment_filename 세그먼트 파일의 이름 형식을 지정합니다 (예: segment_001.ts).

📌 실시간성이 중요한 프로젝트일수록 preset은 빠르게, hls_time은 짧게 유지하는 게 핵심이에요.
단, 너무 짧게 자르면 오히려 버퍼링이 더 자주 발생할 수 있으니 2초~4초 선이 현실적입니다.

**Q. NestJS에서 정적 파일 보안은 어떻게 하나요?

NestJS의 기본 Static Serving은 무방비입니다. 예를 들어 아래처럼 설정할 경우:

app.useStaticAssets(join(__dirname, '..', 'public/hls'));

이렇게만 설정하면 누구든지 .m3u8 주소를 알기만 하면 직접 접근이 가능합니다. 즉, 인증 없이 영상 스트림을 가져갈 수 있게 되는 것이죠. 프록시 기반 전급제어로 정적 파일 보안합니다.

이를 막기 위해선 m3u8/ts 파일은 공개하지 않고, NestJS 내에서 /video/:streamKey 같은 경로로 프록시합니다.
이 프록시 경로에서 JWT나 세션 검사를 합니다.

@Get('/video/:key')
@UseGuards(AuthGuard)
streamVideo(@Param('key') key: string, @Res() res: Response) {
  const filePath = `/hls/${key}/index.m3u8`;
  res.sendFile(filePath, { root: join(__dirname, '..', 'public') });
}

Q. 시청자 증가 시 어떤 부분을 확장성 있나요?**

  • HLS는 정적 파일 기반이라서 .m3u8.ts 파일을 CDN(예: CloudFront, Cloudflare 등)에 올리기만 해도 글로벌 캐싱이 가능합니다.
  • 서버에 직접 요청이 들어오는 것이 아니라 캐시된 파일을 제공하므로, 수천 명이 동시에 시청해도 서버 부하가 거의 없습니다.
  • 정적 파일이기 때문에 로드 밸런싱 없이도 잘 분산됩니다.

💡 핵심 기술 요약

기술 정의 장점 단점
RTMP 송출자(OBS 등) → 서버 간 실시간 영상 전송에 사용되는 프로토콜 - 저지연 전송
- 송출 프로그램에서 널리 지원
- 브라우저 미지원
- 자체 보안 기능 없음
NGINX RTMP 모듈 NGINX에 RTMP 기능을 추가하여 스트림 수신/중계 역할 수행 - 가볍고 구성 간단
- FFmpeg 자동 실행 연동 가능
- 인증 기능 미비
- 대규모 확장에는 제약
FFmpeg RTMP 스트림을 HLS(m3u8+ts)로 변환하는 오픈소스 툴 - 고성능 인코딩
- 다양한 포맷 지원
- 자동화에 유리
- CPU 사용량 높음
- 설정이 복잡할 수 있음
HLS 세그먼트 기반 HTTP 스트리밍 방식으로 Apple이 개발, 웹/모바일 기본 지원 - 웹 호환성 뛰어남
- CDN과 쉽게 연동
- 적응형 화질 가능
- 5~10초 지연 발생
- 초저지연 방송엔 부적합

.

태그: RTMP 서버, nginx-rtmp-module, 실시간 방송, FFmpeg, HLS 스트리밍, m3u8, ts 파일, NestJS, WebSocket 채팅, 라이브 시스템 구축