firestore) 배열 수정하는 방법 (cloud function)

김명성·2023년 2월 8일

먼저 말씀 드리자면 Firestore에는 배열의 요소를 수정하는 기능이 없습니다.

기존의 배열 내 요소를 수정하기 위해서는 firestore의 FieldValue에 arrayRemove와 arrayUnion 두가지를 사용하여 배열을 수정할 수 있습니다.

arrayRemove가 삭제하려는 동일한 객체를 참조하도록 만들고, 수정하려는 새 객체를 만들어 arrayUnion 메서드로 삽입해야 합니다.

출퇴근을 기록하는 로직으로 배열 내 요소를 수정하는 방법에 대해 알아보겠습니다.

  1. 출근 기록
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는 여러 출/퇴근 날짜를 객체로 기입해야 하기에 배열로 작성하였습니다.


  1. 퇴근 작성

이제 똑같은 근무일에, 퇴근시간 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을 통해 작업에 안정성을 더할 수 있습니다.

0개의 댓글