Firestore를 처음 써봤는데, 간단하다고 생각했던 문제를 처리하는데 의외로 오래 걸려서 정리해둔다.
하나의 collection에서 데이터 조회한 후에 그 값을 키로 다른 collection에 있는 데이터를 읽어와서 JSON array를 만드는 작업이었다.
아주 간단한 일인데, 두번째 collection의 조회가 끝날 때까지 기다렸다가 json array 만드는 것이 잘 안 됐다.
Collection 1 : channels {
"chname1": {
"group": 1,
"order": 1,
"name": "채널1"
},
"chname2": {
"group": 1,
"order": 2,
"name": "채널2"
}, ....
Collection 2: schedules {
"chname1": {
"title": "프로그램1",
},
"chname2": {
"title": "프로그램2",
}, ...
대략 이렇게 되어 있고, channels 에서 검색하여 각각에 대해 schedules 정보를 merge해서 JSON array 를 만든다고 가정한다.
작업 순서는
1) channels 검색 : db.collection("channels").where("group", "==", 1).orderBy("order", "asc").get()
2) 검색 결과 iterate하며 schedules 검색 db.collection("schedjles").doc(ch.id).get()
3) 검색 결과 합쳐서 JSON array 구성
첫번째 시도했던 코드는 대략 이렇다.
// using express
app.get("/chs", async (req, res) => {
var ret = [];
const chs = await db.collection("channels").where("group", "==", 1).orderBy("order", "asc").get();
chs.forEach(async (ch) => {
const schedule = await db.collection("schedules").doc(ch.id).get();
ret.push({
id: ch.id,
title: schedule.data().title
});
});
return res.json(ret);
});
기대하는 결과는
[{id: "chname1", title:"프로그램1"}, {id: "chname2", title: "프로그램2"}...]
였지만 실제 결과는
[]
chs.forEach() 안의 부분이 실행되기 전에 빈 array를 return해버린 것이다.
forEach() 는 순차적으로 callback을 실행만 할 뿐, callback의 완료를 보장하지 않기 때문이다.
가장 간단한 해결 방법은 forEach()를 for()로 변경하는 것이다.
const ret = [];
const chs = await db.collection("channels").where("group", "==", 1).orderBy("order", "asc").get();
for (const i in chs.docs) {
const doc = chs.docs[i];
const schedule = await db.collection("schedules").doc(doc.id).get();
ret.push({
id: doc.id,
title: schedule.data().title,
});
}
return res.json(ret);
해보는 김에 schedules 조회를 비동기로 처리하고 싶다면 Promise를 사용해서 이렇게도 할 수 있다.
const promises = [];
const chs = db.collection("channels").where("group", "==", 1).orderBy("order", "asc").get();
chs.forEach((ch) => {
promises.push(db.collection("schedules").doc(ch.id).get().then(function(schedule) {
return {
id: ch.id,
title: schedule.data().title,
};
}));
});
Promise.all(promises).then((ret) => {
res.json(ret).end();
}).catch((error) => res.json({"result": error}));