[Next.js]글 작성 및 이미지 업로드 기능 구현

전유덕·2024년 8월 17일
0
post-thumbnail

개요

부트캠프 과정 중 파이널 프로젝트로 Next.js를 사용했었습니다. 개발했던 기능 중 게시물등록에 대해 학습차 기록하려 합니다.
주요기능은 텍스트와 이미지 등록으로, 이미지는 S3에 업로드 한 후 Url을 응답값으로 받아 그 값만 db에 저장하는 형식으로 구현했습니다.

이미지 파일 유효성 검사

const validateFile = (file: File): boolean => {
  // 허용되는 이미지 확장자 리스트
  const allowedExtensions = ["png", "jpg", "jpeg", "gif"];
  
  // 파일 크기 제한: 10MB
  const maxSize = 10 * 1024 * 1024; // 10MB

  // 파일 확장자를 추출하고 소문자로 변환
  const extension = file.name.split(".").pop()?.toLowerCase();

  // 확장자가 없거나 허용된 확장자가 아닌 경우
  if (!extension || !allowedExtensions.includes(extension)) {
    alert("이미지는 PNG, JPG, GIF 형식만 업로드 가능합니다.");
    return false;
  }

  // 파일 크기가 최대 허용 용량을 초과하는 경우
  if (file.size > maxSize) {
    alert("파일 크기는 최대 10MB를 넘을 수 없습니다.");
    return false;
  }

  // 모든 유효성 검사를 통과한 경우 true 반환
  return true;
};

}

이미지 파일의 확장자와 크기를 검사하여 허용되지 않은 파일 형식 또는 크기가 용량제한을 초과한 파일은 업로드하지 않도록 처리합니다.

이미지 업로드 처리

const handleImageChange = async (
  event: React.ChangeEvent<HTMLInputElement>
) => {
  // input 요소에서 선택한 파일을 가져옵니다.
  const files = event.target.files;

  if (files && files.length > 0) {
    // 유효한 파일만 필터링합니다.
    const validFiles = Array.from(files).filter(validateFile);
    if (validFiles.length === 0) return;

    // 폼 데이터를 생성하고, 선택한 파일을 추가합니다.
    const formData = new FormData();
    validFiles.forEach((file) => {
      formData.append("files", file);
    });

    try {
      // 서버로 폼 데이터를 전송합니다.
      const response = await fetch("https://711.ha-ving.store/attach", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        // 서버로부터 응답 데이터를 JSON 형식으로 받아옵니다.
        const data = await response.json();

        // 이미지 URL 리스트를 업데이트합니다.
        const newUrls = data.map((item: { url: string }) => item.url);
        setImageUrls((prev) => [...prev, ...newUrls]);
      } else {
        console.error("이미지 업로드 실패");
      }
    } catch (error) {
      console.error("에러 발생:", error);
    }
  }
};

파일 선택 이벤트 발생 시, 유효성 검사를 거친 파일들을 서버에 업로드합니다. 서버로부터 이미지 URL을 받아오고, 이를 상태에 저장한 후 글 등록시 서버로 전송합니다.

이미지 삭제

const handleImageDelete = (url: string) => {
  setImageUrls(prev => prev.filter(image => image !== url))
}

이미지 삭제 버튼 클릭 시 해당 이미지를 URL 목록에서 제거합니다.

글 작성과 수정

const handleUpdate = async (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault(); // 폼 제출 기본 동작 방지

  const formData = new FormData(e.currentTarget); // 폼 데이터를 가져옵니다.
  const category_id = selectedType; // 선택된 카테고리 ID
  const title = formData.get("title"); // 제목 필드 값
  const content = formData.get("content"); // 내용 필드 값
  const visible = true; // 게시물 공개 여부 (true: 공개)
  
  // 서버로 보낼 데이터를 준비합니다.
  const updatedItem: Partial<{
    category_id: number;
    title: FormDataEntryValue | null;
    content: FormDataEntryValue | null;
    visible: boolean;
    image_urls: string[];
    id?: number;
  }> = {
    category_id: category_id,
    title: title,
    content: content,
    visible: visible,
    image_urls: imageUrls, // 업로드된 이미지의 URL 배열
  };

  // 새 게시물 작성 또는 기존 게시물 수정에 따라 URL과 메서드를 설정합니다.
  let url = `https://711.ha-ving.store/boards/${category.value}/`;
  let method = "POST";

  if (currentPost) {
    // 수정 시에는 PUT 메서드를 사용하고, URL에 게시물 ID를 포함합니다.
    url = `https://711.ha-ving.store/boards/${category.value}/${currentPost.id}`;
    method = "PUT";
    updatedItem.id = currentPost.id;
  }

  try {
    // 서버에 데이터 전송
    const response = await fetch(url, {
      method: method,
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_ACCESS_TOKEN", // 인증 토큰 추가
      },
      body: JSON.stringify(updatedItem), // JSON 형식으로 데이터 전송
    });

    if (response.ok) {
      const responseData = await response.json();

      // 게시물 작성 또는 수정 후 상세 페이지로 리디렉션
      if (currentPost) {
        router.push(`/boards/${category.value}/${updatedItem.id}`);
      } else {
        router.push(`/boards/${category.value}/${responseData.data.id}`);
      }
    } else {
      console.error("게시물 등록을 실패했습니다.");
    }
  } catch (error) {
    console.error("에러 발생:", error);
  }
};

사용자가 폼을 제출하면, 새로운 게시물을 생성하거나 기존 게시물을 수정합니다. 이때 서버에 데이터를 전송하고, 성공 시 해당 게시물의 상세 페이지로 리디렉션됩니다.

profile
zi존 개발자 되고싶다ㅏㅏ(훈수 대환영!)

0개의 댓글