느려도 한걸음씩

Next.js 토이 프로젝트 (8) - 게시물 삭제 기능 구현 본문

토이프로젝트/find side(가제)

Next.js 토이 프로젝트 (8) - 게시물 삭제 기능 구현

hoj0806 2025. 5. 8. 21:40

 

이번 포스트에서는 게시물 삭제 기능을 구현해보겠습니다.


단순히 데이터를 삭제하는 것을 넘어, 본인 소유의 게시물만 삭제할 수 있도록 보안 정책을 적용하고, UI에서도 조건부로 삭제 버튼을 노출하는 흐름까지 함께 살펴볼게요.

 

 

 

✅ Step 1. Supabase RLS 설정

먼저 posts 테이블에서 본인 소유의 게시물만 삭제 가능하도록 Supabase에서 RLS(행 수준 보안, Row-Level Security)를 설정합니다.

CREATE POLICY "Users can delete their own posts"
ON posts
FOR DELETE
USING (auth.uid() = user_id);

 

  • auth.uid()는 현재 로그인한 사용자의 고유 ID입니다.
  • user_id는 게시물을 작성한 사용자의 ID입니다.
  • 즉, 두 값이 일치할 경우에만 삭제가 허용됩니다.

⚠️ RLS가 비활성화 되어 있다면, 먼저 ENABLE ROW LEVEL SECURITY ON posts; 명령으로 활성화해주세요.

 

 

✅ Step 2. 서버 액션 함수 작성

이제 실제 게시물을 삭제하는 서버 함수 deletePost()를 작성합니다.

'use server';

import { createClient } from '@/utils/supabase/server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

export async function deletePost(formData: FormData) {
  const supabase = await createClient();
  const post_id = formData.get('post_id')?.toString();

  // 1. 로그인된 유저 정보 확인
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (!user || userError) {
    console.error("❌ 로그인된 유저가 없습니다:", userError?.message);
    return;
  }

  // 2. 해당 게시물이 현재 유저의 것인지 확인
  const { data: post, error: postError } = await supabase
    .from("posts")
    .select("user_id")
    .eq("id", post_id)
    .single();

  if (postError) {
    console.error("❌ 게시글 조회 실패:", postError.message);
    return;
  }

  if (post.user_id !== user.id) {
    console.error("❌ 이 게시글을 삭제할 권한이 없습니다.");
    return;
  }

  // 3. 게시물 삭제 요청
  const { error: deleteError } = await supabase
    .from("posts")
    .delete()
    .eq("id", post_id);

  if (deleteError) {
    console.error("❌ 게시글 삭제 실패:", deleteError.message);
    return;
  }

  console.log("✅ 게시글 삭제 성공!");

  // 4. 메인 페이지로 리디렉션
  revalidatePath("/");
  redirect("/");
}

 

🔒 비록 Supabase에서 RLS로 보안을 걸었더라도, 프론트엔드에서 한번 더 확인해주는 습관은 보안적으로 매우 중요합니다.

 

 

 

 

✅ Step 3. 클라이언트에서 삭제 버튼 조건부 렌더링

게시물 상세 페이지에서는 현재 로그인한 유저와 게시글 작성자 ID가 일치할 때만 삭제 버튼이 보이도록 합니다.

const supabase = await createClient();
const {
  data: { user },
} = await supabase.auth.getUser();

const isAuthor = user?.id === post.user_id;

 

 

그 후, 조건부 렌더링을 통해 삭제 버튼을 노출합니다

{isAuthor && (
        <form action={deletePost} className='absolute top-4 right-4'>
          <input type='hidden' name='post_id' value={params.postId} />
          <button
            type='submit'
            className='text-sm bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600'
          >
            삭제
          </button>
        </form>
      )}

 

 

게시물 삭제 테스트

 

 

 

✅ 정리

  • Supabase에서 RLS 정책으로 본인만 게시물 삭제 가능하도록 설정
  • 서버 액션에서 유저 검증 → 게시글 검증 → 삭제 → 리디렉션
  • UI에서는 작성자일 때만 삭제 버튼 표시