Streaming 서비스


이번강좌에서는 node.js 의 강점이랄 수 있는 대용량 streaming 처리에 대해 알아보겠습니다. node.js 는 이벤트 Loop 기반의 비동 처리를 지원하기 때문에 대용량 파일을 구간별로 작게 나누어서 처리 하는 작업에 강점을 가지고 있습니다.

가. 일반적인 readFile( ) 의 문제점

아래의 소스코드를 한번보겠습니다. fs.readFile( ) 을 이용해서 'movie.mp4' 라는 바이너리 파일을 서비스 하는 코드입니다. 사이즈가 아주 큰(적어도 100MB 이상되는..) 동영상 파일을 서비스 한다고 생각해보죠. 아래 코드는 서버에서 파일을 다 읽은 후에 파일읽기가 완료가 되면 클라이언트로 한번에 전송하게 됩니다. 그럼 어떻게 될까요? 클라이언트에는 서버로부터 데이터를 전송받을 때 까지 대기를 하게 됩니다. 용량이 커질수록 클라이언트의 대기시간도 길어질 뿐더러 요청이 많아질수록 서버의 효율이 떨어지게 될겁니다.

fs.readFile('movie.mp4', function(error, data) {
    request.end(data);
});

이런 단점을 보완하기 위해 데이터를 전체를 다 읽거나 쓰지 않아도 중간에 처리할 수 있도록 해주는 것이 stream 입니다. 현재 인터넷에서 서비스 되고 있거나 오픈소스로 지원하는 streaming 라이브러리가 많이 있지만 node.js 는 아주 적은량의 코딩만으로 이런 streaming 서비스를 쉽게 구현할 수 있습니다.

나. 예제 실행을 위한 동영상 파일 다운로드

먼저 용량이 큰 동영상 파일을 하나 준비해야 합니다. 유튜브 같은 곳을 동영상 사이트를 통해서 어렵지 않게 구하실 수 있으리라 생각됩니다.

프로젝트 폴더아래에 movie 라는 폴더를 새로 만들고, 해당 동영상 파일을 복사해둡니다. 임시로 800MB 용량의 mp4 파일을 다운로드 받아서 movie 폴더에 big.mp4 로 저장해두었습니다.

  • movie/big.mp4

다. 동영상 실행을 위한 플레이어 HTML

그리고 웹브라우저에서 동영상 파일을 재생하기 위해 HTML5의 멀티미디어 재생 태그인 <video>를 사용해서 저장해둔 big.mp4 파일을 실행하는 형태로 소스코드를 작성해 보겠습니다. 먼저 각 리소스를 분리하기 위해 html 폴더를 하나 생성합니다. 그리고 html 폴더 아래에 movie_player.html 파일을 만들고 아래와 같이 입력합니다.

  • html/movie_player.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Movie Player</title>
</head>
<body>
    <h1>Movie Player</h1>
    <video width="320" height="240" controls>
        <source src="movie/big.mp4" type="video/mp4">
        Your browser does not support the video tag.
    </video>
</body>
</html>

위의 html 파일에서 video 폴더에 있는 big.mp4 파일을 실행해 줄것입니다.

라. 동영상 파일을 Streaming 서비스 하기위한 소스코드

이제 streaming_movie.js 파일을 생성하고 아래와 같이 입력합니다.

  • streaming_movie.js
var http = require('http');
var url = require('url');
var fs = require('fs');

var server = http.createServer(function(request,response){

  var parsedUrl = url.parse(request.url);
  var resource = parsedUrl.pathname;
  console.log('resource='+resource);

  var resourcePath = '.'+resource;
  console.log('resourcePath='+resourcePath);

  // html 페이지 요청이 들어왔을 경우는 텍스트 파일 처리
  if(resource.indexOf('/html/') == 0){
    fs.readFile(resourcePath, 'utf-8', function(error, data) {
      if(error){
        response.writeHead(500, {'Content-Type':'text/html'});
        response.end('500 Internal Server '+error);
      }else{
        response.writeHead(200, {'Content-Type':'text/html'});
        response.end(data);
      }
    });

  }else if(resource.indexOf('/movie/') == 0){
    // 1. stream 생성
    var stream = fs.createReadStream(resourcePath);
    // 2. 잘게 쪼개진 stream 이 몇번 전송되는지 확인하기 위한 count
    var count = 0;
    // 3. 잘게 쪼개진 data를 전송할 수 있으면 data 이벤트 발생 
    stream.on('data', function(data) {
      count = count + 1;
      console.log('data count='+count);
      // 3.1. data 이벤트가 발생되면 해당 data를 클라이언트로 전송
      response.write(data);
    });

    // 4. 데이터 전송이 완료되면 end 이벤트 발생
    stream.on('end', function () {
      console.log('end streaming');
      // 4.1. 클라이언트에 전송완료를 알림
      response.end();
    });

    // 5. 스트림도중 에러 발생시 error 이벤트 발생
    stream.on('error', function(err) {
      console.log(err);
      // 5.2. 클라이언트로 에러메시지를 전달하고 전송완료
      response.end('500 Internal Server '+err);
    });
  }else{
    response.writeHead(404, {'Content-Type':'text/html'});
    response.end('404 Page Not Found');
  }

});

server.listen(80, function(){
    console.log('Server is running...');
});

마. 폴더 및 파일구조

아래와 같은 구조의 폴더와 파일이 생성되어 있어야 합니다.

  • 프로젝트ROOT/streaming_movie.js
  • 프로젝트ROOT/html/movie_player.html
  • 프로젝트ROOT/movie/big.mp4

바. 실행 및 브라우저를 통한 확인

node streaming_movie 를 실행한 후에 브라우저에서 http://localhost/html/movie\_player.html 을 호출해봅니다. 브라우저에 플레이어가 출력되는 데 플레이 버튼을 클릭하면 정상적으로 실행되는 것을 확인할 수 있습니다.

그리고 CMD 창을 확인하면 아래와 같이 로그가 대량으로 발생해 있는것을 확인할 수 있습니다. 데이터를 잘게 나누어서 전송가능한 양이 되면 stream의 data 이벤트를 발생하기 때문에 800MB 용량의 파일은 로그에서 보듯이 10,000개 이상의 파일로 잘게 나누어서 전송을 하게 되는 것입니다.

results matching ""

    No results matching ""