How to Implement File Upload: Claude Code 활용 가이드
implement file upload: Claude Code 활용. 실용적인 코드 예시와 단계별 가이드를 포함합니다.
파일업로드機能の구현にClaude Code를 사용하는
파일업로드は유효성 검사、署名付きURL、プ로그レス표시、이미지リサイズなど구현項目が多い機能です。Claude Code에一括して依頼することで、セキュアで使いやすい업로드機能を효율적으로구축할 수 있습니다。
署名付きURL에 의한ダイレクト업로드
> S3の署名付きURL를 사용한파일업로드를 구현해줘。
> 서버を経由せず、클라이언트から直接S3に업로드する구성で。
> 파일サイズは10MB以下、이미지とPDFのみ허가して。
서버サイド(署名付きURL発行)
// src/app/api/upload/route.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { NextRequest, NextResponse } from 'next/server';
import { nanoid } from 'nanoid';
const s3 = new S3Client({ region: 'us-east-1' });
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
const MAX_SIZE = 10 * 1024 * 1024; // 10MB
export async function POST(req: NextRequest) {
const { fileName, fileType, fileSize } = await req.json();
// 유효성 검사
if (!ALLOWED_TYPES.includes(fileType)) {
return NextResponse.json(
{ error: '許可されていないファイル形式です' },
{ status: 400 }
);
}
if (fileSize > MAX_SIZE) {
return NextResponse.json(
{ error: 'ファイルサイズは10MB以下にしてください' },
{ status: 400 }
);
}
const ext = fileName.split('.').pop();
const key = `uploads/${nanoid()}.${ext}`;
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET!,
Key: key,
ContentType: fileType,
ContentLength: fileSize,
});
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
return NextResponse.json({
uploadUrl: signedUrl,
fileUrl: `https://${process.env.S3_BUCKET}.s3.us-east-1.amazonaws.com/${key}`,
key,
});
}
클라이언트サイド(プ로그レス付き업로드)
import { useState, useCallback } from 'react';
interface UploadState {
progress: number;
uploading: boolean;
error: string | null;
fileUrl: string | null;
}
export function useFileUpload() {
const [state, setState] = useState<UploadState>({
progress: 0,
uploading: false,
error: null,
fileUrl: null,
});
const upload = useCallback(async (file: File) => {
setState({ progress: 0, uploading: true, error: null, fileUrl: null });
try {
// 1. 署名付きURLを취득
const res = await fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fileName: file.name,
fileType: file.type,
fileSize: file.size,
}),
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(error);
}
const { uploadUrl, fileUrl } = await res.json();
// 2. S3に直接업로드(プ로그レス付き)
await new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
setState(prev => ({ ...prev, progress: Math.round((e.loaded / e.total) * 100) }));
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) resolve();
else reject(new Error('Upload failed'));
});
xhr.addEventListener('error', () => reject(new Error('Network error')));
xhr.open('PUT', uploadUrl);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
});
setState({ progress: 100, uploading: false, error: null, fileUrl });
return fileUrl;
} catch (err) {
setState(prev => ({
...prev,
uploading: false,
error: (err as Error).message,
}));
throw err;
}
}, []);
return { ...state, upload };
}
업로드컴포넌트
function FileUploader() {
const { progress, uploading, error, fileUrl, upload } = useFileUpload();
const [dragOver, setDragOver] = useState(false);
const handleDrop = useCallback(async (e: React.DragEvent) => {
e.preventDefault();
setDragOver(false);
const file = e.dataTransfer.files[0];
if (file) await upload(file);
}, [upload]);
const handleFileSelect = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) await upload(file);
}, [upload]);
return (
<div>
<div
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
onDragLeave={() => setDragOver(false)}
onDrop={handleDrop}
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors
${dragOver ? 'border-blue-500 bg-blue-50' : 'border-gray-300'}
${uploading ? 'pointer-events-none opacity-50' : 'cursor-pointer'}`}
>
<p className="text-gray-600">
ファイルをドラッグ&ドロップ、またはクリックして選択
</p>
<p className="mt-1 text-sm text-gray-400">
JPEG, PNG, WebP, PDF(10MB以下)
</p>
<input
type="file"
accept="image/jpeg,image/png,image/webp,application/pdf"
onChange={handleFileSelect}
className="hidden"
id="file-input"
/>
<label htmlFor="file-input" className="mt-4 inline-block cursor-pointer rounded bg-blue-600 px-4 py-2 text-white">
ファイルを選択
</label>
</div>
{uploading && (
<div className="mt-4">
<div className="h-2 rounded-full bg-gray-200">
<div
className="h-full rounded-full bg-blue-600 transition-all"
style={{ width: `${progress}%` }}
/>
</div>
<p className="mt-1 text-sm text-gray-500">{progress}%</p>
</div>
)}
{error && <p className="mt-2 text-sm text-red-600">{error}</p>}
{fileUrl && (
<p className="mt-2 text-sm text-green-600">
アップロード完了
</p>
)}
</div>
);
}
이미지リサイズの処理
업로드された이미지をLambdaで自動リサイズする処理もClaude Code에依頼할 수 있습니다。
// lambda/resize-image.ts
import { S3Event } from 'aws-lambda';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';
const s3 = new S3Client({});
const SIZES = [
{ suffix: 'thumb', width: 200, height: 200 },
{ suffix: 'medium', width: 800 },
{ suffix: 'large', width: 1600 },
];
export async function handler(event: S3Event) {
for (const record of event.Records) {
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key);
const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const buffer = Buffer.from(await Body!.transformToByteArray());
for (const size of SIZES) {
const resized = await sharp(buffer)
.resize(size.width, size.height, { fit: 'cover' })
.webp({ quality: 80 })
.toBuffer();
const resizedKey = key.replace(/\.[^.]+$/, `-${size.suffix}.webp`);
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: resizedKey,
Body: resized,
ContentType: 'image/webp',
}));
}
}
}
정리
Claude Code를 활용하면 파일업로드機能を署名付きURL、プ로그レス표시、유효성 검사、이미지リサイズまで含めて효율적으로구현할 수 있습니다。보안を考慮した구현を一括で생성できるのが強みです。AWS연동의 상세 정보는AWS배포の자동화도 참고하세요.個人프로젝트で의 활용법은個人개발を爆速にする方法で紹介しています。
Claude Code의 상세 정보는Anthropic공식 문서를 확인하세요.
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code 다국어 글을 매일 발행하기 전에 확인할 7가지
누락된 언어, 깨진 CTA, 반영되지 않은 배포를 막기 위해 다국어 Claude Code 글을 매일 발행하기 전에 확인할 체크리스트입니다.
Codex Automations란? 잠자는 동안 AI가 콘텐츠 운영을 처리하게 하는 방법
Codex Automations로 트래픽 분석, 주제 선정, 글 작성, CTA 개선, 배포까지 자동화하는 실전 가이드.
Claude Code × GCP Cloud Functions 완전 가이드 | 서버리스 함수 초고속 개발
Claude Code로 GCP Cloud Functions를 효율화. HTTP/Pub/Sub/Firestore 트리거 구현부터 로컬 테스트·배포 자동화까지, Masa의 실무 경험을 토대로 실제 코드로 해설.