본문 바로가기
Next.js

[Next.js] 이미지 업로드하기 (Nest.js연결 및 S3에 업로드)

by 상똥프 2024. 6. 14.

목표

1. 클라이언트에서 이미지를 선택해 저장을 누르면

2. 서버를 통해 S3에 저장된다

 

목차

1. S3버켓 만들기 및 서버 코드 작성하기

2. 서버와 클라이언트 연결하기

3. 클라이언트 로직 구현하기

 


 

1. S3 버켓 만들기 & 연결되는 서버 코드 작성하기

- 아래 링크에서 확인할 수 있다

- https://sangddong-back.tistory.com/59

 

2. 서버와 클라이언트 연결하기

(1) 서버 코드 수정

- cors 설정하기

- 포트를 클라이언트와 겹치지 않도록 설정하기 (필자는 서버 3001, 클라이언트 3000으로 설정했다)

// main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // cors설정
  app.enableCors({
    origin: true,
    credentials: true,
  });

  // 포트 설정
  await app.listen(3001);
}
bootstrap();

 

(2) 클라이언트 설정

- 서버 url을 .env에 설정한다. 이때 변수값을 꼭 NEXT_PUBLIC_SERVER_URL로 설정해야 한다

// .env

NEXT_PUBLIC_SERVER_URL = `http://localhost:3001`

- axios로 서버 코드와 연결

// src/app/api/index.ts

import axios from "axios";

export const server = axios.create({
  baseURL: process.env.NEXT_PUBLIC_SERVER_URL,
  withCredentials: true,
});

export async function uploadImage(formData: any) {
  await server.post("/upload-image", formData);
}

 

3. 클라이언트 로직 구현

// src/app/profile/page.tsx

"use client";

import React, { useRef } from "react";
import { uploadImage } from "../api";

function ImageUploadPage() {
  // 파일 초기 설정, 타입은 HTMLInputElement
  const profileImageInputRef = useRef<HTMLInputElement>(null);
  const homeImageInputRef = useRef<HTMLInputElement>(null);

  // 이미지 제출 폼 로직 작성
  const handleSubmitForm = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    // 파일이 있으면 설정해주고, 없으면 null값 삽입
    const profileImage = profileImageInputRef.current?.files
      ? profileImageInputRef.current?.files[0]
      : null;
    const homeImage = homeImageInputRef.current?.files
      ? homeImageInputRef.current?.files[0]
      : null;

    // FormData 초기화
    const formData = new FormData();
    // 해당하는 필드에 값을 각각 넣어주기
    if (profileImage) {
      formData.append("profileImage", profileImage);
    }
    if (homeImage) {
      formData.append("homeImage", homeImage);
    }

    // 서버 로직과 연결
    await uploadImage(formData);
    alert("완료되었습니다.");
  };

  return (
    <form onSubmit={handleSubmitForm}>
      <div id="profileImage">
        <span>프로필 이미지 선택</span>
        <input
          title="choose-profile-image-file"
          type="file"  /* 타입은 file */
          accept="image/*"  /* 받을 파일을 이미지로 제한 */
          ref={profileImageInputRef}
        />
      </div>
      <div id="homeImage">
        <span>홈 이미지 선택</span>
        <input
          title="choose-home-image-file"
          type="file"  /* 타입은 file */
          accept="image/*"  /* 받을 파일을 이미지로 제한 */
          ref={homeImageInputRef}
        />
      </div>
      <button title="upload-images" type="submit">
        저장하기
      </button>
    </form>
  );
}

export default ImageUploadPage;

- 아래와 같이 화면이 구성되고

- 파일을 선택하면 아래와 같이 표시된다 (파일이름은 필자가 저렇게 만들었음)

- 저장하기 버튼을 누르면 alert 뜨고

- S3확인하면 이미지 업로드된 것을 확인 (원본 파일명 노출을 방지하기 위해 서버에서 파일 이름을 nanoid를 통해 바꾼다)

 

끝!