본문 바로가기

개발 공부/React

UploadThing을 통한 이미지 업로드 구현하기

UploadThing

UploadThing은 Next.js 기반 프로젝트에 파일 업로드기능을 매우 쉽게 붙일 수 있도록 도와주는 서비스/라이브러리입니다.

 

UploadThing의 장점

  • Next.js에 최적화 (App Router 완전 지원)
  • 서버 구성 불필요 — UploadThing 서버가 업로드 처리
  • Image, video, pdf 등 다양한 타입 지원
  • 미들웨어 기반 인증 처리 가능 (Clerk, NextAuth 등과 결합 용이)
  • 업로드 UI 제공 (uploadthing/react)
  • S3 연동 가능 or UploadThing 자체 스토리지 사용 가능

즉, 업로드 기능을 빠르게 세팅하고 싶을 때 가장 실용적인 선택지 중 하나입니다.

 


프로젝트 생성 후 UploadThing 패키지를 설치

npm install @uploadthing/react/styles

 

환경 변수 설정 (.env)

UploadThing은 API Key 기반으로 동작합니다.

계정을 만들면 다음 환경 변수를 발급해줍니다.

 

예시:
UPLOADTHING_TOKEN= 'eyJhcGl...'

이 값은 반드시 .env.local 또는 서버 환경 변수에 넣어야 합니다.

UploadThing 내부에서 자동으로 참조하므로, 개발자는 따로 가져다 쓸 필요가 없습니다.

 

 


 

Next.js App Router Setup 구현

 

  • uploadthing/next 기반으로 라우터 생성
  • Clerk 인증 사용
  • middleware 단계에서 인증된 사용자만 업로드 가능하도록 설정
  • 업로드 완료 시 Cloud URL 반환
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { auth } from "@clerk/nextjs/server";

const f = createUploadthing();

export const ourFileRouter = {
  postImage: f({
    image: {
      maxFileSize: "4MB",
      maxFileCount: 1,
    },
  })
    .middleware(async () => {
      const { userId } = await auth();
      if (!userId) throw new Error("Unauthorized");

      return { userId };
    })
    .onUploadComplete(async ({ metadata, file }) => {
      try {
        return { fileUrl: file.url };
      } catch (error) {
        console.error("Error in onUploadComplete:", error);
        throw error;
      }
    }),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;

 

  • Clerk 인증을 사용해 로그인한 사용자만 업로드 가능
  • 업로드 파일은 하나만 허용 (maxFileCount: 1)
  • metadata로 userId 전달 → 추후 DB 저장 시 활용 가능
  • Cloud URL을 프론트로 반환해 즉시 이미지 미리보기/등록 가능

 


UploadThing Route Handler 연결

import { createRouteHandler } from "uploadthing/next";
import { ourFileRouter } from "./core";

export const { GET, POST } = createRouteHandler({
  router: ourFileRouter,
});

 

  • GET, POST 두 메서드를 모두 export해야 UploadThing UI가 제대로 동작함
  • router: ourFileRouter로 core.ts의 설정을 연결해줌

 


 

클라이언트에서 이미지 업로드 UI 구성

  • 파일을 선택하면 UploadThing을 통해 업로드 진행
  • 업로드 중 로딩 UI 표시
  • 업로드된 이미지 미리보기 표시
  • 삭제 버튼 구현
"use client";

import { UploadButton } from "@uploadthing/react";
import type { OurFileRouter } from "@/app/api/uploadthing/core";
import { useState } from "react";

export default function PostUploader({ onChange }: { onChange: (url: string | null) => void }) {
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [isUploading, setIsUploading] = useState(false);

  return (
    <div className="flex flex-col gap-3">
      {previewUrl && (
        <div className="relative">
          <img src={previewUrl} className="rounded-md border w-full" />
          <button
            className="absolute top-2 right-2 bg-black/50 text-white px-2 py-1 rounded"
            onClick={() => {
              setPreviewUrl(null);
              onChange(null);
            }}
          >
            삭제
          </button>
        </div>
      )}

      <UploadButton<OurFileRouter>
        endpoint="postImage"
        onUploadBegin={() => setIsUploading(true)}
        onClientUploadComplete={(res) => {
          setIsUploading(false);
          const url = res?.[0].fileUrl ?? null;
          setPreviewUrl(url);
          onChange(url);
        }}
        onUploadError={(error) => {
          setIsUploading(false);
          alert(`업로드 실패: ${error.message}`);
        }}
      />

      {isUploading && <p className="text-sm text-gray-500">이미지 업로드 중...</p>}
    </div>
  );
}
  • 이전에 사용하던 base64 방식 → 업로드 후 Cloud URL 기반 방식으로 전환됨
  • 미리보기 UI가 안정적으로 변경
  • UploadThing 리턴값 구조(fileUrl)에 맞춰 onChange 처리