🎁 모든 강의 30% + 무료 강의 선물🎁

[인프런 워밍업 스터디 클럽 3기 풀스택] 4주차 발자국

[인프런 워밍업 스터디 클럽 3기 풀스택] 4주차 발자국

강의수강

supabase Auth

  • 다양한 인증방식을 지원하는 인증 시스템

  • 이메일 인증, 소셜 로그인 등 다양한 방법을 지원

  • confirmation URL 방식

    • supabase가 사용자의 이메일로 확인 링크를 전송

    • 사용자가 해당 링크를 클릭하면, supabase에서 해당 이메일을 확인하고 인증 완료

  • 6 - Digit OTP 방식

    • 이메일이나 SMS를 통해 6자리 숫자를 전송

    • 사용자는 이 코드를 입력하여 본인 확인

    supabase Realtime

  • 데이터베이스의 변경 사항을 실시간으로 클라이언트에 전달하는 기능을 제공

  • broadcast

    • 모든 사용자에게 동일한 데이터를 전송하는 방식

  • presence

    • 현재 연결된 사용자를 실시간으로 추적하는 방식

  • postgres changes

    • 데이터베이스에서 발생하는 변경 사항을 실시간으로 추적하는 방식

supabase RLS

  • Row Level Security

  • 데이터베이스 테이블에 대해 구체적으로 접근 권한을 설정할 수 있게 해줌

 

미션

채팅 메시지 삭제

  • 사용자가 특정 채팅 메시지를 삭제

    • 다른 사용자가 작성한 메시지는 삭제 불가능

    • 메시지 클릭 시 모달 화면을 띄어 삭제 여부 결정

  • 삭제된 메시지 대신 "이 메시지는 삭제되었습니다" 같은 알림 표시

     

     

    메시지 삭제 기능은 message 테이블의 is_deleted 컬럼 값을 이용하기로 했다. is_deleted 가 true 이면 삭제된 메시지고, false면 삭제되지 않은 메시지다.

     

    구현 전 생각했던 방식은 is_deleted 값을 변경하는 action 함수를 만들고, 특정 메시지를 클릭 시 모달 창을 띄어 해당 메시지를의 삭제 여부를 묻는다. 삭제한다고 하면 action 함수를 호출해 is_deleted 값을 변경한다.
    또한 상대방 채팅에서도 실시간으로 변경하기 위해 channel에서 update event도 구독한다.

/actions/chat-actions.ts

export async function deleteMessage({ message }) {
  const supabase = await createServerSupabaseClient();

  const {
    data: { session },
    error,
  } = await supabase.auth.getSession();

  if (error || !session.user) {
    throw new Error("User is not authenticated");
  }

  const { error: updateError } = await supabase
    .from("message")
    .update({
      ...message,
      is_deleted: true,
    })
    .eq("id", message.id);

  if (updateError) {
    throw new Error("Message error");
  }
}

export async function getMessage(id: string) {
  if (!id) {
    return null;
  }
  const supabase = await createServerSupabaseClient();

  const { data, error } = await supabase
    .from("message")
    .select("*")
    .eq("id", Number(id))
    .maybeSingle();

  if (error) {
    return null;
  }

  return data;
}

deleteMessage: 메시지 객체를 받아 is_deleted 컬럼의 값을 true 변경

getMessage: 모달 창에서 이용할 action message의 id를 받아 해당 id로 message 테이블을 쿼리해 메시지를 반환

 

/components/chat/chat-delete-button.tsx

"use client";

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { deleteMessage } from "actions/chat-actions";
import { useRouter } from "next/navigation";
import { useRecoilValue } from "recoil";
import { selectedUserIdState } from "utils/recoil/atoms";

export default function ChatDeleteButton({ message }) {
  const queryClient = useQueryClient();

  const selectedUserId = useRecoilValue(selectedUserIdState);

  const deleteMessageMutaition = useMutation({
    mutationFn: () => deleteMessage({ message }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["messages", selectedUserId] });
    },
  });

  const router = useRouter();

  return (
    <div className="flex flex-col gap-1 font-bold bg-white border-2 px-6 pt-8 py-4 rounded-md">
      <p>
        message : <span className="underline">{message.message}</span>
      </p>
      <p>Are you sure you want to delete this message?</p>
      <div className="flex gap-2 justify-center py-4">
        <button
          onClick={() => {
            deleteMessageMutaition.mutate();
            router.back();
          }}
          className="w-14 py-1 px-2 bg-red-100 rounded-md border-2"
        >
          yes
        </button>
        <button
          onClick={() => router.back()}
          className="w-14 py-1 px-2 bg-blue-100 rounded-md border-2"
        >
          no
        </button>
      </div>
    </div>
  );
}

모달 창에서 렌더링할 컴포넌트 yes 버튼은 deleteMessageMutation.mutate()를 호출해 deleteMessage action을 호출한다. yes, no 버튼 마지막에 router.back()을 호출해 모달창을 종료한다.

/components/chat/chat-screen.tsx

  useEffect(() => {
    const channel = supabase
      .channel("message_postgres_changes")
      .on(
        "postgres_changes",
        { event: "INSERT", schema: "public", table: "message" },
        (payload) => {
          if (
            payload.eventType === "INSERT" &&
            !payload.errors &&
            !!payload.new
          ) {
            getAllMessagesQuery.refetch();
          }
        }
      )
      .on(
        "postgres_changes",
        { event: "UPDATE", schema: "public", table: "message" },
        (payload) => {
          if (payload.eventType === "UPDATE" && !payload.errors) {
            getAllMessagesQuery.refetch();
          }
        }
      )
      .subscribe();

    return () => {
      channel.unsubscribe();
    };
  }, []);

chat-screen.tsx 컴포넌트에 supabase.channel 구독에서 { event: 'UPDATE' }를 추가함으로 message 테이블에 변경사항이 있으면 모든 메시지를 리패치하도록 함

 

 

댓글을 작성해보세요.


채널톡 아이콘