[React/supabase] 변경된 column만 업데이트하기

팀프로젝트에서 게시글 작성, 수정 기능을 메인으로 담당하게 됐다

처음엔 주요 요구사항들 위주로 빠르게 진행하느라 게시글 내용을 저장해둔 여러 column들 중 제목만, 제목이랑 내용만 이런식으로 수정한 부분만 테이블 update하는 부분을 처리하지 않고 넘어갔는데 결국 처리해야하는 순간이 다가왔다 ㅋㅋㅋ

 

supabase를 사용했고, 게시글의 썸네일을 저장하기위해 supabase에서 storage에 저장 -> storage에서 해당 이미지의 publicUrl을 받아오기 -> 그 publicUrl을 posts table에 저장하는 과정을 거쳤는데, 이미지는 수정에 있어 여러 경우로 나뉠 수 있어 수정 페이지에 들어온 첫 순간과 수정을 마무리하는 순간에 파일이 변했는지 확인하는 과정이 필요했다.

 

게시글 수정에서 이미지 첨부파일이 가질 수 있는 경우의 수

1) 이미지를 수정하지 않고 다른 내용만 수정하는 경우

2) 기존 등록돼있는 이미지를 삭제하고 이미지가 없는 상태로 수정을 마무리

3) 새로운 파일을 등록

...

 

아무튼 기존 값과 새로운 입력값을 비교해 일치하지 않는 내용들만 객체에 넣어 update 요청을 보냈고, 당연히 불필요한 column의 업데이트까지 하지 않도록 처리할 수 있게 됐다!

 

const editPost = async (id, prevColumns, newColumns) => {
    const updateColumns = {};

    if (prevColumns.title !== newColumns.title) updateColumns.title = newColumns.title;
    if (prevColumns.tech_stack !== newColumns.tech_stack) updateColumns.tech_stack = newColumns.tech_stack.split(' ');
    if (prevColumns.content !== newColumns.content) updateColumns.content = newColumns.content;
    if (prevColumns.project_start_date !== newColumns.project_start_date)
      updateColumns.project_start_date = newColumns.project_start_date;
    if (prevColumns.project_end_date !== newColumns.project_end_date)
      updateColumns.project_end_date = newColumns.project_end_date;
    if (prevColumns.thumbnail !== newColumns.thumbnail)
      updateColumns.thumbnail_url = await getImageURL(newColumns.thumbnail, 'thumbnails');

    const { error: tableError } = await supabase.from('DEV_POSTS').update(updateColumns).eq('post_id', id).select();

    if (tableError) {
      console.log('🚀 ~ addPosts ~ tableError:', tableError);
    } else {
      await fetchPosts();
      alert('프로젝트가 정상적으로 등록되었습니다.');
    }
  };

* 현재 프로젝트에서는 context에 등록된 모든 post들을 담아 관리하기 때문에 등록/수정/삭제가 일어날 때 모두 fetch해주지만, 실제 배포하는 대형 프로젝트의 경우에는 한번 글을 썼을 때 백만개의 데이터가 fetch될 수도 있기 때문에 데이터를 보여주는 곳에서 중요도/개수 등에따라 데이터를 받아오는 방법이 달라진다고 한다.

 

ex. 인스타그램 좋아요 수 같이 크게 실시간 수치가 중요하지 않은 경우: 네트워크 요청을 바로바로 주고받지않고 우선 유저의 눈에 변화가 보이도록 하고 -> 처리 요청은 보내둠

ex. 실시간 데이터는 중요하지만 양이 방대한 경우: 페이지네이션, 무한스크롤 등을 활용해 우선 nn개만 받아옴 -> 다음 데이터들을 보여줘야할 때 다시 nn개를 받아옴

 

getImageURL 함수는 내가 이미지를 저장하고 URL을 받아오는 기능을 만들면서, 추후 다른 팀원들도 해당 기능이 필요할 때 사용할 수 있도록 하기위해 util 폴더에 따로 만들어두었다

유저 인증을 담당한 팀장님이 추가로 프로필사진을 구현하기로 했는데, 이 기능과 이미지 입력을 받기위해 커스텀해둔 ImageInput 컴포넌트를 사용하셔서 좀 뿌듯했다.. 다양하게 사용할 수 있는 공용 컴포넌트를 만들어보고싶었는데 조금 더 감을 잡을 수 있게된 것 같다..!

 

import supabase from '../supabaseClient';

/**
 * 이미지를 supabase storage에 업로드 후, publicUrl을 반환
 * @param {file} uploadImg - URL을 얻을 이미지 file, input[type="file"]로 받아온 값
 * @param {string} bucket - 사진이 저장될 bucket, 지정되지 않는 경우 images bucket에 저장
 * @param {string} folder - 사진이 저장될 폴더, 지정되지 않는 경우 public 폴더에 저장
 * @returns {string} uploadImg의 URL
 */
export const getImageURL = async (uploadImg, bucket, folder) => {
  let imageUrl = null;
  const imgName = `${Date.now()}-${crypto.randomUUID()}`;
  const imgPath = `${folder || 'public'}/${imgName}`;

  if (!uploadImg) return imageUrl;

  try {
    const { data: uploadData, error: uploadError } = await supabase.storage
      .from(`${bucket || 'images'}`)
      .upload(imgPath, uploadImg);

    if (uploadError) {
      console.error('🚀 ~ getImageURL ~ uploadError:', uploadError);
      return imageUrl;
    }

    const { data: urlData, error: urlError } = supabase.storage
      .from(`${bucket || 'images'}`)
      .getPublicUrl(uploadData.path);

    if (urlError) {
      console.error('🚀 ~ getImageURL ~ urlError:', urlError);
      return imageUrl;
    }

    imageUrl = urlData.publicUrl;
  } catch (error) {
    console.log('🚀 ~ getImageURL ~ error:', error);
  }

  return imageUrl;
};