카테고리 없음

Cloud Firestore 및 Storage - nosql CRUD 및 Securiy 설정

유혁스쿨 2024. 2. 20. 16:11
728x90
반응형

FireBase Collection


Collection은 일반적인 관계형 데이터베이스의 테이블과 유사하다.

Collection에 데이터를 저장하면 Documents 단위로 저장되는데, 이는 각 테이블에 저장된 Row(행)과 유사하다.

 

 

Cloud Firestore 데이터베이스 생성 및 활성화

 

 

 

우선은 테스트 모드로 시작한 뒤 마지막에 Security Rule, API key 설정 진행할 예정이다..

(마지막까지 꼭 참고하길 바란다!)

 

 

아래와 같이 Firebase 콘솔에서 직접 추가/제거/수정이 가능하다

 

 

우리는 코드로 직접 추가/수정/제거를 할 예정이므로 위 과정은 생략한다.

 

다음은 첨부파일이다.

콘솔의 좌측 네비 빌드 메뉴에서 Storage를 선택하고 우측에서 [시작하기] 버튼을 클릭한다.

 

다음은 어플리케이션으로 돌아와 파이어베이스 설정파일을 초기화하는 모듈에서 Cloud Firestorage와 Cloud Storage를 활성화한다.

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore"; // 추가
import { getStorage } from "firebase/storage"; // 추가

const firebaseConfig = {
  /* 생략 */
};

const app = initializeApp(firebaseConfig);

export const auth = getAuth(app);

export const db = getFirestore(app); // 추가
export const storage = getStorage(app); // 추가

 

 

Database, Storage - 문서, 첨부파일 CRUD 추가/수정/삭제/조회

/* 데이터베이스 관련 모듈 - firebase/firestore */
import { 
collection, /* 조작할 DB 컬렉션 지정 함수 */
addDoc, updateDoc, /* Doc - 추가, 수정 함수 */
} from "firebase/firestore";

/* 파일첨부 관련 모듈 - firebase/storage  */
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";

import { auth, db, storage } from "./firebase";


/*============================================================================================*/

const collection = collection(db, "articles"); // collection(): Fire Store로부터 articles 컬렉션 반환

const user = auth.currentUser; // 게시글에 작성자를 저장하기 위해 로그인한 user객체 조회

/**
 * [Doc 데이터 추가]
 * addDoc(reference, data)
 * Fire Store에 새로운 Document 생성함수 
 * @param reference 생성할 대상 컬렉션
 * @param data 추가할 {필드:데이터} 객체
 * @returns 추가된 document Promise객체 반환 - 해당 document객체를 기준으로 update가 가능해진다.
 */
const doc = await addDoc(collection, { 
    article,
    createdAt: Date.now(),
    username: user.displayName || "Anonymous", // 닉네임이 존재하지 않으면 Anonymous 반환
    userId: user.uid // 게시글을 삭제하기 위한 작성자와 로그인한 userid일치여부
})

/*============================================================================================*/

/* [ ========== 데이터 조회 ========== ] */

const user = auth.currentUser;

const query = query(
    collection(db, "articles"),  // 조작할 컬렉션 객체 반환
    orderBy("createdAt", "desc"), //createdAt 기준 내림차순
    where("userId", "==", user?.uid), // 로그인한 사용자에 대한 게시글 조회
    limit(25)
)


/**
 * [Doc 데이터 조회]
 * Firebase 쿼리기반 데이터 조회
 * 조회할 컬렉션과 조건을 지정하여 쿼리를 생성후 getDocs함수의 매개변수로 넘겨 쿼리에대한 spanshot객체를 전달받는다.
 * 해당 snapshot 객체로부터 각 도큐먼트의 데이터를 조회할 수 있게 된다.
 * @param query 조회할 Query객체
 * @returns 조회된 시점의 데이터가 저장된 snapshot 객체를 반환한다.
 */
 
const getArticles = (snapshot: QuerySnapshot<DocumentData>) => {
    return snapshot.docs.map(doc=>{
        const{photo, article, userId, username, createdAt} = doc.data();
        return {
          photo,
          article,
          userId,
          username,
          createdAt,
          docId: doc.id
        };
    })
}
const fetchArticles = async () => {
    const querySnapshot = await getDocs(query);
    const articles = getArticles(querySnapshot)
    console.log(articles)
}
fetchArticles();

/**
 * [Doc 데이터 실시간 조회]
 * onSnapshot - firebase realtime
 * Firebase 쿼리기반 실시간 데이터 조회
 * 데이터베이스 및 쿼리와 실시간 연결을 생성하고, 해당 쿼리에 새 요소가 생성되거나 요소가 삭제되었거나 또는 업데이트 됐을 때 쿼리에 알려준다.
 * 문서를 한 번만 가져오는 대신 쿼리에 리스너를 추가한다.
 * 그리고 무언가 삭제, 편집 또는 생성되었다는 알림을 받으면 해당 쿼리의 문서에서 필요한 데이터를 추출한다.
 * unsubscribe (구독취소) 함수를 반환한다
 * @param query 조회할 Query객체
 * @param 조회된 시점의 데이터가 저장된 snapshot 객체를 전달받아 실행할 콜백 함수
 * @returns unsubscribe() 구독 취소 함수를 반환받는다.
 */
useEffect(()=>{ // 리엑트에서 구독취소...
    let unsubscribe: Unsubscribe|null = null;
    const fetchRealtimeArticles = async () => {
      unsubscribe = await onSnapshot(query, (snapshot) => {
        // snapshot으로부터  마지막 스냅샷 이후의 변경사항(크기, 쿼리, 메타데이터, 문서 등)을 볼 수 있다.
        // snapshot.docChanges // 변경사항 을 배열로 반환한다. (변경사항으로 작업을 분기처리할 수 있음..) 
        const articles = getArticles(snapshot)
        console.log(articles)
      })
    }
    fetchRealtimeArticles();

    /* useEffect 클린업 함수 */
    return () => {// 리액트가 아닌 환경에서는 새로고침, 브라우저종료에 대한 이벤트에 클린업함수에 실행되는 구독취소 함수를 핸들러로 등록해준다.
      unsubscribe && unsubscribe(); // 사용자가 해당 컴포넌트를 해제하면 구독을 취소하여 리소스 비용을 절감한다.
    }
}, [])

/*============================================================================================*/

/**
 * [Storage 파일 첨부]
 * uploadBytes(reference, data)
 * 저장경로와 파일을 매개변수로 담아 Byte로 변환하여 Cloud Storage에 저장한다.
 * @param reference 저장할 파일에 대한 저장경로
 * @param data 저장할 파일 객체
 * @returns 추가된 첨부결과 Promise객체 반환 - 해당 첨부결과 객체를 기준으로 Storage에 저장된 URL조회가 가능하다
 */
const locationRef = ref(storage, `articles/${user.uid}/${doc.id}`) // 저장할 파일 경로 (doc.id인 게시글 PK는 자동 생성된다.)
const result = await uploadBytes(locationRef, file); //파일을 바이트 변환 저장 후 저장결과 반환
const url = await getDownloadURL(result.ref);// 저장된 파일 경로
console.log(url)



/**
 * [Storage 첨부 파일 삭제]
 * deleteObject(reference, data)
 * 저장된 경로를 매개변수로 넘기면 해당 경로를 포함한의 파일을 삭제한다.
 * @param reference 삭제할 파일에 대한 저장경로
 * @returns 첨부파일 삭제 결과 단순 Promise객체 반환
 */
const reference = ref(storage, `articles/${user.uid}/${docId}`); // 삭제할 파일 경로 (doc.id인 게시글 PK는 자동 생성된다.)
await deleteObject(reference);


/**
 * [Doc 데이터 조회]
 * Firebase 쿼리기반 데이터 조회
 * 조회할 컬렉션과 조건을 지정하여 쿼리를 생성후 getDocs함수의 매개변수로 넘겨 쿼리에대한 spanshot객체를 전달받는다.
 * 해당 snapshot 객체로부터 각 도큐먼트의 데이터를 조회할 수 있게 된다.
 * @param data 추가할 {필드:데이터} 객체
 * @returns 추가된 document Promise객체 반환 - 해당 document객체를 기준으로 update가 가능해진다.
 */

/*============================================================================================*/

/**
 * [Doc 데이터 수정]
 * updateDoc(reference, data)
 * Fire Store에 새로운 Document 생성함수 
 * @param reference 수정할 대상 컬렉션
 * @param data 수정할 {필드:데이터} 객체
 * @returns 단순 Promise를 반환한다.
 */
 
// const doc = doc(db, "articles", doc.id); //저장된 doc객체 조회 (저장 후 연관관계 데이터 수정이라면, addDocs로 반환받은 값을 바로 넣는다.)
updateDoc(doc, {photo:url}) // 저장한 file URL을 저장되었던 Document에 다시 저장 (관계형 DB의 연관관계와 유사)

/*============================================================================================*/

/**
 * [Doc 데이터 삭제]
 * updateDoc(reference, data)
 * Fire Store에 새로운 Document 생성함수 
 * @param reference 수정할 대상 컬렉션
 * @param data 수정할 {필드:데이터} 객체
 * @returns 단순 Promise를 반환한다.
 */
 
const doc = doc(db, "articles", doc.id); //삭제를 위해 저장된 doc객체 조회
deleteDoc(doc);

 


[ Security Rules ]

 

[ Rule - Cloud Firestore]

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /{tweets}/{doc} {
      allow read, create : if request.auth != null; /* 로그인 */
      allow delete, update : if request.auth.uid == resource.data.userId /* 로그인 유저 == 작성자 */
    }
  }
}

[ Rule - Storage]

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {

    match /{allPaths=**} {
      allow read, delete : if request.auth != null; /* 로그인 */
      allow write : if request.auth != null && request.resource.size < 2 * 1024 * 1024; /* 로그인, 사이즈 2MB미만 */
    }
  }
}

[API Security 설정 ]

 

 

구글 클라우드 링크

 

위 구글 클라우드  링크로 접속한뒤 아래 콘솔 버튼을 클릭한다.

 

콘솔 버튼을 클릭한다.

 

좌측 API 및 서비스 탭의 사용자 인증 정보 선택

 

API키의 노란색 삼각형 경고에 해당하는 Key 선택
(아직 인증되지 않은 Key이므로 경고가 출력되는것이다.)

 

1번 2번 순서대로 클릭

 

 

 

지정한 URL이 아닌 다른 URL을 통해 접근한다면 (에를들어 배포된 URL을 등록하고 개발서버의 localhost를 통해 접근) 아래와 같이 오류가 발생한다.

 

728x90
반응형