ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] React 웹 앱 Firebase에 호스팅하기
    Javascript & TypeScript 2021. 2. 4. 23:17
    반응형

    firebase + React를 이용해 스트리밍 음악 웹앱 개발 가능여부를 체크하기위한 프로젝트가 어느덧 막바지 단계에 들어왔다. 이전 포스트를 통해 진행했던 React 테스트 웹 페이지를 firebase에 호스팅 해 볼 차례이다.

     

    지난시간 글은 아래 링크로 확인가능하다.

     

    https://ddochea.tistory.com/87

     

    [javascript] blob으로 저장된 미디어파일 재생처리

    지난 시간에 firebase storage에 저장된 음악파일을 blob 형식으로 받아 indexdDB에 저장하는 기능을 구현했었다. 해당내용은 아래 포스트를 통해 확인할 수 있다. https://ddochea.tistory.com/86 [javascript]..

    ddochea.tistory.com

    이번시간엔 호스팅을 통해 다음과 같은 내용을 확인해보고자 한다.

     

    - 모바일 웹 브라우저에서 접속 후 음악 다운로드

    - 모바일 웹 브라우저에서 음악 재생.

    - 2~3일간 실사용 후 트래픽 확인

    Firebase의 Storage 서비스 트래픽 모니터에서 트래픽이 0 Byte에 수렴해야 한다.

    웹페이지에 대한 트래픽은 아직 캐싱처리 방법을 몰라 0에 수렴시키긴 어려울 듯 하다. PWA의 request 캐싱을 염두하고 있었는데  Firebase의 Hosting 서비스가 Static Page 호스팅에 국한된건 줄 몰랐다. 아마 방법은 있을 듯 한데 정식 개발때 살펴봐야겠다.

     

    호스팅 기능을 이용하기위해 firebase가 안내하는 방법에 따라 진행했다.

     

    1. 시작하기 선택

    2. CLI 설치

    3. 로그인

    npm 을 통해 firebase CLI을 설치 한 후 로그인을 진행하는 단계이다. PC의 콘솔창에서 firebase login을 입력한다.

    오류보고서 제공 동의 여부를 묻는데 어느쪽을 선택하건 상관없다. 엔터를 누르면 브라우저가 열리면서 Google 계정으로 로그인페이지가 표시된다.

    성공하면 아래와 같은 페이지가 표시된다.

    4. firebase init 명령어를 통해 프로젝트 폴더 생성

    프로젝트를 담을 폴더를 하나 생성한 뒤 해당 위치로 이동하고, firebase init 명령어를 입력한다. 기존에 만든 테스트 프로젝트 폴더에 명령어를 날렸다가 파일을 날려먹는 참사(?)가 발생할 듯 하여 새로 폴더를 생성했다.

    다음 단계에서 Hosting, Storage를 선택했다. 그런데 어차피 기존 테스트 소스를 덮어씌울 예정이라 딱히 의미있을 것 같진 않다.

    이미 firebase 에 프로젝트를 생성해놨기 때문에 "Use an existing project"를 선택했다.

    선택하면 Firebase 콘솔앱에 표시되는 프로젝트명이 리스트로 표시된다. 유관 프로젝트를 선택하고 다음단계로 넘어가면 호스팅 세팅창이 표시된다. public 폴더를 생성하고 해당 폴더를 사용하겠냐는 질문인데 npx create-react-app 명령어로 생성한 React 프로젝트가 기본적으로 빌드결과물을 "build" 폴더에 생성하기 때문에 build로 바꿔줘야 한다.

     

    두번째 질문은 SPA 페이지의 url 규칙에 따라 url이 재정의되는지를 묻는 여부인 듯 한데 별상관없으로 Y/N 아무거나 입력하고 넘어간다.

    현재 프로젝트는 테스트용도이므로 Github와는 무관하다.  N으로 간다.

    호스팅에 대한 설정은 끝나고 Storage에 대한 설정이다. 해당 rules을 통해 인증된 사용자에 대한 접근 허용/차단을 설정하는데 그냥 엔터 누르고 넘어간다.

    다되었다. 겁나기네

    5. 소스 붙여넣기

    기존에 테스트했던 React 프로젝트 내용을 통째로 복사하여 붙여넣었다.

     .firebaserc, firebase.json, storage.rules 파일은 앞 과정의 프로젝트 설정과정에 의해 생긴 파일이다. build 폴더와 내부 내용도 생성이 되었으나 React 소스로 덮어씌웠기 때문에 의미는 없다.

     

    6. 앱 플랫폼 생성

    메뉴 상단 프로젝트 개요 옆에 설정아이콘이 보일 것이다. 해당 아이콘을 선택 한 후 "내 앱" 항목에 웹 플랫폼을 선택한다.

    선택하면 아래 화면이 나타난다. 호스팅설정은 체크안해도 된다.

     

    앱 등록 버튼을 누르면 다음 단계가 나오는데 해당 스크립트를 다 복사할 필요는 없고, firebaseConfig 객체 데이터만 복사해서 React 프로젝트 내 사용한 firebase.initializeApp() 함수의 매개변수로 붙여넣으면 된다.

    7. Storage 에 음악파일 업로드 및 규칙설정

    React 앱이 음악다운로드가 될 수 있도록 아래와 같이 규칙을 설정해준다. 테스트를 위한 작업이기 때문에 read, write 모두 조건없이 허용시켰는데 실제 이렇게 만들면 안된다.

    * 음악파일은 별도의 하위 디렉토리 생성없이 root에 위치시켰다.

    *  storage.rules 파일에서 수정하도록 하자. 여기서 수정해도 deploy 명령시 프로젝트의 rules 파일 내용으로 적용된다.

    8. CORS 설정

    Storage URL과 Host URL이 달라 CORS 설정이 필요하다. 이는 firebase에선 따로 설정할 수 없다.

    해당 설정방법은 아래 링크의 "CORS 구성" 부분을 참조해주기 바란다.

     

    웹에서 파일 다운로드  |  Firebase (google.com)

     

    웹에서 파일 다운로드  |  Firebase

    Cloud Storage를 사용하면 Firebase가 제공하고 관리하는 Google Cloud Storage 버킷의 파일을 빠르고 손쉽게 다운로드할 수 있습니다. 참고: 기본적으로 Cloud Storage 버킷에서 파일을 다운로드하려면 Firebase

    firebase.google.com

    9. React 프로젝트 소스 수정 및 빌드

    음악파일 3개를 넣고 소스를 아래와 같이 수정했다.

    import React, { useEffect, useRef, useState } from "react";
    import firebase from "firebase/app";
    import localforage from "localforage";
    import "firebase/storage";
    
    import logo from './logo.svg';
    import './App.css';
    
    firebase.initializeApp({
      //firebase config 정보입력위치
    })
    
    
    localforage.config({
      storeName : "media" // 테이블명?
    })
    
    function App() {
      const [srcValue, setSrcValue] = useState('');
      const [totalTime, setTotalTime] = useState(0);
      const [currentTime, setCurrentTime] = useState(0);
      
      const ref = useRef(null);
      
      function play(idx) {
        localforage.key(idx).then((key) => {
          return localforage.getItem(key);
        }).then(item => {
          console.log(item);
          const url = URL.createObjectURL(item);
          setSrcValue(url);
        })
      }
    
      // 동영상 시간 업데이트 함수 참조(https://ideveloper2.dev/blog/2020-06-27--video-tag-react-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-custom-%ED%95%98%EA%B8%B0/)
      const addTimeUpdate = () => {
        const observedVideoElement = ref && ref.current;
        if (observedVideoElement) {
          observedVideoElement.addEventListener("timeupdate", function() {
            const currentTime = observedVideoElement.currentTime;
            const totalTime = observedVideoElement.duration;
            setCurrentTime(currentTime);
            setTotalTime(totalTime);
            if(currentTime >= totalTime) {
              const key = Math.floor(Math.random() * 3); // 랜덤 재생(0~2)
              play(key);
            }
          });
        }
      };
    
      useEffect(() => {
        addTimeUpdate();
      }, []);
    
      function getMusicFile() {
        const storage = firebase.storage();
        var storageRef = storage.ref();
        storageRef.listAll().then(result => {
          console.log(result);
          result.items.forEach(item => {
            item.getDownloadURL().then(url => {
              const xhr = new XMLHttpRequest();
              xhr.responseType = 'blob';
              xhr.onload = (event) => {
                const blob = xhr.response;
                localforage.setItem(item.name, blob);
              }
              xhr.open('GET', url);
              xhr.send();
            })
          });
        });
      }
      
      return (
        <div className="App">
          <header className="App-header">
            <audio src={srcValue} ref={ref} controls={true} autoPlay={true} >
            </audio>
            <button onClick={getMusicFile}>getMusicFile</button>
            <p></p>
            <button onClick={_ => play(0)}>PlaySavedFile</button>
            <div>
            {currentTime} / {totalTime}
            </div>
          </header>
        </div>
      );
    }
    
    export default App;
    

     

    화면은 아래와 같다. 테스트화면이기 때문에 멋 따윈 없다.

     

    getMusicFile 버튼을 누르면 firebase Storage 에 저장된 음악파일을 모두 받아 indexed DB에 저장시킨다. PlaySavedFile 버튼을 누르면 indexed DB에 저장된 음악 중 key 값이 0인 음악이 재생되고, 이후는 0~2 사이의 랜덤 값에 의해 재생된다.

    * 이전 시간엔 vedio 태그를 이용했는데 실제 모바일기기에선 background로 재생되지 않고, 다음 음악으로 진행되는 로직이 제대로 동작하지 않는 문제를 확인하게 되었다. 그래서 audio 태그로 변경했다.

     

    npm run build 명령어로 static page를 생성한다.

    10. Deploy 명령어로 배포

    firebase deploy 명령어로 배포한다. 이상이 없다면 Deploy complete! 를 확인할 수 있다.

     

    11. 모바일기기로 접속 확인

    모바일기기에서 호스팅된 기본 URL로 접속한 뒤, getMusicFile을 눌러 음악을 다운로드 받는다. 그리고 몇 초 후에 PlaySavedFile 버튼을 눌러보자 indexed 에 저장된 순서 중순서상 1번째(key0)로 저장된 파일이 재생된다.

    재생이 끝나면 다음곡이 랜덤하게 선택되는데 똑같은 곡이 나올 수 있다.

    이것으로 기술구현가능여부를 확인해보았다.

     

    [2021.02.05 내용추가]

    글 작성시점이 02.04인데 스크린샷마다 민감한 정보가 포함되어있어 편집하느라 공개시점이 하루 늦었다. 배포 이후 출퇴근길에 실제 사용해보니 원하는 결과대로 재생되어 나름 만족스럽다.

    (덤으로 똑같은 음악을 연속3번 듣고 나서야 "셔플재생 != 랜덤재생" 임을 깨닫게 되었다.)

     

    반응형

    댓글

Designed by Tistory.