vue

 

 

 

소스 :

https://github.com/braverokmc79/pwa-ex-11

 

 

 

 

 

 

기본적으로  nodejs 설치 및 vue 와 Vue CLI  설치 되어 있어야 한다.

nodejs 설치

https://nodejs.org/ko/download/

 

nodejs 설치 후

vue 설치

npm install vue

명령을 사용하여 설치한 버전 번호를 확인합니다(vue --version).

 

 

Vue CLI를 설치하려면 npm을 사용합니다. 업그레이드하려면 -g 플래그를 사용하여 전역으로 설치해야 합니다(vue upgrade --next).

 

1 npm install -g @vue/cli

 

 

 

 

프로젝트 결과 화면

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1. vue add vuetify  설치

 

 

2.  npm install firebase   vuefire   설치

 

 

3. 매니페스트 작성

 

public/manifest.json

{
  "name": "카메라 갤러리",
  "short_name": "카메라 갤러리",
  "icons": [
    {
      "src": "./img/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "./img/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "./index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#ffffff"
}

 

 

 

 

 

4. 파이어베이스 프로젝트 만들기

 

https://console.firebase.google.com/

 

1) firebase.google.com 에 접속해서 pwa-camera 라는 이름으로 프로젝트 만들기

2) 파이어베이스 프로젝트 설정 화면에서 웹앱에서 파이어베이스 추가하기 (닉네임 pwa-camera 로 등록)

3) 파이어베이스 SDK 추가에서 databaseURL, storageBucket 값을 복사해서 기록해 두기

4) Realtime Database 만들기 -> 테스트 모드로 시작

 

 

 

 

 

 

5. 파이어베이스 스토리지  및  DB 연동 파일 생성하기

 

1) src/datasources/firebase.js

// 파이어베이스 앱 객체 모듈 가져오기
import firebase from 'firebase/compat/app'
// 파이어베이스 패키지 모듈 가져오기
import 'firebase/compat/database';
import 'firebase/compat/storage';

// 파이어베이스 DB를 초기화
const oFirebase = firebase.initializeApp({
  // 파이어베이스 콘솔에서 복사하여 붙여넣기
  databaseURL: "https://pwa-camera.firebaseio.com",
  storageBucket: "pwa-camera.appspot.com",
});
// 파이어베이스 DB객체 연결 
const oDB = oFirebase.database();

// 파이어베이스 DB객체 중에서 pictures 항목을 다른 곳에서 사용하도록 공개
export const oPicturesinDB = oDB.ref('pictures');
// 파이어베이스 스토리지 객체 공개
export const oStorage = oFirebase.storage();

 

 

2)src/main.js 에 다음을 추가

//뷰파이어 노드 모듈 가져와서 Vue에 연결
import { rtdbPlugin } from "vuefire";
Vue.use(rtdbPlugin);

 

import Vue from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
import router from "./router";
import vuetify from "./plugins/vuetify";

//뷰파이어 노드 모듈 가져와서 Vue에 연결
import { rtdbPlugin } from "vuefire";
Vue.use(rtdbPlugin);

Vue.config.productionTip = false;

new Vue({
  router,
  vuetify,
  render: (h) => h(App),
}).$mount("#app");

 

 

3) DB 연동 테스트 확인

App.vue 파일에 firebase.js 파일 임포트 후 확인

<script>
 // 파이어베이스 DB 객체 가져옴
 import { oPicturesinDB   } from '@/datasources/firebase'
 console.log("oPicturesinDB : " ,oPicturesinDB);

export default {
  name: 'App',

  data: () => ({
    //
  }),
};
</script>

 

 

 

 

 

 

 

 

 

6. 앱 실행 화면 만들기

 

1) public/index.html 수정

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 상태 표시줄 테마 색상을 흰색으로 변경 -->
    <meta name="theme-color" content="#ffffff">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>카메라 갤러리</title>
    <!-- 구글 머티리얼 디자인 아이콘 추가-->
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
  </head>
  <body>
    <noscript>
      <strong>We're sorry but ex11 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

 

 

 

2)datasources/picture-data.js   파일 추가

export default {
  aPictures: [{
    'id': 1,
    'url': 'https://farm1.staticflickr.com/654/22663129542_e3df218c90_b.jpg',
    'title': '청춘플랫폼_우수부스투표',
    'info': '졸업작품 전시회에 출품한 작품 중에서 방문자들이 직접 우수 전시 부스를 투표로 선정하는 모습입니다.'
  },
  {
    'id': 2,
    'url': 'https://farm1.staticflickr.com/739/22663128752_7b347e01a9_b.jpg',
    'title': '전시장모습',
    'info': '전시가 열렸던 예술의 전당 전시실 안의 모습입니다. '
  },
  {
    'id': 3,
    'url': 'https://farm1.staticflickr.com/674/22055529403_aeeb5fc371_b.jpg',
    'title': 'pc실습실',
    'info': '윈도우OS 환경에서 다양한 멀티미디어디자인 S/W를 실습하는 공간입니다.'
  },
  {
    'id': 4,
    'url': 'https://farm1.staticflickr.com/706/22650669476_d8bf33c153_b.jpg',
    'title': '졸업작품전시회 주제',
    'info': '청춘 플랫폼이라는 주제로 젊은 열정이 펼쳐지는 무대가 마련되었습니다.'
  },
  {
    'id': 5,
    'url': 'https://farm1.staticflickr.com/677/22055530223_91e3f73227_b.jpg',
    'title': '명함디자인',
    'info': '자신의 미래 모습을 담은 명함을 직접 디자인하고 인쇄 제작하여 전시한 모습입니다.'
  },
  {
    'id': 6,
    'url': 'https://farm1.staticflickr.com/777/22650669326_e180cd84ca_b.jpg',
    'title': '전시_방명록',
    'info': '전시장을 방문한 관객들의 다양한 소감을 방명록의 기록으로 남겨졌습니다.'
  },
  {
    'id': 7,
    'url': 'https://farm1.staticflickr.com/759/22687855771_92dc8b3245_b.jpg',
    'title': '매킨토시 실습실',
    'info': '맥OS 환경에서 멀티미디어디자인 관련 S/W를 사용하여 작업하기 위한 맥 실습실입니다.'
  },
  {
    'id': 8,
    'url': 'https://farm6.staticflickr.com/5646/22053918354_ee031eae46_b.jpg',
    'title': '캘리그래피_디자인',
    'info': '학생들이 직접 디자인한 아날로그 정서의 캘리그래피 작품입니다.'
  },
  {
    'id': 9,
    'url': 'https://farm1.staticflickr.com/762/22687856291_963df9b90a_b.jpg',
    'title': '실습실복도',
    'info': '전공존에 따라 색상이 그룹으로 연결된 실습실 복도 모습입니다.'
  },
  {
    'id': 10,
    'url': 'https://farm1.staticflickr.com/766/22055529593_c162dc3143_b.jpg',
    'title': '인터뷰_촬영',
    'info': '졸업작품을 준비한 학생들의 과정을 인터뷰의 기록으로 남기는 모습입니다. '
  },
  {
    'id': 11,
    'url': 'https://farm6.staticflickr.com/5775/22053918014_f0e0873b7c_b.jpg',
    'title': '전시_메모',
    'info': '관객들이 전시를 보면서 느낀 점을 간단한 메모의 글로 남긴 흔적입니다.'
  },
  {
    'id': 12,
    'url': 'https://farm6.staticflickr.com/5672/22055530083_8f633d57f3_b.jpg',
    'title': '종이컵_캘리그래피',
    'info': '종이컵을 캔버스 삼아 학생들의 자유로운 생각을 캘리그래피의 작품으로 제작한 종이컵 아트워크입니다.'
  }
  ]
}

 

 

 

 

3)src/router/index.js 라우터  추가 및 변경

 

[4페이지]
· 홈(home_page)
· 세부 정보(info_page)
· 포스트(post_page)
· 카메라 촬영(camera_page)
 

 

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "home_page",
    component: () => import("@/components/home_page.vue"),
  },
  {
    path: "/camera",
    name: "camera_page",
    component: () => import("@/components/camera_page.vue"),
  },
  {
    path: "/info",
    name: "info_page",
    component: () => import("@/components/info_page.vue"),
  },
  {
    path: "/post",
    name: "post_page",
    component: () => import("@/components/post_page.vue"),
  },
];

const router = new VueRouter({
  routes,
  mode: "history",
});

export default router;

 

 

 

 

4) App.vue  

<template>
  <v-app>
    <v-app-bar app color="red" dark fixed>
      <!-- 홈 화면이 아닌 경우 돌아가기 버튼 표시 -->
      <v-btn icon v-if="$route.name !== 'home_page'" @click="$router.go(-1) ">
        <v-icon>arrow_back</v-icon>
      </v-btn>
      <v-toolbar-title>카메라 갤러리</v-toolbar-title>
      <v-spacer></v-spacer>
      <!-- 홈 화면에서만 촬영아이콘 표시-->
      <v-btn icon v-if="$route.name=='home_page'" @click="$router.push('/camera')">
        <v-icon>camera_alt</v-icon>
      </v-btn>
    </v-app-bar>
   
    <!-- 라우터에 등록했던 홈, 카메라 촬영, 상세 정보, 포스트 페이지가 표시 -->
    <v-main>
      <router-view />
    </v-main>
  </v-app>
</template>
<script>
  export default {
    name: 'App'
  }
</script>

 

 

 

 

 

 

 

 

7. 컴포넌트 작성하기

 

1)  홈컴포넌트 작성하기

 

 

home_page.vue

 

<template>
   <v-container>
      <v-row>
          <v-col cols="12" sm="6" md="4" lg="3" xl="2"
            v-for="item in this.oPictures" :key="item.key"
          >
            <v-card class="py-2 px-2">
                  <!-- 사진을 읽어서 표시하고 세부 페이지로 이동하도록 링크 설정 -->
                  <v-img :src="item.url" height="200px" class="pointer"  @click="fnDisPlayInfo(item['.key'])">

                  </v-img>

                  <!-- 사진의 제목과 내용을 바인딩하여 표시 -->
                  <v-card-title>
                    <h1 class="title grey--text text-darken-3 mb-3">{{ item.title }}</h1>
                    <p class="body-1 grey--text">{{ item.info }}</p>
                  </v-card-title>
            </v-card>
          </v-col>

          <v-col  cols="12" class="text-cener">
            <!-- 만약에 업로된 이미지가 없으면 안내 문구 표시 -->
            <p v-if="!this.oPictures.length">
              사진이 없습니다. 추가해 주세요!
            </p>
          </v-col>


          <!-- 오른쪽 하단에 포스트 추가 버튼 표시 -->
          <v-btn @click="$router.push('/post')" color="red" dark fixed bottom right fab>
              <v-icon>add</v-icon>
          </v-btn>
      </v-row>
   </v-container>
</template>


<script>
//파이어베이스 DB 객체 가져옴
import {oPicturesinDB} from "@/datasources/firebase";
export default {
  name: 'App',

  data(){
    return{
          oPictures:[] //사진 데이터 저장 변수
    }
  },
  //파이어베이스와 연결된 뷰파이어 oPictures 객체 준비
  firebase:{oPictures:oPicturesinDB},
  methods:{
    //라우터를 이용해서 세부 페이지로 이동할 때 사진의 ID 전달
    fnDisPlayInfo(pID){
      this.$router.push({
        name:"info_page",
        params:{p_id:pID}
      })
    }
  }
};
</script>
<style>
/* 마우스 커서가 손 모양이 되도록 설정 */
.pointer{
  cursor: pointer;
}
.text-cener{
  text-align: center;
}
</style>

 

 

 

 

 

2)  세부정보 컴포넌트 작성하기

 

info_page.vue

<template>
  <v-container>
    <v-row>
       <v-col cols="12">
          <v-card class="py-3 px-3">
              <!-- contain 어트리뷰트를 사용해서 세부 사진을 컨테이너 크기에 맞도록 자동으로 조절하여 표시 -->
              <v-img height="450px" contain :src="this.itemPic.url" 
              ></v-img>

              <!-- 제목이 있는 경우만 하단에 제목과 내용 표시 -->
              <v-card-text v-if="this.itemPic.title">
                <h1 class="headline mt-1 txt-center">{{ this.itemPic.title }}</h1>
                <p class="body-1 mt-1 text-center">{{ this.itemPic.info }}</p>   
              </v-card-text>

              <v-col cols="12" class="mt-3 text-center">
                <!-- 현재 사진을 삭제하는 버튼 처리 -->
                <v-btn color="grey" fab dark @click="fnDeleteItem()">
                   <v-icon>delete</v-icon>
                </v-btn>
              </v-col>
          </v-card>
       </v-col>
    </v-row>
  </v-container>
</template>

<script>
//파이어베이스에서 DB와 스토리지 객체 가져옴
import {oStorage, oPicturesinDB} from "@/datasources/firebase";

export default {
  name: 'App',
  //파이어베이스와 연결된 뷰파이어 oPictures 객체 준비
  firebase:{oPictures:oPicturesinDB},
  data(){
    return{
      oPictures:[], //사진 데이터 저장 변수
      itemPic:null,  //검색 결과 항목을 저장할 객체 변수
    }
  },

  created(){
    //라우터의 매개변수로 전달된 항목 ID값 읽기
    const itemID =this.$route.params.p_id;

    //find 검색 기능으로 파이어베이스에서 해당 ID 항목 검색 및 저장
    this.itemPic =this.oPictures.find(item=> item['.key']===itemID);
   
  },
  methods: {
    fnDeleteItem() {        
        // 파이어베이스 DB의 사진 항목 삭제
        oPicturesinDB.child(this.itemPic['.key']).remove()
        // 스토리지에 이미지가 존재할 경우(카메라 사용)만 삭제
        if (this.itemPic['filename']) oStorage.ref('images').child(this.itemPic['filename']).delete()
        // 홈화면으로 이동
        this.$router.push('/');        
      }
  },
};
</script>

 

 

 

 

 

 

 

 

 

3)  포스트 컴포넌트 작성하기

 

post_page.vue

<template>
  <v-container>
    <!-- 첫 번째 행에는 사진 표시 -->
    <v-row mt-4>
       <v-col offset="1" cols="10">
          <v-card>
             <v-img height="200px" :src="this.sPicUrl"></v-img>
          </v-card>
       </v-col>
    </v-row>


    <!-- 두 번째 행에는 기본 제목을 표시하고 수정할 수 있도록 함 -->
    <v-row mt-5>
      <v-col offset="2" cols="8">
          <v-text-field name="title" label="사진 제목" v-model="sTitle" autofocus></v-text-field>
      </v-col>
    </v-row>



    <!-- 세번째 행에는 기본 내용을 표시하고 수정할 수 있도록 함 -->
    <v-row>
      <v-col offset="2" cols="8">
          <!-- 3줄로 편집 제한-->
          <v-text-field name="info" label="사진 설명" v-model="sInfo" multi-line rows="3"></v-text-field>
      </v-col>
    </v-row>





    <v-row>
      <v-col cols="12" class="text-center">
        <v-btn color="orange" darak large @click="fnSubmitPost()">업로드</v-btn>
      </v-col>
    </v-row>


  </v-container>
</template>

<script>
  //JSON 파일로부터 이미지 정보 가져옴
import oPictureData from '@/datasources/picture-data';
//파이어베이스에서 DB 객체 가져옴.
import {oPicturesinDB} from '@/datasources/firebase';

export default {
  //파이어베이스와 연결된 
  firebase : {
    oPictures:oPicturesinDB
  },
  name: 'App',

  data(){
    return{
      oPictures:[], //사진 데이터 저장 변수
      //초깃값으로 JSON 파일의 이미지 배열 저장
      aPictures:oPictureData.aPictures,
      sTitle:'',
      sInfo:'',
      sPicUrl:''
    }
  }
  ,
  mounted() {
    //JSON 파일에서 사진 정보를 랜덤으로 읽어 와서 사진과 포스트 글 준비
    let nIndex=Math.floor(Math.random()*12);
    const itemPic=this.aPictures[nIndex];
    this.sTitle=itemPic.title;
    this.sInfo=itemPic.info;
    this.sPicUrl=itemPic.url;
  },

  methods: {
    fnSubmitPost(){
      //DB에 저장하고 홈 화면으로 이동
      oPicturesinDB.push({
        'url':this.sPicUrl,
        'title':this.sTitle,
        'info':this.sInfo
      }).then(this.$router.push('/'))
    }
  
  },

};
</script>

 

 

 

 

 

 

 

 

 

 

 

4)  카메라  촬영 컴포넌트 작성하기

 

 

파이어베이스에서 storage 사용 설정을 한다.

다음화면은 storage 사용 설정 후 동영상 업로드 목록 화면 

 

 

 

카메라 영상 표시하기

카메라 영상을 실시간으로 표시하려면 video 엘리먼트를 사용하면 된다.
그리고 video 엘리먼트에 담긴 영상을 갤러리에 전달하려면 ref 어트리뷰트를 지정해야 한다.

 

 

camera_page.vue

<template>
  <v-container>
    <v-row>
      <v-col cols="12" class="text-center">
        <!-- 카메라 영상 부분을 표시 -->
        <video ref="rVideo" class="style_video"></video>
      </v-col>
      <v-col cols="12" class="mt-5 text-center">
        <!-- 만약에 업로드된 이미지가 없으면 안내 문구 표시-->
        <p>현재 iOS는 지원하지 않습니다.</p>
      </v-col>
    </v-row>
    <div class="text-center my-3">
      <!-- 카메라 캡처 버튼을 영상 하단 중앙에 위치 -->
      <v-btn v-if="!this.bIsWait" color="red" fab dark bottom @click="fnCameraCapture( )">
        <v-icon>camera</v-icon>
      </v-btn>
      <v-progress-circular v-if="this.bIsWait" :size="50" indeterminate color="grey"></v-progress-circular>
    </div>
  </v-container>
</template>

<script>
  // 파이어베이스에서 DB와 스토리지 객체 가져옴
  import {
    oStorage,
    oPicturesinDB
  } from '@/datasources/firebase'
  export default {
    // 파이어베이스와 연결된 뷰파이어 oPictures 객체 준비
    firebase: {
      oPictures: oPicturesinDB
    },
    data() {
      return {
        oPictures: [], // 사진 데이터 저장 변수
        oVideoStream: null, // 카메라 영상 스트림을 저장할 객체변수
        bIsWait: false
      }
    },
    mounted() {
      // Web API를 통해서 사용자 카메라의 접근(영상 only)을 요청함
      navigator.mediaDevices.getUserMedia({
        video: true
      }).then(pVideoStream => {
        // 카메라 영상 스트림 정보를 oVideoStream에 저장함
        this.oVideoStream = pVideoStream
        // 카메라 영상 스트림 정보를 video 엘리먼트에 표시함
        this.$refs.rVideo.srcObject = pVideoStream
        this.$refs.rVideo.play()
      }).catch(function (err) {
        console.log(err)
      })
    },
    destroyed() {
      // 현재 화면을 종료할 경우 현재 재생되는 영상 트랙을 찾아 종료시킴
      const oTrack = this.oVideoStream.getTracks()
      oTrack.map(pTrack => pTrack.stop())
    },
    methods: {
      fnCameraCapture() {
        this.bIsWait = true
        // 현재 재생되는 트랙을 찾아 스틸이미지로 캡처함
        const oVideoTrack = this.oVideoStream.getVideoTracks()[0]
        let oCapturedImage = new window.ImageCapture(oVideoTrack)
        const options = {
          imageHeight: 359,
          imageWidth: 640,
          fillLightMode: 'off'
        };
        const self = this
        // 캡처된 이미지를 파이어베이스 DB와 스토리지에 저장함
        return oCapturedImage.takePhoto(options).then(pImageData => {
          // 영상 정지
          const oTrack = self.oVideoStream.getTracks()
          oTrack.map(pTrack => pTrack.stop())
          console.log('캡처: ' + pImageData.type + ', ' + pImageData.size + '바이트');
          // 저장할 이미지 파일이름으로 사용할 ID 준비
          const nID = new Date().toISOString()






          //pImageData  서버에 저장 처리 하면 된다. 여기서는 
          // 파이어베이스 스토리지에 이미지 파일 저장
          let uploadTask = oStorage.ref('images').child('pic' + nID).put(pImageData);
          uploadTask.on('state_changed', function (snapshot) {
            // state_changed 이벤트를 통해서 얼만큼의 바이트가 업로드 중인지 콘솔에 표시
            let progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
            console.log('업로드: ' + progress + '% 완료', snapshot.state);
          }, function (error) {
            console.log(error)
            // 오류 발생 시 콘솔에 표시
          }, function () {
            // 성공적으로 업로드 완료 후 파이어베이스 DB에 정보 저장
            uploadTask.snapshot.ref.getDownloadURL().then(function (downloadURL) {
              console.log('업로드URL:', downloadURL);
              oPicturesinDB.push({
                  'url': downloadURL,
                  'title': '',
                  'info': '',
                  'filename': 'pic' + nID
                })
                .then(self.$router.push('/')) // 저장 후 홈 화면으로 이동
            });
          });




        })
      }
    }
  }
</script>

<style>
  /* 카메라 영상의 너비값을 브라우저 너비에 맞춤 */
  .style_video {
    width: 100%
  }
</style>

 

 

 

 

 

 

 

 

 

8.워크박스로 서비스 워커에서 캐시 관리하기

 

카메라 사진 갤러리 앱 만들기 예제에서 카메라로 직접 찍은 사진은 기본 캐시가 되지 않으므로 InjectMainfest 모드를 통해 캐시해야 한다.


지금까지는 캐시를 관리할 때 GenerateSW 모드를 사용하며 프리캐시와 런타임 캐시를 쉽고 빠르게 설정값으로 관리할 수 있었다. 하지만 프로젝트 성

격상 서비스 워커에서 직접코드를 넣어야 할 경우가  발생하면 GenerateSW 모드 대신에 InjectManifest 모드를 사용해야 한다. 이렇게 하면
서비스 워커에 자신의 코드를 넣을 수 있으며 프리캐시와 런타임 캐시를 직접 관리할 수 있다.


 

기존

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: [
    'vuetify'
  ]
})

 

InjectManifest 플러그인 모드는 vue.config.js 파일을 루트 폴더에 생성한 후에 다음 내용을 입력

다음과 같이 변경한다.

vue.config.js

module.exports = {
  pwa: {
    // 서비스워커를 코드로 수정하기 위해 InjectManifest 모드 사용
    workboxPluginMode: 'InjectManifest',
    workboxOptions: {
      swSrc: "./src/service-worker.js"
    }
  }
}

 

 

 

src 폴더에 service-worker.js 를 추가

importScripts(
  "https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js"
); // <-- workbox 최신번전 CDN 추가

//워크박스는 캐시를 수행할 때 진행 상황을 콘솔 창에 표시해 줍니다.
//개발하면서 이러한 메시지를 볼 수 있으면 편리한데 디버그 모드일 때만 가능합니다.
//배포를 위해 더는 디버그가 필요없다면 프로덕션 모드로 변경하면 됩니다.
// Workbox를 디버그모드로 설정
// workbox.setConfig({
//   debug: true,
// });

// 배포용 프로덕션 모드
workbox.setConfig({
  debug: false,
});

// Vue-Cli에서 기본으로 제공하는 프리캐시 설정을 Workbox에 적용
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST); // <-- self.__WB_MANIFEST로 변경

// 촬영된 이미지 캐시
workbox.routing.registerRoute(
  new RegExp(
    "https://firebasestorage.googleapis.com/v0/b/pwa-camera.appspot.com/.*"
  ),
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: "camera-images",
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // <-- ExpirationPlugin로 대문자 'E' 변경
        maxEntries: 60, //이미지를 총 60개만 캐시하도록 지정
        maxAgeSeconds: 365 * 24 * 60 * 60, // 1년 지정
      }),
    ],
  })
);

 

 

 

워크박스는 캐시를 수행할 때 진행 상황을 콘솔 창에 표시해 줍니다.

개발하면서 이러한 메시지를 볼 수 있으면 편리한데 디버그 모드일 때만 가능합니다.

배포를 위해 더는 디버그가 필요없다면 프로덕션 모드로 변경하면 됩니다.

// Workbox를 디버그모드로 설정
 workbox.setConfig({
   debug: true,
});

// 배포용 프로덕션 모드
workbox.setConfig({
  debug: false,
});

 

 

workbox.routing.registerRoute(
    //캐시 경로
    //캐시 전략
)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

9.빌드 및 firebase  서버에 배포하기

 

다음을 참조

 

https://macaronics.net/m04/vue/view/2048

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 : 

 

Do it! 프로그레시브 웹앱 만들기

 

반응형 웹 개발부터 하이브리드 앱 배포까지 PWA 완전 정복!

김응석 저자(글)

이지스퍼블리싱 · 2020년 08월 06일

8.5(7개의 리뷰)

도움돼요(50%의 구매자)

 

  • Do it! 프로그레시브 웹앱 만들기 대표 이미지

  •  

 

 

https://product.kyobobook.co.kr/detail/S000001817978

 

 

 

 

 

spring

 

about author

PHRASE

Level 60  라이트

위(衛) 나라 땅 복수(복水)가에 있는 상림(桑林) 사이에서 영공(靈公)이 들은 음악은 음탕하고 문란한 망국의 가락이었다. 위 나라 영공이 진(晋)에 가는 도중 복수가에서 들었던 음악으로서, 이것을 진평공(晋平公) 앞에 연주하였더니 사광(師曠)이 망국의 은 나라 음악이라고 하여 중지시킨 일이 있다. -예기

댓글 ( 4)

댓글 남기기

작성