본문 바로가기

Firebase『Firestore 쿼리문』

by Recstasy 2019. 12. 12.

1 CDN설치

firestore() DB를 사용할 수 있는 CDN을 <head>태그 사이에 삽입한다. 

<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-firestore.js"></script>


파이어베이스의 계정에서 각 프로젝트별로 할당받은 apiKey를 삽입하고, db변수에 firebase.firestore()모듈을 선언한다. DB의 collection의 데이터를 사용하기 위해서는 반드시 firebase.firestore()인스턴스를 만들어야 한다. 


// Initialize Cloud Firestore through Firebase
firebase
.initializeApp({
  apiKey
: '### FIREBASE API KEY ###',
  authDomain
: '### FIREBASE AUTH DOMAIN ###',
  projectId
: '### CLOUD FIRESTORE PROJECT ID ###'
});

var db = firebase.firestore();



2 데이터 읽기

파이어베이스에서 Collection의 데이터를 get().then()메서드로 불러올 수 있다. 문서에 있는 데이터 전체를 불러오는 방법은 아래와 같다.


 2-1 『컬렉션의 모든 문서 읽기

DB All 불러오기 : get().then()

var docRef = db.collection("cities");

docRef
.get().then(function(querySnapshot) {
   
if (querySnapshot) {
        querySnapshot.forEach(function(doc){

let docs = doc.data();

console.log('문서의 id :'+doc.id);


for(let item in docs){

console.log('key :'+ item);

console.log('value :'+ docs[item]);

} });
   
} else {
       
// doc.data() will be undefined in this case
        console
.log("No such document!");
   
}
}).catch(function(error) {
    console
.log("Error getting document:", error);
});


.then()은 프로미스 구문으로써 콜벡함수를 받을 수 있다. doc전체 데이터를 get().then()으로 받는 콜벡함수는 [ 객체1, 객체2, 객체3 .... ] 형태이다. 따라서 forEach()구문으로 객체(doc데이터)를 받을 수 있다. 


2-2 『컬렉션의 특정 문서 읽기

* 특정(id) 문서 불러오기 : doc(id).get().then()  

var docRef = db.collection("cities").doc("SF");

docRef
.get().then(function(querySnapshot) {
   
if (querySnapshot.exists) {
       
for(let doc in querySnapshot.data()){

             console.log( `key : ${doc}, value : ${querySnapshot.data()[doc]}` )

       }

    } else {
       
// doc.data() will be undefined in this case
        console
.log("No such document!");
   
}
}).catch(function(error) {
    console
.log("Error getting document:", error);
});


get().then()메서드 체인 앞에 doc(id)를 붙여주면, id에 해당하는 문서의 데이터만 불러온다. 여기서 doc(id)를 통해 리턴받는 콜벡함수의 값은 배열 안에 객체가 있는 형태가 아닌 순수 객체의 형태다. 따라서 for in문을 통해 키와 값을 추출한다. 정해진 규칙은 아니지만 데이터를 추출할 때 아래 3가지 구문이 가장 효과적이다.


1) forEach(callback) :: [ {객체1}, {객체2}, {객체3} ....]

2) for in문 :: {키 :"값"}

3) for of문 :: [ "값1", "값2", "값3" ....]


**forEach(), for in, for of문 내용은 "skj5766"님의 블로그를 참고하자.

https://medium.com/sjk5766/foreach-for-in-for-of-%ED%8A%B9%EC%A7%95-%EB%B0%8F-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90-47a77464b034


2-3 『컬렉션의 문서 검색

* 여러문서 불러오기 : where("키값","==","value값").get().then()

db.collection("books").where("title", "==", "지식의 세계사")
   
.get()
   
.then(function(querySnapshot) {
        querySnapshot
.forEach(function(doc) {
           
// doc.data() is never undefined for query doc snapshots
            console
.log(doc.id, " => ", doc.data());
       
});
   
})
   
.catch(function(error) {
        console
.log("Error getting documents: ", error);
   
});


where(①, ②, ③)의 값은 다음과 같다.


① : key

② : 비교단위

③ : value


에 검색하고자 하는 값을 입력하면 해당하는 doc.id와 doc.data()를 받을 수 있다. get().then()메서드와 관련한 자세한 내용은 아래의 firebase API 자료에서 확인할 수 있다. 


firestore() API 문서https://firebase.google.com/docs/firestore/query-data/get-data


2-4 『실시간 문서읽기

클라우드firestore() DB에서는 onSnapshot()메서드를 사용하여 DB변화("쓰기", "수정", "삭제")를 파악할 수 있다. onSnapshot()은 vue의 양방향 바인딩이라 생각하면 이해하기 쉽다. onSnapshot()은 DB의 변화를 실시간으로 전달한다. 만일 바뀐 데이터를 프론트에 전달할 필요가 없다면 get().then()과 별 차이가 없다.


db.collection("cities").doc("SF")
   
.onSnapshot(function(doc) {
        console
.log("Current data: ", doc.data());
   
});


단순히 '읽기'를 실행하는 쿼리문은 get().then()과 비슷하다. 아래 코드는 cities 컬렉션에서 'CA'값을 갖고 있는 'state' key를 감시하면서 변화양상을 cities배열에 전달하는 내용이다. cities컬렉션 내의 doc중에서 state값이 변경되면,  console.log()값이 출력된다. 


db.collection("cities").where("state", "==", "CA")
   
.onSnapshot(function(querySnapshot) {
       
var cities = [];
        querySnapshot
.forEach(function(doc) {
            cities
.push(doc.data().name);
       
});
        console
.log("Current cities in CA: ", cities.join(", "));
   
});


onSnapshot()메서드와 get().then()코드의 확실한 차이점은 아래 코드에서 드러난다.


db.collection("cities").where("state", "==", "CA")
   
.onSnapshot(function(snapshot) {
        snapshot
.docChanges().forEach(function(change) {
           
if (change.type === "added") {
                console
.log("New city: ", change.doc.data());
           
}
           
if (change.type === "modified") {
                console
.log("Modified city: ", change.doc.data());
           
}
           
if (change.type === "removed") {
                console
.log("Removed city: ", change.doc.data());
           
}
       
});
   
});


cities 컬렉션에서 "state" : "CA"값을 갖고 있는 문서의 변경사항이 발생했을 때, "추가", "삭제" , "수정"에 따라 해당하는 기능(console.log)을 실행한다. get().then()메서드와 달리 위의 코드는 마치 vue의 양방향 바인딩처럼 실행된다. 이외에도 local 변경, 메타데이터 변경 이벤트와 같은 기능도 아래 문서에서 확인할 수 있다.


onSnapshot() API문서https://firebase.google.com/docs/firestore/query-data/listen?hl=ko



3 데이터 추가

데이터 추가는 add()와 set()메서드를 사용한다. add()와 set()의 차이점은 다음과 같다. 


1] set() : 문서 추가하기, 문서 덮어쓰기(병합 ), doc(id) 지정 

2] add() : 문서 추가하기, doc(id) 미지정(id자동 생성)


* set() 추가하기

// Add a new document in collection "cities"
db
.collection("cities").doc("LA").set({
    name
: "Los Angeles",
    state
: "CA",
    country
: "USA"
})
.then(function() {
    console
.log("Document successfully written!");
})
.catch(function(error) {
    console
.error("Error writing document: ", error);
});


파이어베이스 공식문서에도 나오듯이 set()과 add()는 거의 같다. set()같은 경우에도 "덮어쓰기 방지"기능을 다음과 같이 구사할 수 있다. { merge: true }를 넣으면 중복되는 데이터는 저장되지 않고, 새로 추가되는 내용만 저장된다. 단, { merge : true }가 없을 때는 그냥 덮어쓰기가 되면서 기존의 데이터가 모두 삭제되기 때문에 주의해야 한다.


db.collection("cities").doc("LA").set({
    name
: "Los Angeles",
    state
: "CA",
    country
: "USA"
},{ merge : true })
.then(function() {
    console
.log("Document successfully written!");
})
.catch(function(error) {
    console
.error("Error writing document: ", error);
});


추가할 수 있는 데이터타입은 '문자열', 'boolean값', '날짜', 'null', '배열', '객체'를 쓸 수 있다.

 

var docData = {
    stringExample
: "Hello world!",
    booleanExample
: true,
    numberExample
: 3.14159265,
    dateExample
: firebase.firestore.Timestamp.fromDate(new Date("December 10, 1815")),
    arrayExample
: [5, true, "hello"],
    nullExample
: null,
    objectExample
: {
        a
: 5,
        b
: {
            nested
: "foo"
       
}
   
}
};
db
.collection("data").doc("one").set(docData).then(function() {
    console
.log("Document successfully written!");
});



* add() 추가하기

db.collection("cities").add({
    name
: "Tokyo",
    country
: "Japan"
})
.then(function(docRef) {
    console
.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
    console
.error("Error adding document: ", error);
});


add()메서드를 사용할 때는 반드시 doc(id)메서드를 제외해야 한다. set()을 사용할 때처럼 doc의 id를 지정해주면 에러가 발생한다는 점을 기억하자. 



4 데이터 수정(Update)

문서를 수정할 때는 위에서 봤듯이 기본적으로 set()메서드를 사용한다.  파이어베이스에는 update()메서드를 따로 제공하는데 굳이 update()를 사용할 필요가 있을까한다. 일단 기본 update()메서드의 사용법은 아래와 같다.


var washingtonRef = db.collection("cities").doc("DC");

// Set the "capital" field of the city 'DC'
return washingtonRef.update({
    capital
: true
})
.then(function() {
    console
.log("Document successfully updated!");
})
.catch(function(error) {
   
// The document probably doesn't exist.
    console
.error("Error updating document: ", error);
});


set()과 거의 동일하며, 중첩된 부분은 다음과 같은 점표기법을 써야하기 때문에 오히려 귀찮게 느껴진다. age와 favorites는 중첩되는 key이므로 "age", "favorites"로 앞뒤에 " "를 붙여줘야한다.


// Create an initial document to update.
var frankDocRef = db.collection("users").doc("frank");
frankDocRef
.set({
    name
: "Frank",
    favorites
: { food: "Pizza", color: "Blue", subject: "recess" },
    age
: 12
});

// To update age and favorite color:
db
.collection("users").doc("frank").update({
   
"age": 13,
   
"favorites.color": "Red"
})
.then(function() {
    console
.log("Document successfully updated!");
});


배열 요소에 대한 업데이트와 같은 추가기능이 있는데, 개인적으로 많이 사용할 일이 없을 듯하다. 


파이어베이스 update문서 : https://firebase.google.com/docs/firestore/manage-data/add-data



5 데이터 삭제(Remove)

Restful API가 그렇듯 삭제는 항상 간단하다. delete()메서드를 활용하면 문서를 쉽게 제거할 수 있다. 


db.collection("cities").doc("DC").delete().then(function() {
    console
.log("Document successfully deleted!");
}).catch(function(error) {
    console
.error("Error removing document: ", error);
});


문서가 아닌 특정필드를 삭제하는 방법은 update()도중에 FieldValue.delete()메서드를 사용한다. 가령, 특정 유저의 댓글을 삭제하는 처리를 한다면, user의 id를 where()문으로 검색한 후에 update()메서드에서 아래와 같은 명령을 내려준다.


var cityRef = db.collection('cities').doc('BJ');

// Remove the 'capital' field from the document
var removeCapital = cityRef.update({
    capital
: firebase.firestore.FieldValue.delete()
});


  

6 응용하기

firestore() DB에 있는 자료를 쿼리문으로 찾아서 특정 부분의 값을 사용자의 새로 지정한 데이터로 update하고, 기존의 DB를 삭제하는 처리를 해보자. 


댓글

최신글 전체

이미지
제목
글쓴이
등록일