[Excalidraw] 커스텀 버튼으로 게시글 등록하기 - supabase RLS 에러 해결하기

voyager 999·2024년 3월 25일

Next.js

목록 보기
8/9

드디어 오늘 발표까지 완료된 심화주차 프로젝트! 프로젝트 끝날 때마다 느끼는 건데. 정말 이걸 기한 내에 어떻게 다 할 수 있을까 걱정이 되다가도 차근차근 하다보면 어느 새 제출을 할 수 있을 정도만큼은 완성이 되어가는 것이 신기하다.

🎨내가 맡은 그림그리기 기능

앞선 글에서 작성한 것처럼 Excalidraw 라이브러리를 사용하여 그림을 그리고 게시글을 등록할 수 있는 페이지를 만들었다. 아래는 '등록' 버튼에 걸려 있는 온클릭 함수인데, 코드가 상당히 길기 때문에 조금씩 뜯어서 살펴보자.

  • 데이터 유효성 검사
  /** 게시글 등록 버튼 클릭 핸들러 */
  const handleOnClick = async () => {
    if (!title || !description) {
      alert(`제목과 내용을 입력해주세요.`);
      return;
    }

  • Excalidraw 에디터에서 그림 데이터 가져와서 보낼 준비하기
    try {
      if (!excalidrawAPI) return;
      const created_at = new Date().toISOString();
      const timestamp = new Date().getTime();
      const fileName = `image_${timestamp}.png`;

      const blob = await exportToBlob({
        elements: excalidrawAPI.getSceneElements(),
        mimeType: "image/png",
        appState: {
          ...initialData.appState,
        },
        files: excalidrawAPI.getFiles(),
      });

ExcalidrawAPI가 있다면(제대로 Excalidraw 패키지가 설치되었고, 컴포넌트를 사용하고 있다면) 함수를 실행한다. 아래의 3가지 데이터를 만들어둔다.

(1) 등록시 날짜 정보 : 게시글을 등록할 때 createdat 으로 활용된다.
(2) 등록시 시간 정보 : supabase 스토리지에 이미지를 업로드할 건데, 파일명이 겹치면 안되므로 등록 시 시간 정보를 붙여 유니크한 파일명을 만들어 업로드할 때 사용한다.
(3) 파일명 : 그래서 파일명은 'image
시간정보.png'로 저장될 것이다.

Excalidraw 에디터에서 그린 그림 정보를 가져와서 blob 형태로 구성한다. 사실 이 부분도 많이 어려웠는데 수수께끼 같았던 공식 문서와 예제 사이트를 참고하여 얼레벌레 작성할 수 있었다.

(1) elements : 에디터에 그려진 그림 정보. 예를 들어 선, 도형, 텍스트 등에 관한 것이다. exportToBlob() 함수는 이 요소들을 이용하여 그림을 Blob 형태로 내보낸다.
(2) mimeType : blob의 파일 타입과 확장자
(3) appState : Excalidraw 기본 설정 파일. 특히 initialData가 바로 그것.
(4) files : 사용자가 에디터에 삽입한 미디어 파일을 가리킨다. 예를 들어 위에 있는 그림에서처럼 직접 그린 게 아니라 다른 곳에서 이미지를 복사해 와서 에디터에 붙여넣기 할 수 있는데, 이렇게 첨부된 파일들이 배열 형태로 구성되어 있고, excalidrawAPI.getFiles()를 통해 얻을 수 있다.


  • 준비된 blobsupabase 스토리지에 업로드하기
      const file = new File([blob], "name");
      const imgUrl: string | unknown = await uploadImageToStorage(
        file,
        fileName
      );

      if (typeof imgUrl !== "string") {
        console.error(`이미지 URL이 유효하지 않습니다.`);
        return;
      }

Blob은 위에서 미리 만들어 둔 파일명을 달고 프로젝트에 연결된 supabase의 스토리지로 업로드될 것이다. 스토리지에 이미지가 업로드 될 때까지 기다렸다가, 업로드된 이미지의 주소를 받아서 imgUrl 이름으로 활용할 것이다.

      const newPost: Posts = {
        title,
        description,
        created_at,
        drawing_url: imgUrl,
        painter_email: currentUserEmail,
        likes: 0,
        chooseTheme,
      };
      insertMutation.mutate(newPost, {
        onSuccess: (updatedPost) => {
          queryClient.invalidateQueries({ queryKey: ["posts"] });
          router.replace(`/detail/${updatedPost.drawing_id}`);
        },
      });
    } catch (error) {
      console.error(`그림 업로드 중 오류 발생:`, error);
    }
  };

게시글의 제목, 내용, 주제선택 input도 있으니 해당 정보까지 싹다 모아서 newPost를 구성해준다. 그런다음 mutation을 호출하여 이 newPost를 "posts" 테이블에 insert하는 로직이다.

🥲이렇게 작성하면 작동이 되어야 할 것이다. 그런데...외않되

여기까지 코드를 작성하고 로직을 꼼꼼히 몇 번이나 다시 읽어보고 수정해보고 하는데, 뭔가 좀 이상하다. 그 어떤 코드를 실행하든지 간에 똑같은 오류가 하 루 종 일 뜨는 것이다. 진짜 하루종일 그 동안 CRUD 중에서 C가 제일 까다로울 구석이 없는 부분이라고 생각했는데 4~5시간 동안 똑같은 부분을 해결하지 못하고 제자리걸음 하니 정말 도라버릴 지경이었다🥲🥲🥲 403 error new row violates row-level security policy 빨간 에러가 계속 뜨고 있었다.

마침 테이블 관련 기능을 담당하고 있는 같은 팀원분이 테이블을 생성하고 설정할 때 RLS 에러가 있어 데이터를 가져오지 못했던 것에 대해 말씀해 주셨던 것이 기억났다. 스토리지도 같은 방법으로 하면 되겠지 하고 생각했다.

스토리지의 Policies 메뉴에서 New policy -> Get started Quickly 를 선택하여 모두가 이 스토리지에 접근 및 등록할 수 있도록 새로운 권한 설정을 추가해주었다.

그런데 안된다.... 빨간 403 에러가 계속해서 나타난다. 테이블 RLS 권한 설정과 똑같은 방법으로 스토리지 설정을 해줬는데도 문제가 해결되지 않으니 혹시 코드에 문제가 있는건가, Blob을 업로드 하려고 시도해서 안 되는건가 등등 한참을 고민했다. 이 방법 저 방법 다 해봐도 안되고 혹시 Excalidraw에서 데이터 가져오는 게 문제가 되는 건가 싶어서 그냥 평범하게 file input을 사용해서 로컬에서 이미지 파일을 선택해서 스토리지에 업로드하는 방식으로 바꿔봤는데도 403 403 403 403...🚨🚨🚨

결론적으로는 코드의 문제가 아니었다. 참 에러 메시지가 정직하게 권한 설정에 오류가 있다고 일관성 있게 알려 주고 있었는데...

👽4시간 씨름하고 겨우 문제의 원인을 알게 되었을 때

위에서 Get started Quickly 메뉴를 클릭하면 그림에서 보이는 것처럼 미리 작성된 Policy SQL template가 있다.

제목에 "익명의(인증되지 않은) 유저들을 / 퍼블릭 폴더 안에 있는 / JPG 이미지들에 접근할 수 있게 해준다"고 쓰여 있다. 왜 이게 그때는 보이지 않았을까........ 여튼 당시에는 여기 쓰여 있는 코드들을 하나하나 자세히 뜯어볼 생각은 않고, 우리는 png 확장자를 다룰 거니까 jpg를 png로 고치면 되겠지 하고 안일하게 생각했다.

알고보니 여기 쓰여 있는 코드가 모두 제한사항이었다.

for full customization 메뉴를 선택해준다.

SELECT / INSERT / UPDATE / DELETE 모두 선택해준다. Policy Definition에는 권한을 설정하고자 하는 스토리지 버킷의 id만 명시되어 있고 아무 것도 없다.

아무 제한 사항도 없다 -> 즉, 모든 사용자가 자유롭게 스토리지에 접근 및 등록할 수 있게 되는 것이다.
이렇게 스토리지 버킷 권한 설정을 해주었더니 지긋지긋한 403 에러는 감쪽같이 사라졌다.

그... 그래도 해결은 했으니까....!!

0개의 댓글