먼저 말씀 드리자면 Firestore에는 배열의 요소를 수정하는 기능이 없습니다.
기존의 배열 내 요소를 수정하기 위해서는 firestore의 FieldValue에 arrayRemove와 arrayUnion 두가지를 사용하여 배열을 수정할 수 있습니다.
arrayRemove가 삭제하려는 동일한 객체를 참조하도록 만들고, 수정하려는 새 객체를 만들어 arrayUnion 메서드로 삽입해야 합니다.
출퇴근을 기록하는 로직으로 배열 내 요소를 수정하는 방법에 대해 알아보겠습니다.
module.exports = async function(req,res) {
const { entrance,date,email } = req.body;
const attendanceRef = admin.firestore().collection('Attendance').doc(`${email}`);
const obj = {
date: date,
"entrance": entrance,
createdAt:admin.firestore.Timestamp.now()
}
await attendanceRef.set({
attendance: admin.firestore.FieldValue.arrayUnion(obj),
},{ merge:true })
const attendance = await attendanceRef.get();
const data = attendance.data()
return await res.json({ message: '출근시간이 저장되었습니다.', attendance: data });
}
위 스크립트는 출근 시간과 근무 일자를 받아온 뒤 파이어 스토어에 저장하는 스크립트입니다.
attendance는 여러 출/퇴근 날짜를 객체로 기입해야 하기에 배열로 작성하였습니다.
이제 똑같은 근무일에, 퇴근시간 fieldValue만 추가하여 업데이트하려 합니다.
처음에도 말씀드렸다시피, 배열 내 요소만 수정하는 기능은 현재 fireStore에 존재하지 않습니다.
기존의 요소를 삭제한 뒤, 새로운 요소를 입력해야 합니다.
const { leave,date,email } = req.body;
const attendanceRef = admin.firestore().collection('Attendance').doc(`${email}`);
const attendanceData = await attendanceRef.get();
// 현재 attendance fieldValue를 가져옵니다.
const attendanceRecord = attendanceData.data().attendance;
// client로 입력되는 date와 attendance 배열 내 date가 같은 객체를 찾아 Target으로 잡습니다.
const removeTarget = attendanceRecord.find((item) => item.date === date);
// arrayRemove를 통해 해당 객체를 배열에서 삭제합니다.
await attendanceRef.update({
attendance: admin.firestore.FieldValue.arrayRemove(removeTarget),
})
// removeTarget을 참조하여 기존의 fieldValue를 받고, 퇴근 시간인 "leave"를 클라이언트로 받아
// 다시 객체를 생성합니다.
const obj = {
...removeTarget,
"leave": leave,
}
// set 해줍니다. merge를 넣지 않으면 덮어씌우면서 한개의 값만 남으니 꼭 merge 해주세요.
await attendanceRef.set({
attendance: admin.firestore.FieldValue.arrayUnion(obj),
},{ merge: true })
// 새로운 배열을 client로 보내줍니다.
const newAttendance = await attendanceRef.get();
const newAttendanceRecord = newAttendance.data().attendance;
return await res.json({
message: '퇴근시간이 저장되었습니다.',
attendance:newAttendanceRecord
});
정상적으로 실행되지만, 만약 클라이언트나 서버쪽의 오류로 인해 update나 set메서드를 실행하다가 중간에 멈출 수 있습니다.
update 실행 중 멈추면 문제가 없으나, set 메서드를 실행하면서 오류가 발생한다면, update 메서드만 실행되어 요소만 삭제되고 기록이 남지 않게됩니다.
두가지 메서드 중 한가지가 실패하면 전체가 실행되지 않게끔 묶어 줄 필요가 있습니다.
이럴 때에는 batch 메서드를 사용할 수 있습니다.
const admin = require('firebase-admin');
module.exports = async function(req,res) {
const { leave,date,email } = req.body;
const batch = admin.firestore().batch();
const attendanceRef = admin.firestore().collection('Attendance').doc(`${email}`);
const attendanceData = await attendanceRef.get();
const attendanceRecord = attendanceData.data().attendance;
const removeTarget = attendanceRecord.find((item) => item.date === date);
batch.update(
attendanceRef,{ attendance: admin.firestore.FieldValue.arrayRemove(removeTarget) }
)
const obj = {
...removeTarget,
"leave": leave,
}
batch.set(
attendanceRef, { attendance: admin.firestore.FieldValue.arrayUnion(obj) },{ merge: true }
)
await batch.commit();
const newAttendance = await attendanceRef.get();
const newAttendanceRecord = newAttendance.data().attendance;
return await res.json({message: '퇴근시간이 저장되었습니다.',attendance:newAttendanceRecord});
}
create,update,delete,set 하는 로직을 batch로 덮어준 뒤 마지막에 commit을 통해 작업에 안정성을 더할 수 있습니다.