[Next.js][프로젝트] Pocket Art (2) - 팬아트 CRUD, 마이페이지 UI 구현

우엥·2024년 7월 12일
post-thumbnail

Create

import supabase from '@/supabase/supabase';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const data = await request.formData();
  const imageFile = data.get('imageFile') as File;
  const content = data.get('content') as string;
  const postId = data.get('postId') as string;
  const writerId = data.get('writerId') as string;

  const extension = imageFile.name.split('.').slice(-1)[0];
  const filename = `/${crypto.randomUUID()}.${extension}`;

  const { data: imageFileData, error: imageFileError } = await supabase.storage
    .from('fanArts')
    .upload(filename, imageFile);

  if (imageFileError) {
    throw new Error(imageFileError.message);
  }

  const fanArtURL = imageFileData?.fullPath;

  const { error: insertError } = await supabase.from('FanArts').insert({ content, fanArtURL, postId, writerId });

  if (insertError) {
    throw new Error(insertError.message);
  }

  return NextResponse.json('팬아트 리뷰 등록 완료!');
}
  • 팬아트 추가 시, 무슨 포켓몬의 팬아트인지 포켓몬 이름도 같이 table에 저장되면 좋을 것 같다.
  • 현재는 포켓몬 도감 번호만 저장하고 있는데, 이 경우 포켓몬 API 호출을 해야지만 포켓몬의 이름을 가져올 수 있어서 이름도 팬아트 table에 같이 저장되는 게 나중에 포켓몬 이름을 알아야 하는 상황에 더 빠르게 데이터를 불러올 수 있을 것 같다.

 

Read

import { itemCountPerPage } from '@/components/FanArtSection/FanArtSection';
import supabase from '@/supabase/supabase';
import { FanArt } from '@/types/FanArt.type';
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest, page: number) {
  const { searchParams } = new URL(request.url);
  const postId = searchParams.get('postId') as string;

  if (!postId) {
    return NextResponse.json({ error: 'postId is required' }, { status: 400 });
  }
  const start = (page - 1) * itemCountPerPage;
  const end = itemCountPerPage * page - 1;

  const { count } = await supabase.from('FanArts').select('id', { count: 'exact', head: true }).eq('postId', postId);
  const { data, error } = await supabase
    .from('FanArts')
    .select('*')
    .eq('postId', postId)
    .order('id', { ascending: false })
    .range(start, end);

  if (error) {
    throw new Error(error.message);
  }

  const fanArts: FanArt[] = await Promise.all(
    data.map(async (fanArt): Promise<FanArt> => {
      const { data: user, error: userError } = await supabase.from('Users').select('*').eq('id', fanArt.writerId);

      if (userError) {
        throw new Error(userError.message);
      }

      return {
        id: fanArt.id,
        content: fanArt.content,
        fanArtURL: 'https://wixafbbadrjlqppqupbt.supabase.co/storage/v1/object/public/' + fanArt.fanArtURL,
        createdAt: fanArt.createdAt.slice(0, 10),
        user: {
          id: user[0].id || '',
          nickname: user[0].nickname || 'writer',
          profileURL: user[0].profile_img
        }
      };
    })
  );

  return NextResponse.json({ fanArts, count });
}
  • 팬아트 저장 시 포켓몬 이름도 같이 저장할 거면 FanArt Type도 수정해 줘야 한다.

 

Update

import supabase from '@/supabase/supabase';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const data = await request.formData();
  const imageFile = data.get('imageFile') as File;
  const content = data.get('content') as string;
  const id = Number(data.get('id') as string);
  const prevFanArtURL = data.get('fanArtURL') as string;

  const filename = prevFanArtURL.split('/').slice(-1)[0];

  const { error: imageDeleteError } = await supabase.storage.from('fanArts').remove([filename]);

  if (imageDeleteError) {
    throw new Error(imageDeleteError.message);
  }

  const extension = imageFile.name.split('.').slice(-1)[0];
  const newFilename = `/${crypto.randomUUID()}.${extension}`;

  const { data: imageUploadData, error: imageUploadError } = await supabase.storage
    .from('fanArts')
    .upload(newFilename, imageFile);

  if (imageUploadError) {
    throw new Error(imageUploadError.message);
  }

  const fanArtURL = imageUploadData?.fullPath;

  const { error: updateError } = await supabase.from('FanArts').update({ content, fanArtURL }).eq('id', id);

  if (updateError) {
    throw new Error(updateError.message);
  }

  return NextResponse.json('팬아트 수정 완료!');
}

 

Delete

import supabase from '@/supabase/supabase';
import { NextRequest, NextResponse } from 'next/server';

export async function DELETE(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const id = Number(searchParams.get('id'));

  const { data, error: selectError } = await supabase.from('FanArts').select().eq('id', id);
  const imageURL: string = data?.[0].fanArtURL.split('/').slice(-1)[0] || '';

  if (selectError) {
    throw new Error(selectError.message);
  }

  const { error: storageError } = await supabase.storage.from('fanArts').remove([imageURL]);

  if (storageError) {
    throw new Error(storageError.message);
  }

  const { error: deleteError } = await supabase.from('FanArts').delete().eq('id', id);

  if (deleteError) {
    throw new Error(deleteError.message);
  }

  return NextResponse.json('팬아트가 삭제되었습니다');
}

 

마이페이지 UI 구현

  • CRUD하다가 CSS 하니까 재미땅ㅎ
profile
🌸😊🌸

0개의 댓글