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 처리
'개발 공부 > React' 카테고리의 다른 글
| 탭 간 상태 동기화 - React (0) | 2025.12.21 |
|---|---|
| Clerk 로그인/회원가입 (0) | 2025.11.29 |
| Next.js App Router 자동 실행 규칙 (0) | 2025.11.24 |
| Next.js Route Handler (0) | 2025.11.22 |
| Clerk 인증 정보와 Prisma User 테이블 동기화(sync) (0) | 2025.11.06 |