콘텐츠로 건너뛰기

Laravel 파일 업로드 구현 가이드|유효성 검사, Storage, S3 지원

카테고리: Laravel·PHP
이 기사는 현재 일본어로만 제공됩니다. 번역본은 순차적으로 공개될 예정입니다.

Laravel은 파일 업로드와 관련된 풍부한 기능을 갖고 있으며, 유효성 검사, 스토리지 추상화, S3 통합을 일관된 API로 구현할 수 있습니다. 이 문서는 <code>Request::file()</code> 사용법부터 시작하여 유효성 검사 규칙, Storage 파사드를 통한 저장, S3 드라이버 구성까지 실무 수준의 파일 업로드 구현을 체계적으로 설명합니다.

Client Request file() / validate() Storage disk()->put() storage/app/public S3 Bucket
그림: Laravel 업로드 흐름 (Request → Storage → 로컬/S3)

Request::file()과 hasFile() 사용 방법

Laravel 컨트롤러에서 업로드된 파일을 수신하려면 <code>$request->file()</code> 메서드를 사용합니다. 이 메서드는 Symfony의 <code>UploadedFile</code>을 상속하는 <code>Illuminate\Http\UploadedFile</code> 인스턴스를 반환합니다.

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class FileUploadController extends Controller
{
    public function store(Request $request)
    {
        // ファイルが送信されているか確認
        if (!$request->hasFile('avatar')) {
            return response()->json(['error' => 'ファイルが選択されていません。'], 422);
        }

        // UploadedFile インスタンスを取得
        $file = $request->file('avatar');

        // アップロードが成功しているか確認
        if (!$file->isValid()) {
            return response()->json(['error' => 'ファイルのアップロードに失敗しました。'], 422);
        }

        // ファイル情報の取得
        $originalName  = $file->getClientOriginalName();  // 元のファイル名
        $extension     = $file->getClientOriginalExtension(); // 元の拡張子
        $mimeType      = $file->getMimeType();             // サーバー側で判定したMIMEタイプ
        $clientMime    = $file->getClientMimeType();       // クライアントが申告したMIMEタイプ(信頼しない)
        $size          = $file->getSize();                 // バイト単位のサイズ
        $tmpPath       = $file->getRealPath();             // 一時ファイルのパス

        // 複数ファイルの場合(input[type="file" multiple])
        $files = $request->file('documents');
        foreach ($files as $uploadedFile) {
            // 各ファイルを処理
        }
    }
}

<code>$request->validate()</code>를 이용한 파일 검증

Laravel의 유효성 검사 기능을 사용하면 파일의 MIME 타입, 크기, 이미지 치수 등을 선언적으로 검증할 수 있습니다. 유효성 검사 실패 시 422 응답이 자동으로 반환됩니다.

public function store(Request $request)
{
    $validated = $request->validate([
        // 基本的なファイルバリデーション
        'avatar' => [
            'required',
            'file',                          // ファイルであること
            'mimes:jpg,jpeg,png,gif,webp',   // 許可するMIMEタイプ(拡張子指定でMIMEを自動判定)
            'max:5120',                      // KB単位のサイズ上限(5120KB = 5MB)
        ],

        // 画像専用のバリデーションルール
        'thumbnail' => [
            'required',
            'image',                         // 画像ファイルであること(JPEG/PNG/GIF/WebP/SVG)
            'mimes:jpg,jpeg,png,webp',
            'max:2048',                      // 2MBまで
            'dimensions:min_width=100,min_height=100,max_width=4096,max_height=4096',
        ],

        // PDFのバリデーション
        'document' => [
            'required',
            'file',
            'mimes:pdf',
            'max:20480',                     // 20MBまで
        ],

        // 複数ファイル
        'attachments'   => 'required|array|max:5',
        'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf|max:10240',
    ]);

    // バリデーション済みファイルを処理
    $file = $request->file('avatar');
    // ...
}
검증 규칙 설명 예시
file 업로드된 파일이어야 합니다 'file'
image 이미지 파일(JPEG/PNG/GIF/WebP/SVG) 'image'
mimes 지정된 확장자의 MIME 타입 허용 'mimes:jpg,png,pdf'
mimetypes MIME 타입을 직접 지정 'mimetypes:image/jpeg,image/png'
max KB 단위의 크기 제한 'max:10240'(10MB)
min KB 단위의 최소 크기 <code>'min:1'</code>(1KB 이상)
dimensions 이미지의 세로 가로 픽셀 수 'dimensions:min_width=100'

Storage::disk()->put()를 이용한 저장 (public / s3 디스크)

Laravel의 <code>Storage</code> 파사드는 파일 시스템을 추상화하므로 로컬 디스크, 공개 디스크, S3 등의 클라우드 스토리지를 동일한 API로 조작할 수 있습니다.

use Illuminate\Support\Facades\Storage;

public function store(Request $request)
{
    $request->validate([
        'file' => 'required|file|max:10240',
    ]);

    $file = $request->file('file');

    // ===== ローカル保存(storage/app/ 以下)=====

    // 自動でユニークなファイル名を生成して保存
    $path = $file->store('uploads');
    // → storage/app/uploads/xxxx.jpg のような形で保存される

    // 第2引数でディスクを指定
    $path = $file->store('uploads', 'local');

    // ファイル名を指定して保存
    $filename = uniqid('file_') . '.' . $file->getClientOriginalExtension();
    $path = $file->storeAs('uploads', $filename);

    // ===== public ディスク(公開アクセス可能)=====

    // storage/app/public/ 以下に保存(公開URL: /storage/... でアクセス可能)
    $path = $file->store('avatars', 'public');
    // または
    $path = Storage::disk('public')->put('avatars', $file);

    // 保存パスから公開URLを取得
    $url = Storage::disk('public')->url($path);
    // → /storage/avatars/xxxx.jpg

    // ===== Storage ファサードの各種操作 =====

    // ファイルの存在確認
    if (Storage::exists('uploads/file.jpg')) { ... }

    // ファイルの削除
    Storage::delete('uploads/file.jpg');

    // ファイルの移動
    Storage::move('uploads/temp.jpg', 'uploads/final.jpg');

    // ファイルの取得
    $contents = Storage::get('uploads/file.txt');

    // ファイルのURL取得
    $url = Storage::url('uploads/file.jpg');

    return response()->json(['path' => $path, 'url' => $url]);
}

php artisan storage:link의 설명

<code>storage/app/public</code> 디렉토리를 웹에 공개하려면 <code>public/storage</code>라는 심볼릭 링크를 생성해야 합니다. 이 링크를 생성하는 명령어는 <code>php artisan storage:link</code>입니다.

# シンボリックリンクを作成
php artisan storage:link

# 出力例:
# The [public/storage] link has been connected to [storage/app/public].

# 作成されたリンクを確認
ls -la public/storage
# lrwxrwxrwx ... public/storage -> /var/www/storage/app/public

# Docker/本番環境での注意点:
# デプロイ時に毎回実行するか、Dockerfile に含める
# RUN php artisan storage:link

심볼릭 링크가 작성되면, <code>storage/app/public/avatars/photo.jpg</code>에 저장한 파일이 <code>https://example.com/storage/avatars/photo.jpg</code>에서 액세스 가능하게 됩니다.

S3 드라이버 설정(.env)

AWS S3에 파일을 저장하려면 <code>league/flysystem-aws-s3-v3</code> 패키지가 필요합니다(Laravel 9 이상에서는 기본적으로 포함될 수 있습니다).

composer require league/flysystem-aws-s3-v3
# .env の設定
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=my-app-bucket
AWS_USE_PATH_STYLE_ENDPOINT=false

# CloudFront 経由でURLを取得したい場合
AWS_URL=https://d1234abcd.cloudfront.net
// S3 への保存
$path = $file->store('uploads', 's3');

// S3 への保存(アクセス制御を指定)
$path = Storage::disk('s3')->put('uploads/' . $filename, $file, 'public');

// 署名付きURL(一時的なプライベートファイルへのアクセス)
$url = Storage::disk('s3')->temporaryUrl(
    $path,
    now()->addMinutes(30) // 30分間有効
);

// S3 のURL取得(バケットが public の場合)
$url = Storage::disk('s3')->url($path);

config/filesystems.php의 설명

디스크 설정은 <code>config/filesystems.php</code>에서 관리됩니다. 여러 S3 버킷이나 커스텀 디스크를 추가할 수도 있습니다.

// config/filesystems.php
return [
    // デフォルトのファイルシステムディスク
    'default' => env('FILESYSTEM_DISK', 'local'),

    'disks' => [
        'local' => [
            'driver' => 'local',
            'root'   => storage_path('app'),
            'throw'  => false,
        ],

        'public' => [
            'driver'     => 'local',
            'root'       => storage_path('app/public'),
            'url'        => env('APP_URL') . '/storage',
            'visibility' => 'public',
            'throw'      => false,
        ],

        's3' => [
            'driver'                  => 's3',
            'key'                     => env('AWS_ACCESS_KEY_ID'),
            'secret'                  => env('AWS_SECRET_ACCESS_KEY'),
            'region'                  => env('AWS_DEFAULT_REGION'),
            'bucket'                  => env('AWS_BUCKET'),
            'url'                     => env('AWS_URL'),
            'endpoint'                => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw'                   => false,
        ],

        // カスタムディスク(別のS3バケットなど)
        's3-thumbnails' => [
            'driver' => 's3',
            'key'    => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_THUMBNAILS_BUCKET'),
        ],
    ],

    // storage:link コマンドで作成するリンクのマッピング
    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],
];

임시 파일 처리 및 보안

업로드된 파일은 <code>/tmp</code> 등의 디렉터리에 임시로 저장됩니다. 처리 후 <code>move_uploaded_file()</code>(PHP의 경우) 또는 Laravel의 <code>store()</code> 계열 메서드를 사용하여 최종 위치로 이동되며, 스크립트 종료 시 임시 파일은 자동으로 삭제됩니다.

  • Storage의 <code>public</code> 디스크에 저장된 파일은 웹에서 액세스할 수 있으므로 민감한 파일을 두면 안 됩니다
  • 파일명은 <code>hashName()</code> 메서드로 임의의 해시명을 생성하면 안전합니다
  • 원본 파일명을 데이터베이스에 기록하고 실제 저장명과 분리
  • S3는 기본적으로 비공개이므로 공개 파일의 경우 명시적으로 <code>public</code>을 지정하거나 서명된 URL을 사용합니다.
// セキュアなファイル保存の例
public function store(Request $request)
{
    $request->validate([
        'document' => 'required|file|mimes:pdf,docx|max:20480',
    ]);

    $file = $request->file('document');

    // hashName() でランダムなファイル名を生成(元の拡張子を保持)
    $hashedName = $file->hashName(); // 例: 5af75b.pdf

    // S3 の private エリアに保存
    $path = Storage::disk('s3')->putFileAs(
        'documents/' . auth()->id(),
        $file,
        $hashedName
    );

    // DBに元のファイル名と保存パスを記録
    Document::create([
        'user_id'       => auth()->id(),
        'original_name' => $file->getClientOriginalName(),
        'stored_path'   => $path,
        'mime_type'     => $file->getMimeType(),
        'size'          => $file->getSize(),
    ]);

    return response()->json(['message' => 'アップロード完了']);
}

이 기사에서 사용할 수 있는 테스트 파일 (무료)

  • → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">테스트 이미지 목록</a> — 검증 (mimes/dimensions) 테스트용
  • → <a href="/ja/files/pdf/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF 테스트 파일 목록</a> — PDF 업로드 유효성 검증용
  • → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">경계값 테스트 파일 목록</a> — max 검증 규칙의 상한 확인용

관련 기사

  • → <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">PHP에서 파일 업로드를 구현하는 방법 | 검증・저장・보안 완벽 가이드</a>
  • → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">AWS S3・CloudFront 파일 업로드 제한 정리</a>
  • → <a href="/ja/blog/file-validation-checklist/" class="text-primary-600 dark:text-primary-400 hover:underline">웹 폼 파일 검증 구현 체크리스트</a>

자주 묻는 질문

Laravel에서 업로드 파일의 유효성 검사 규칙은 무엇입니까?

<code>validate</code> 메서드에서 <code>file</code>, <code>mimes</code>(허용되는 확장자), <code>max</code>(KB 단위의 최대 크기) 등의 규칙을 지정할 수 있습니다.

Laravel의 Storage::put과 store의 차이는?

<code>store</code> 는 UploadedFile의 메서드로 자동으로 고유한 파일명을 생성합니다. <code>Storage::put</code> 은 임의의 경로와 파일명을 지정하여 저장합니다.

Laravel에서 S3에 파일을 업로드하려면?

<code>.env</code>에 AWS 인증 정보를 설정하고, <code>filesystems.php</code>에서 s3 디스크를 구성한 후 <code>Storage::disk('s3')->put()</code>로 저장할 수 있습니다.