전에 vite, vue3, typescript와 파이어베이스를 이용해 프로젝트를 만들었었다.(https://minu0807.tistory.com/154)
최근 시간 날 때 nuxt3를 공식문서를 보면서 조금씩 이용해 보다가 vuefire라는 걸 이용해 파이어베이스를 쉽게 사용할 수 있는 것 같아 한 번 사용해보려고 했으나.... 생각보다 불편해서 그냥 firebase 패키지만 사용했다.
정확한 이유는 본문 맨 마지막에 작성...
이 글에서는 파이어베이스-파이어스토어에 있는 데이터를 가져오는 것까지만 작성했다.
본문에서 작업한 프로젝트는
https://minu0807.tistory.com/157
https://minu0807.tistory.com/158
이 2개를 진행한 이후에 작업했다.
설치
npm i firebase
.env 생성
파이어베이스 설정 값들을 공개할 순 없기에 .env에 작성을 했다.
FIREBASE_API_KEY=
FIREBASE_AUTH_DOMAIN=
FIREBASE_DATABASE_RUL=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_APP_ID=
FIREBASE_MEASUREMENT_ID=
이렇게 생성 후 파이어베이스 프로젝트 설정에 있는 값을 넣는다.
nuxt.config.ts 수정
env 파일에 작성한 값을 읽어와 사용하기 위해 따로 작업을 해줘야 한다.
파일 안에 값을 가져와 사용하려면 runtimeConfig 옵션을 활용해야 한다.
export default defineNuxtConfig({
modules: ['nuxt-quasar-ui'],
quasar: {
lang: 'ko-KR',
extras: {
fontIcons: ['material-icons'],
},
},
runtimeConfig: {
public: {
FB_API_KEY: process.env.FIREBASE_API_KEY,
FB_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
FB_DATABASE_URL: process.env.FIREBASE_DATABASE_RUL,
FB_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FB_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
FB_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID,
FB_APP_ID: process.env.FIREBASE_APP_ID,
FB_MEASUREMENT_ID: process.env.FIREBASE_MEASUREMENT_ID,
},
},
});
https://nuxt.com/docs/guide/going-further/runtime-config#runtime-config
초기화 작업
파이어베이스앱 설정 초기화의 경우 한 번만 실행되면 되기 때문에 plugins 디렉터리에 만들어서 처리하려고 했으나 진행 중 발생한 문제를 도저히 해결을 못하겠어서 고민하다 composables 방식으로 변경했다.
Error: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp().
이거랑 방법을 바꿀 때마다 다른 종류의 에러들이 계속 발생했었다.
프로젝트에 composables 디렉터리를 생성 후 useFirebase.ts 파일 생성 후 아래와 같이 소스코드를 작성
import { initializeApp, getApps } from 'firebase/app';
import type { FirebaseApp } from 'firebase/app';
export const useFirebaseApp = (): FirebaseApp => {
const config = useRuntimeConfig();
let app: FirebaseApp;
if (!getApps().length) {
app = initializeApp({
apiKey: config.public.FB_API_KEY,
appId: config.public.FB_APP_ID,
authDomain: config.public.FB_AUTH_DOMAIN,
databaseURL: config.public.FB_DATABASE_URL,
measurementId: config.public.FB_MEASUREMENT_ID,
messagingSenderId: config.public.FB_MESSAGING_SENDER_ID,
projectId: config.public.FB_PROJECT_ID,
storageBucket: config.public.FB_STORAGE_BUCKET,
});
} else {
app = getApps()[0];
}
return app;
};
getApps() 함수는 이미 초기화된 파이어베이스앱이 있는지 확인할 수 있다.
이 함수를 사용해서 이미 초기화된 파이어베이스앱이 있으면 다시 초기화 시키지 않고, 생성된 파이어베이스 앱을 전달하게끔 할 수 있도록 작성했다.
https://nuxt.com/docs/guide/directory-structure/composables
파이어스토어 데이터베이스에서 데이터 가져오기
composables 디렉터리에 useFirestoreDB.ts를 생성 후 아래와 같이 소스코드 작성
import { getFirestore, collection, getDocs } from 'firebase/firestore';
import type { DocumentData } from 'firebase/firestore';
export const getFirestoreData = async (
collectionName: string,
): Promise<(DocumentData & { id: string })[] | []> => {
let result: (DocumentData & { id: string })[] = [];
try {
if (!collectionName) throw new Error('Need CollectionName.');
const querySnapshot = await getDocs(collection(db(), collectionName));
if (!querySnapshot.empty) {
result = querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
}
} catch (err) {
console.error(err);
throw new Error('데이터 가져오기 실패');
}
return result;
};
파이어스토어에서 데이터를 가져오는 걸 공통으로 사용하기 위해 컴포저블 함수로 만들었다.
가져올 컬렉션 이름만 넘겨주면 해당 컬렉션에 데이터가 있으면 모든 데이터를 넘겨주고, 없으면 빈 배열을 반환한다.
추후에 옵션 파라미터로 where을 넘겨서 조건식으로 데이터를 가져올 수 있도록 함수를 수정할 생각이다.
현재 위 소스는 파라미터로 받은 컬렉션네임의 컬렉션에 있는 모든 데이터를 가져온다.
그리고 추후에 id를 사용할 것 같아서 id와 data를 동일한 레벨상에 담게 했다.
처음엔 { id: string, data: { DocumentData } } 이런 형식으로 데이터 구조를 만들었었다.
퀘이사 테이블 컴포넌트에 데이터를 맵핑할 때 1뎁스 이상은 맵핑이 안돼서 이렇게 만들었다.
데이터 가져와서 화면에 그리기
아래 소스는 위에 만든 컴포저블 함수를 이용해 데이터를 가져와서 화면에 뿌려준다.
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import type { Ref } from 'vue';
import type { DocumentData } from 'firebase/firestore';
import type { QTableColumn } from 'quasar';
let lists: Ref<{ id: string; data: DocumentData }[] | []> = ref([]);
onMounted(async () => {
lists.value = await getFirestoreData('test-board');
});
const columns: QTableColumn[] = [
{
name: 'title',
label: '제목',
field: 'title',
align: 'left',
headerStyle: 'width: 20%',
sortable: true,
},
{
name: 'wireter',
label: '작성자',
field: 'writer',
align: 'center',
sortable: true,
},
{
name: 'createdAt',
label: '작성일',
field: 'createdAt',
align: 'center',
sortable: true,
format(val, _row) {
return val.toDate().toLocaleString();
},
},
{
name: 'viewer',
label: '조회수',
field: 'viewer',
align: 'center',
sortable: true,
},
];
</script>
<template>
<q-card dark bordered class="bg-brown-7 my-card">
<q-card-section>
<div class="text-h6">Firebase / Firestore Database</div>
<div class="text-subtitle2">firestore</div>
</q-card-section>
<q-separator dark inset />
<q-card-section>
파이어베이스에서 제공하는 Firestore Database를 사용해보는 화면
</q-card-section>
</q-card>
<q-separator class="q-mt-md q-mb-xs" />
<div class="q-pa-md">
<q-table
bordered
title=""
:rows="lists"
:columns="columns"
no-data-label="No Data."
row-key="name"
/>
</div>
</template>
퀘이사의 테이블 컴포넌트를 사용했다.
field 부분에서 화면에 보여줄 데이터를 맵핑하면 된다.
결과 화면
vuefire를 사용하지 않은 이유
파이어베이스를 사용하는데 vuefire만 쓰는게 아니라 firebase와 같이 섞어서 사용해야 하는 게
굳이 이럴 거면 firebase 패키지만 쓰는 게 더 편한 것 같은데?라는 생각이 쓸대마다 점점 크게 느껴졌다.
<script setup>
import { useCollection } from 'vuefire'
import { collection } from 'firebase/firestore'
const todos = useCollection(collection(db, 'todos'))
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<span>{{ todo.text }}</span>
</li>
</ul>
</template>
이게 vuefire 공식문서에 있는 파이어스토어 컬렉션 데이터를 가져오는 예제 소스이다.
firebase/firestore의 collection과 vuefire의 useCollection을 써야 한다.
사용자가 2개를 선언해서 사용할게 아니라 vuefire 패키지 내에서 firebase의 함수를 사용해 처리해 줬으면 어땠을까 하는 생각이 많이 들었다.
그리고 가장 큰 문제는 타입지정이었다.
공식문서도 js 방식의 예제들 뿐이라 소스를 하나하나 들어가서 내가 사용하는 함수가 어떤 타입을 리턴하는지, 어떤 타입의 파라미터를 받는지 등등 확인을 해보는데 한계는 좀 느꼈었다.
import { collection, query } from 'firebase/firestore';
import type { DocumentData } from 'firebase/firestore';
import { useFirestore, useCollection } from 'vuefire';
import type { _RefFirestore } from 'vuefire';
export const getFirestoreData = (collectionName: string, query?: string) => {
const db = useFirestore();
let result: _RefFirestore<DocumentData[]>;
if (!collectionName) throw 'Need CollectionName.';
if (!query) {
result = useCollection(collection(db, collectionName));
} else {
result = useCollection(collection(db, collectionName));
}
return result;
};
소스를 커밋하지 않고 계속 수정을 했어서 유일하게 커밋했던 게 이 소스인데...
컴포저블함수로 처리할 당시 컬렉션에서 데이터를 가져와 반환할 때 타입을 _RefFirestore로 지정해 줬었다.
그리고 화면단에서 저 컴포저블 함수를 받아서 처리를 해야 하는데
vue의 ref를 사용하지 않으면 값이 변경되어도 화면에는 렌더링이 되지 않기 때문에 제네릭이던 뭐든... 이용해 ref를 감싸줘야 했다.
타입을 지정하지 않으면 어떻게 이방법 저방법 하다 보니 값을 받을 순 있었는데 소스상에선 계속 빨간줄이 그어져 있고...뭔가 맵핑이 잘 되지도 않았고, 타입을 지정하지 않았다고 에러도 뜨고 아주 난리가 보통이 아니라 그냥 firebase 패키지만 쓰기로 마음먹었다.
vuefire 공식문서를 봐보면 nuxt용 vuefire 모듈이 있긴 한데, 아직 정식버전은 아닌 것 같았다.
이것도 사용해 봤는 데 사용방법 자체가 공식문서에 정보도 아직 부족하고 계속 개발중이라는 문구가 있는 걸 봐선 추후에 다시 봐봐야 할 것 같았다.
작업한 소스
https://github.com/smw0807/vue3/blob/main/nuxt3-quasar/pages/firebase/database.vue
https://github.com/smw0807/vue3/blob/main/nuxt3-quasar/composables/useFirestoreDB.ts
https://github.com/smw0807/vue3/blob/main/nuxt3-quasar/composables/useFirebase.ts
시간날때마다 작업할 예정이라 본문의 소스와 달라질 수 있습니다.
'VueJS > Nuxt' 카테고리의 다른 글
[Nuxt3] GraphQL 사용하기(nuxt-graphql-client) (1) | 2024.10.27 |
---|---|
[VueJS] Nuxt3에 pinia(피니아) 적용하기 (2) | 2023.09.08 |
[VueJS] Nuxt3에 quasar 설치 및 적용(+sass, icon) (0) | 2023.06.06 |
[VueJS] Nuxt3 설치 및 layouts, pages 적용(compositionAPI) (0) | 2023.06.04 |
[VueJS] nuxt-socket-io 모듈 사용해보기 (8) | 2022.04.22 |