본문 바로가기

파일 저장소

업데이트됨

번역일: 2026년 10월 28일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 10월 22일
번역 갱신
2026년 10월 28일

파일 저장소

소개

Laravel은 Frank de Jonge가 만든 훌륭한 PHP 패키지인 Flysystem 덕분에 강력한 파일 시스템 추상화 계층을 제공합니다. Laravel의 Flysystem 통합은 로컬 파일 시스템, SFTP, Amazon S3를 다루기 위한 간단한 드라이버를 제공합니다. 더 좋은 점은, 어떤 저장소를 쓰더라도 API가 동일하기 때문에 로컬 개발 환경과 운영 서버에서 서로 다른 저장 방식을 사용하도록 전환하는 일이 매우 간단하다는 것입니다.

설정

Laravel의 파일 시스템 설정 파일은 config/filesystems.php에 있습니다. 이 파일에서 파일 시스템 "디스크"를 원하는 만큼 설정할 수 있습니다. 각 디스크는 하나의 저장소 드라이버와 저장 위치를 나타냅니다. 설정 파일에는 지원되는 각 드라이버의 예시 설정이 포함되어 있으므로, 이를 수정해 사용하려는 저장소와 자격 증명에 맞게 구성하면 됩니다.

local 드라이버는 Laravel 애플리케이션이 실행 중인 서버에 있는 로컬 파일을 다루고, sftp 드라이버는 SSH 키 기반의 FTP 접속에 사용됩니다. s3 드라이버는 Amazon의 S3 클라우드 저장소 서비스에 파일을 기록할 때 사용합니다.

NOTE

디스크는 원하는 만큼 설정할 수 있으며, 같은 드라이버를 사용하는 디스크를 여러 개 두어도 됩니다.

로컬 드라이버

local 드라이버를 사용하면 모든 파일 작업은 filesystems 설정 파일에 정의된 root 디렉터리를 기준으로 수행됩니다. 기본값은 storage/app/private 디렉터리입니다. 따라서 다음 코드는 storage/app/private/example.txt에 파일을 기록합니다.

use Illuminate\Support\Facades\Storage; Storage::disk('local')->put('example.txt', 'Contents');

퍼블릭 디스크

애플리케이션의 filesystems 설정 파일에 포함된 public 디스크는 외부에 공개될 파일을 위한 디스크입니다. 기본적으로 public 디스크는 local 드라이버를 사용하며, 파일을 storage/app/public에 저장합니다.

public 디스크가 local 드라이버를 사용하고 이 파일들을 웹에서 접근 가능하게 하려면, 원본 디렉터리 storage/app/public에서 대상 디렉터리 public/storage로 심볼릭 링크를 만들어야 합니다.

심볼릭 링크는 storage:link Artisan 명령어로 만들 수 있습니다.

php artisan storage:link

파일을 저장하고 심볼릭 링크를 만든 뒤에는 asset 헬퍼를 사용해 파일의 URL을 생성할 수 있습니다.

echo asset('storage/file.txt');

filesystems 설정 파일에 심볼릭 링크를 추가로 설정할 수도 있습니다. 설정된 링크는 storage:link 명령어를 실행할 때 모두 생성됩니다.

'links' => [ public_path('storage') => storage_path('app/public'), public_path('images') => storage_path('app/images'), ],

storage:unlink 명령어를 사용하면 설정된 심볼릭 링크를 제거할 수 있습니다.

php artisan storage:unlink

드라이버 사전 준비 사항

S3 드라이버 설정

S3 드라이버를 사용하기 전에 Composer 패키지 관리자로 Flysystem S3 패키지를 설치해야 합니다.

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

S3 디스크 설정 배열은 config/filesystems.php 설정 파일에 들어 있습니다. 일반적으로 S3 정보와 자격 증명은 config/filesystems.php가 참조하는 다음 환경 변수로 설정합니다.

AWS_ACCESS_KEY_ID=<your-key-id> AWS_SECRET_ACCESS_KEY=<your-secret-access-key> AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET=<your-bucket-name> AWS_USE_PATH_STYLE_ENDPOINT=false

편의를 위해 이 환경 변수들의 이름은 AWS CLI에서 사용하는 명명 규칙과 동일합니다. 한국 리전을 사용한다면 AWS_DEFAULT_REGION에 서울 리전인 ap-northeast-2를 지정하면 됩니다.

FTP 드라이버 설정

FTP 드라이버를 사용하기 전에 Composer 패키지 관리자로 Flysystem FTP 패키지를 설치해야 합니다.

composer require league/flysystem-ftp "^3.0"

Laravel의 Flysystem 통합은 FTP와도 잘 동작합니다. 다만 프레임워크의 기본 config/filesystems.php 설정 파일에는 FTP 예시 설정이 포함되어 있지 않습니다. FTP 파일 시스템을 설정해야 한다면 아래 예시를 사용하세요.

'ftp' => [ 'driver' => 'ftp', 'host' => env('FTP_HOST'), 'username' => env('FTP_USERNAME'), 'password' => env('FTP_PASSWORD'), // Optional FTP Settings... // 'port' => env('FTP_PORT', 21), // 'root' => env('FTP_ROOT'), // 'passive' => true, // 'ssl' => true, // 'timeout' => 30, ],

SFTP 드라이버 설정

SFTP 드라이버를 사용하기 전에 Composer 패키지 관리자로 Flysystem SFTP 패키지를 설치해야 합니다.

composer require league/flysystem-sftp-v3 "^3.0"

Laravel의 Flysystem 통합은 SFTP와도 잘 동작합니다. 다만 프레임워크의 기본 config/filesystems.php 설정 파일에는 SFTP 예시 설정이 포함되어 있지 않습니다. SFTP 파일 시스템을 설정해야 한다면 아래 예시를 사용하세요.

'sftp' => [ 'driver' => 'sftp', 'host' => env('SFTP_HOST'), // Settings for basic authentication... 'username' => env('SFTP_USERNAME'), 'password' => env('SFTP_PASSWORD'), // Settings for SSH key-based authentication with encryption password... 'privateKey' => env('SFTP_PRIVATE_KEY'), 'passphrase' => env('SFTP_PASSPHRASE'), // Settings for file / directory permissions... 'visibility' => 'private', // `private` = 0600, `public` = 0644 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755 // Optional SFTP Settings... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), // 'maxTries' => 4, // 'passphrase' => env('SFTP_PASSPHRASE'), // 'port' => (int) env('SFTP_PORT', 22), // 'root' => env('SFTP_ROOT', ''), // 'timeout' => 30, // 'useAgent' => true, ],

스코프, 읽기 전용, 리드 스루 파일 시스템

스코프(scoped) 디스크를 사용하면 모든 경로 앞에 지정한 경로 접두사가 자동으로 붙는 파일 시스템을 정의할 수 있습니다. 스코프 파일 시스템 디스크를 만들기 전에, Composer 패키지 관리자로 Flysystem 패키지를 추가로 설치해야 합니다.

composer require league/flysystem-path-prefixing "^3.0"

scoped 드라이버를 사용하는 디스크를 정의하면, 기존에 있는 어떤 파일 시스템 디스크든 경로가 한정된 인스턴스로 만들 수 있습니다. 예를 들어 기존 s3 디스크를 특정 경로 접두사로 한정하는 디스크를 만들면, 이 스코프 디스크를 사용하는 모든 파일 작업에 지정한 접두사가 적용됩니다.

's3-videos' => [ 'driver' => 'scoped', 'disk' => 's3', 'prefix' => 'path/to/videos', ],

"읽기 전용(read-only)" 디스크를 사용하면 쓰기 작업을 허용하지 않는 파일 시스템 디스크를 만들 수 있습니다. read-only 설정 옵션을 사용하기 전에 Composer 패키지 관리자로 Flysystem 패키지를 추가로 설치해야 합니다.

composer require league/flysystem-read-only "^3.0"

그다음, 하나 이상의 디스크 설정 배열에 read-only 설정 옵션을 추가하면 됩니다.

's3-videos' => [ 'driver' => 's3', // ... 'read-only' => true, ],

리드 스루(read-through) 디스크를 사용하면 서비스 중단 없이 디스크 간에 파일을 마이그레이션할 수 있습니다. 파일을 읽을 때 Laravel은 먼저 기본(primary) 디스크를 확인합니다. 파일이 폴백(fallback) 디스크에만 있다면, Laravel은 폴백 디스크에서 파일을 읽은 뒤 이후 요청을 위해 그 파일을 기본 디스크로 복사합니다.

'assets' => [ 'driver' => 'read-through', 'primary' => 's3', 'fallback' => 'legacy-s3', ],

쓰기 작업과 디렉터리 목록 조회는 기본 디스크를 대상으로 합니다. 파일 존재 여부 및 메타데이터 확인은 파일을 기본 디스크로 복사하지 않고 두 디스크 중 어느 쪽이든 사용합니다. 폴백 디스크의 파일을 기본 디스크로 복사하는 데 실패하더라도, 기본적으로 읽기 자체는 성공합니다. 대신 예외를 던지게 하려면 throw_on_promotion_failure 설정 옵션을 true로 지정하세요.

Amazon S3 호환 파일 시스템

애플리케이션의 filesystems 설정 파일에는 기본적으로 s3 디스크 설정이 포함되어 있습니다. 이 디스크로 Amazon S3를 사용할 수 있을 뿐만 아니라, RustFS, DigitalOcean Spaces, Vultr Object Storage, Cloudflare R2, Hetzner Cloud Storage 등 S3와 호환되는 모든 파일 저장소 서비스와도 연동할 수 있습니다.

일반적으로는 디스크의 자격 증명을 사용하려는 서비스의 자격 증명으로 바꾼 뒤, endpoint 설정 옵션의 값만 변경하면 됩니다. 이 옵션의 값은 보통 AWS_ENDPOINT 환경 변수로 정의합니다.

'endpoint' => env('AWS_ENDPOINT', 'https://rustfs:9000'),

디스크 인스턴스 얻기

Storage 파사드를 사용하면 설정해 둔 어떤 디스크와도 상호작용할 수 있습니다. 예를 들어 파사드의 put 메서드로 기본 디스크에 아바타 이미지를 저장할 수 있습니다. disk 메서드를 먼저 호출하지 않고 Storage 파사드의 메서드를 호출하면, 그 호출은 자동으로 기본 디스크에 전달됩니다.

use Illuminate\Support\Facades\Storage; Storage::put('avatars/1', $content);

애플리케이션에서 여러 디스크를 사용한다면, Storage 파사드의 disk 메서드로 특정 디스크의 파일을 다룰 수 있습니다.

Storage::disk('s3')->put('avatars/1', $content);

온디맨드 디스크

때로는 애플리케이션의 filesystems 설정 파일에 해당 설정을 두지 않고, 실행 중에 주어진 설정으로 디스크를 만들고 싶을 수 있습니다. 이럴 때는 Storage 파사드의 build 메서드에 설정 배열을 전달하면 됩니다.

use Illuminate\Support\Facades\Storage; $disk = Storage::build([ 'driver' => 'local', 'root' => '/path/to/root', ]); $disk->put('image.jpg', $content);

파일 조회하기

get 메서드로 파일의 내용을 가져올 수 있습니다. 이 메서드는 파일의 원본 문자열 내용을 반환합니다. 모든 파일 경로는 디스크의 "root" 위치를 기준으로 한 상대 경로로 지정해야 한다는 점을 기억하세요.

$contents = Storage::get('file.jpg');

가져오려는 파일이 JSON이라면 json 메서드로 파일을 읽어 내용을 디코딩할 수 있습니다.

$orders = Storage::json('orders.json');

exists 메서드로 디스크에 파일이 존재하는지 확인할 수 있습니다.

if (Storage::disk('s3')->exists('file.jpg')) { // ... }

missing 메서드로는 디스크에 파일이 없는지 확인할 수 있습니다.

if (Storage::disk('s3')->missing('file.jpg')) { // ... }

파일 다운로드

download 메서드는 사용자의 브라우저가 지정한 경로의 파일을 강제로 다운로드하도록 하는 응답을 생성합니다. 두 번째 인수로 파일명을 전달하면 파일을 내려받는 사용자에게 보이는 파일명이 결정됩니다. 마지막으로 세 번째 인수에는 HTTP 헤더 배열을 전달할 수 있습니다.

return Storage::download('file.jpg'); return Storage::download('file.jpg', $name, $headers);

파일 URL

url 메서드로 특정 파일의 URL을 얻을 수 있습니다. local 드라이버를 사용하면 보통 주어진 경로 앞에 /storage를 붙여 파일에 대한 상대 URL을 반환합니다. s3 드라이버를 사용하면 완전한 형태의 원격 URL이 반환됩니다.

use Illuminate\Support\Facades\Storage; $url = Storage::url('file.jpg');

local 드라이버를 사용할 때, 공개적으로 접근할 수 있어야 하는 파일은 모두 storage/app/public 디렉터리에 두어야 합니다. 또한 storage/app/public 디렉터리를 가리키는 public/storage 심볼릭 링크를 만들어야 합니다.

WARNING

local 드라이버를 사용할 때 url의 반환값은 URL 인코딩되지 않습니다. 따라서 항상 유효한 URL이 만들어지는 이름으로 파일을 저장하는 것을 권장합니다. 특히 한글이나 공백이 들어간 파일명은 URL로 사용할 때 문제가 생기기 쉬우므로, 저장 시 영문·숫자 기반의 파일명(예: 해시된 이름)을 쓰는 편이 안전합니다.

URL 호스트 변경

Storage 파사드로 생성되는 URL의 호스트를 바꾸고 싶다면, 디스크 설정 배열에 url 옵션을 추가하거나 변경하면 됩니다.

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

임시 URL

temporaryUrl 메서드를 사용하면 local 및 s3 드라이버로 저장된 파일에 대한 임시 URL을 만들 수 있습니다. 이 메서드는 경로와, URL이 만료되는 시점을 지정하는 DateTime 인스턴스를 받습니다.

use Illuminate\Support\Facades\Storage; $url = Storage::temporaryUrl( 'file.jpg', now()->plus(minutes: 5) );

로컬 임시 URL 활성화

local 드라이버에 임시 URL 지원이 도입되기 전에 애플리케이션 개발을 시작했다면, 로컬 임시 URL을 활성화해야 할 수 있습니다. 이를 위해 config/filesystems.php 설정 파일에서 local 디스크의 설정 배열에 serve 옵션을 추가하세요.

'local' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'serve' => true, //
'throw' => false,
],

S3 요청 파라미터

추가적인 S3 요청 파라미터를 지정해야 한다면, 요청 파라미터 배열을 temporaryUrl 메서드의 세 번째 인수로 전달하면 됩니다.

$url = Storage::temporaryUrl( 'file.jpg', now()->plus(minutes: 5), [ 'ResponseContentType' => 'application/octet-stream', 'ResponseContentDisposition' => 'attachment; filename=file2.jpg', ] );

임시 URL 생성 방식 커스터마이징

특정 스토리지 디스크에서 임시 URL이 만들어지는 방식을 직접 정해야 한다면 buildTemporaryUrlsUsing 메서드를 사용할 수 있습니다. 예를 들어 일반적으로 임시 URL을 지원하지 않는 디스크에 저장된 파일을 내려받게 하는 컨트롤러가 있을 때 유용합니다. 보통 이 메서드는 서비스 프로바이더의 boot 메서드에서 호출해야 합니다.

<?php namespace App\Providers; use DateTime; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. */ public function boot(): void { Storage::disk('local')->buildTemporaryUrlsUsing( function (string $path, DateTime $expiration, array $options) { return URL::temporarySignedRoute( 'files.download', $expiration, array_merge($options, ['path' => $path]) ); } ); } }

임시 업로드 URL

WARNING

임시 업로드 URL 생성 기능은 s3 및 local 드라이버에서만 지원됩니다.

클라이언트 측 애플리케이션에서 파일을 직접 업로드할 때 사용할 임시 URL을 생성해야 한다면 temporaryUploadUrl 메서드를 사용할 수 있습니다. 이 메서드는 경로와, URL이 만료되는 시점을 지정하는 DateTime 인스턴스를 받습니다. temporaryUploadUrl 메서드는 연관 배열을 반환하며, 이 배열을 구조 분해하여 업로드 URL과 업로드 요청에 포함해야 하는 헤더를 얻을 수 있습니다.

use Illuminate\Support\Facades\Storage; ['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl( 'file.jpg', now()->plus(minutes: 5) );

이 메서드는 주로 클라이언트 측 애플리케이션이 Amazon S3 같은 클라우드 스토리지 시스템에 파일을 직접 업로드해야 하는 서버리스 환경에서 유용합니다.

파일 메타데이터

Laravel은 파일을 읽고 쓰는 것 외에 파일 자체에 대한 정보도 제공합니다. 예를 들어 size 메서드로 파일 크기를 바이트 단위로 얻을 수 있습니다.

use Illuminate\Support\Facades\Storage; $size = Storage::size('file.jpg');

lastModified 메서드는 파일이 마지막으로 수정된 시점의 UNIX 타임스탬프를 반환합니다.

$time = Storage::lastModified('file.jpg');

mimeType 메서드로 주어진 파일의 MIME 타입을 얻을 수 있습니다.

$mime = Storage::mimeType('file.jpg');

파일 경로

path 메서드로 주어진 파일의 경로를 얻을 수 있습니다. local 드라이버를 사용하면 파일의 절대 경로를 반환하고, s3 드라이버를 사용하면 S3 버킷 내 파일의 상대 경로를 반환합니다.

use Illuminate\Support\Facades\Storage; $path = Storage::path('file.jpg');

파일 저장하기

put 메서드로 디스크에 파일 내용을 저장할 수 있습니다. PHP resource를 put 메서드에 전달할 수도 있으며, 이 경우 Flysystem의 내부 스트림 지원을 사용합니다. 모든 파일 경로는 디스크에 설정된 "root" 위치를 기준으로 한 상대 경로로 지정해야 한다는 점을 기억하세요.

use Illuminate\Support\Facades\Storage; Storage::put('file.jpg', $contents); Storage::put('file.jpg', $resource);

쓰기 실패

put 메서드(또는 다른 "쓰기" 작업)가 파일을 디스크에 기록하지 못하면 false가 반환됩니다.

if (! Storage::put('file.jpg', $contents)) { // The file could not be written to disk... }

원한다면 파일 시스템 디스크의 설정 배열에 throw 옵션을 정의할 수 있습니다. 이 옵션을 true로 정의하면, 쓰기 작업이 실패했을 때 put 같은 "쓰기" 메서드가 League\Flysystem\UnableToWriteFile 인스턴스를 예외로 던집니다.

'public' => [ 'driver' => 'local', // ... 'throw' => true, ],

또는 디스크의 설정 배열에 report 옵션을 정의할 수도 있습니다. 이 옵션을 true로 정의하면, "쓰기" 작업이 실패했을 때 Laravel이 예외를 던지거나 쓰기 작업의 반환값을 방해하지 않고, 애플리케이션의 예외 핸들러를 통해 내부 예외를 로그로 기록합니다.

'public' => [ 'driver' => 'local', // ... 'report' => true, ],

throw와 report 옵션이 모두 정의되지 않았다면, 디스크는 실패 시 조용히 false를 반환하며 내부 예외는 던져지거나 기록되지 않습니다.

파일 앞/뒤에 내용 추가하기

prepend와 append 메서드를 사용하면 파일의 처음이나 끝에 내용을 쓸 수 있습니다.

Storage::prepend('file.log', 'Prepended Text'); Storage::append('file.log', 'Appended Text');

파일 복사 및 이동

copy 메서드는 기존 파일을 디스크 내의 새 위치로 복사하고, move 메서드는 기존 파일의 이름을 바꾸거나 새 위치로 이동합니다.

Storage::copy('old/file.jpg', 'new/file.jpg'); Storage::move('old/file.jpg', 'new/file.jpg');

copyToDisk와 moveToDisk 메서드를 사용하면 파일을 다른 디스크로 복사하거나 이동할 수 있습니다. 세 번째 인수를 전달하지 않으면 원본 파일의 경로가 대상 디스크에서도 그대로 사용됩니다.

Storage::disk('local')->copyToDisk('s3', 'reports/report.csv'); Storage::disk('local')->moveToDisk( 's3', 'reports/report.csv', 'archive/report.csv' );

자동 스트리밍

파일을 스토리지로 스트리밍하면 메모리 사용량을 크게 줄일 수 있습니다. Laravel이 주어진 파일을 스토리지 위치로 스트리밍하는 과정을 자동으로 관리하게 하려면 putFile 또는 putFileAs 메서드를 사용하세요. 이 메서드는 Illuminate\Http\File 또는 Illuminate\Http\UploadedFile 인스턴스를 받아 파일을 원하는 위치로 자동으로 스트리밍합니다.

use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; // Automatically generate a unique ID for filename... $path = Storage::putFile('photos', new File('/path/to/photo')); // Manually specify a filename... $path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

putFile 메서드에는 몇 가지 알아 둘 중요한 점이 있습니다. 위 예시에서는 파일명이 아니라 디렉터리 이름만 지정했습니다. 기본적으로 putFile 메서드는 고유 ID를 생성해 파일명으로 사용합니다. 파일 확장자는 파일의 MIME 타입을 검사해 결정됩니다. putFile 메서드는 파일의 경로를 반환하므로, 생성된 파일명을 포함한 경로를 데이터베이스에 저장할 수 있습니다.

putFile과 putFileAs 메서드는 저장되는 파일의 "가시성(visibility)"을 지정하는 인수도 받습니다. Amazon S3 같은 클라우드 디스크에 파일을 저장하면서, 생성된 URL로 파일에 공개적으로 접근할 수 있게 하고 싶을 때 특히 유용합니다.

Storage::putFile('photos', new File('/path/to/photo'), 'public');

파일 업로드

웹 애플리케이션에서 파일을 저장하는 가장 흔한 사례 중 하나는 사진, 문서 같은 사용자 업로드 파일을 저장하는 것입니다. Laravel에서는 업로드된 파일 인스턴스의 store 메서드를 사용해 업로드 파일을 아주 쉽게 저장할 수 있습니다. 업로드된 파일을 저장할 경로를 지정해 store 메서드를 호출하세요.

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserAvatarController extends Controller { /** * Update the avatar for the user. */ public function update(Request $request): string { $path = $request->file('avatar')->store('avatars'); return $path; } }

이 예제에서 알아 둘 중요한 점이 몇 가지 있습니다. 파일명이 아니라 디렉터리 이름만 지정했다는 점에 주목하세요. 기본적으로 store 메서드는 고유 ID를 생성해 파일명으로 사용합니다. 파일 확장자는 파일의 MIME 타입을 검사해 결정됩니다. store 메서드는 파일의 경로를 반환하므로, 생성된 파일명을 포함한 경로를 데이터베이스에 저장할 수 있습니다.

위 예제와 동일한 파일 저장 작업을 Storage 파사드의 putFile 메서드로도 수행할 수 있습니다.

$path = Storage::putFile('avatars', $request->file('avatar'));

파일명 지정하기

저장되는 파일에 파일명이 자동으로 부여되는 것을 원하지 않는다면 storeAs 메서드를 사용하세요. 이 메서드는 경로, 파일명, 그리고 (선택 사항인) 디스크를 인수로 받습니다.

$path = $request->file('avatar')->storeAs( 'avatars', $request->user()->id );

Storage 파사드의 putFileAs 메서드로도 위 예제와 동일한 파일 저장 작업을 수행할 수 있습니다.

$path = Storage::putFileAs( 'avatars', $request->file('avatar'), $request->user()->id );

WARNING

storeAs 또는 putFileAs의 파일명 인수를 사용자가 입력한 값에서 가져와서는 안 됩니다. 공격자가 shell.php 같은 이름을 보내면, 저장된 파일이 웹에서 접근 가능한 경우 PHP로 실행될 수 있습니다. 저장 파일명에는 hashName() 또는 시스템이 생성한 식별자를 사용하고, 원래 이름을 보여 줘야 한다면 별도로 저장하세요.

WARNING

출력할 수 없는 문자와 유효하지 않은 유니코드 문자는 파일 경로에서 자동으로 제거됩니다. 따라서 Laravel의 파일 저장 메서드에 파일 경로를 전달하기 전에 경로를 정제(sanitize)하는 것이 좋습니다. 파일 경로는 League\Flysystem\WhitespacePathNormalizer::normalizePath 메서드로 정규화됩니다.

디스크 지정하기

기본적으로 업로드된 파일의 store 메서드는 기본 디스크를 사용합니다. 다른 디스크를 지정하려면 디스크 이름을 store 메서드의 두 번째 인수로 전달하세요.

$path = $request->file('avatar')->store( 'avatars/'.$request->user()->id, 's3' );

storeAs 메서드를 사용한다면 디스크 이름을 세 번째 인수로 전달하면 됩니다.

$path = $request->file('avatar')->storeAs( 'avatars', $request->user()->id, 's3' );

업로드 파일의 기타 정보

업로드된 파일의 원래 이름과 확장자를 얻고 싶다면 getClientOriginalName과 getClientOriginalExtension 메서드를 사용할 수 있습니다.

$file = $request->file('avatar'); $name = $file->getClientOriginalName(); $extension = $file->getClientOriginalExtension();

다만 getClientOriginalName과 getClientOriginalExtension 메서드는 악의적인 사용자가 파일명과 확장자를 조작할 수 있으므로 안전하지 않은 것으로 간주된다는 점을 기억하세요. 이런 이유로 업로드된 파일의 이름과 확장자는 일반적으로 hashName과 extension 메서드로 얻는 것이 좋습니다.

$file = $request->file('avatar'); $name = $file->hashName(); // Generate a unique, random name... $extension = $file->extension(); // Determine the file's extension based on the file's MIME type...

파일 가시성

Laravel의 Flysystem 통합에서 "가시성(visibility)"은 여러 플랫폼에 걸친 파일 권한을 추상화한 개념입니다. 파일은 public 또는 private으로 선언할 수 있습니다. 파일을 public으로 선언한다는 것은 그 파일이 일반적으로 다른 사람들이 접근할 수 있어야 함을 나타냅니다. 예를 들어 S3 드라이버를 사용할 때는 public 파일에 대한 URL을 얻을 수 있습니다.

파일을 쓸 때 put 메서드로 가시성을 설정할 수 있습니다.

use Illuminate\Support\Facades\Storage; Storage::put('file.jpg', $contents, 'public');

파일이 이미 저장되어 있다면 getVisibility와 setVisibility 메서드로 가시성을 조회하고 설정할 수 있습니다.

$visibility = Storage::getVisibility('file.jpg'); Storage::setVisibility('file.jpg', 'public');

업로드된 파일을 다룰 때는 storePublicly와 storePubliclyAs 메서드로 업로드 파일을 public 가시성으로 저장할 수 있습니다.

$path = $request->file('avatar')->storePublicly('avatars', 's3'); $path = $request->file('avatar')->storePubliclyAs( 'avatars', $request->user()->id, 's3' );

이미지 처리

업로드된 이미지를 저장하기 전에 크기를 조정하거나, 잘라내거나, 형식을 변환해야 한다면 Laravel의 이미지 처리 기능을 사용할 수 있습니다.

$path = $request->image('avatar') ->cover(400, 400) ->toWebp() ->storePublicly('avatars', 'public');

파일 시스템 디스크 중 하나에 이미 저장된 파일로부터 이미지 인스턴스를 만들 수도 있습니다.

$image = Storage::disk('public')->image('avatars/photo.jpg');

로컬 파일과 가시성

local 드라이버를 사용할 때 public 가시성은 디렉터리에는 0755 권한, 파일에는 0644 권한으로 변환됩니다. 이 권한 매핑은 애플리케이션의 filesystems 설정 파일에서 변경할 수 있습니다.

'local' => [ 'driver' => 'local', 'root' => storage_path('app'), 'permissions' => [ 'file' => [ 'public' => 0644, 'private' => 0600, ], 'dir' => [ 'public' => 0755, 'private' => 0700, ], ], 'throw' => false, ],

파일 삭제하기

delete 메서드는 삭제할 파일명 하나 또는 파일명 배열을 받습니다.

use Illuminate\Support\Facades\Storage; Storage::delete('file.jpg'); Storage::delete(['file.jpg', 'file2.jpg']);

필요하다면 파일을 삭제할 디스크를 지정할 수도 있습니다.

use Illuminate\Support\Facades\Storage; Storage::disk('s3')->delete('path/file.jpg');

디렉터리

디렉터리 내 모든 파일 가져오기

files 메서드는 주어진 디렉터리 안의 모든 파일을 배열로 반환합니다. 하위 디렉터리까지 포함해 주어진 디렉터리 안의 모든 파일 목록을 얻고 싶다면 allFiles 메서드를 사용하세요.

use Illuminate\Support\Facades\Storage; $files = Storage::files($directory); $files = Storage::allFiles($directory);

디렉터리 내 모든 하위 디렉터리 가져오기

directories 메서드는 주어진 디렉터리 안의 모든 디렉터리를 배열로 반환합니다. 하위 디렉터리까지 포함해 주어진 디렉터리 안의 모든 디렉터리 목록을 얻고 싶다면 allDirectories 메서드를 사용하세요.

$directories = Storage::directories($directory); $directories = Storage::allDirectories($directory);

디렉터리 생성하기

makeDirectory 메서드는 필요한 하위 디렉터리를 포함해 주어진 디렉터리를 생성합니다.

Storage::makeDirectory($directory);

디렉터리 삭제하기

마지막으로 deleteDirectory 메서드는 디렉터리와 그 안의 모든 파일을 제거합니다.

Storage::deleteDirectory($directory);

테스트

Storage 파사드의 fake 메서드를 사용하면 가짜 디스크를 쉽게 만들 수 있습니다. 이를 Illuminate\Http\UploadedFile 클래스의 파일 생성 유틸리티와 함께 사용하면 파일 업로드 테스트가 훨씬 간단해집니다. 예를 들면 다음과 같습니다.

Pest

<?php use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; test('albums can be uploaded', function () { Storage::fake('photos'); $response = $this->json('POST', '/photos', [ UploadedFile::fake()->image('photo1.jpg'), UploadedFile::fake()->image('photo2.jpg') ]); // Assert one or more files were stored... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // Assert one or more files were not stored... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // Assert that the number of files in a given directory matches the expected count... Storage::disk('photos')->assertCount('/wallpapers', 2); // Assert that a given directory is empty... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); // Assert that the disk contains no files... Storage::disk('photos')->assertEmpty(); });

PHPUnit

<?php namespace Tests\Feature; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; use Tests\TestCase; class ExampleTest extends TestCase { public function test_albums_can_be_uploaded(): void { Storage::fake('photos'); $response = $this->json('POST', '/photos', [ UploadedFile::fake()->image('photo1.jpg'), UploadedFile::fake()->image('photo2.jpg') ]); // Assert one or more files were stored... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // Assert one or more files were not stored... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // Assert that the number of files in a given directory matches the expected count... Storage::disk('photos')->assertCount('/wallpapers', 2); // Assert that a given directory is empty... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); // Assert that the disk contains no files... Storage::disk('photos')->assertEmpty(); } }

기본적으로 fake 메서드는 임시 디렉터리에 있는 모든 파일을 삭제합니다. 이 파일들을 유지하고 싶다면 "persistentFake" 메서드를 대신 사용하세요. 파일 업로드 테스트에 대한 자세한 내용은 HTTP 테스트 문서의 파일 업로드 관련 내용을 참고하세요.

WARNING

image 메서드를 사용하려면 GD 확장 모듈이 필요합니다.

커스텀 파일 시스템

Laravel의 Flysystem 통합은 기본적으로 여러 "드라이버"를 지원합니다. 하지만 Flysystem은 이에 국한되지 않고 다른 많은 스토리지 시스템을 위한 어댑터를 갖추고 있습니다. 이러한 추가 어댑터 중 하나를 Laravel 애플리케이션에서 사용하고 싶다면 커스텀 드라이버를 만들 수 있습니다.

커스텀 파일 시스템을 정의하려면 Flysystem 어댑터가 필요합니다. 커뮤니티에서 관리하는 Dropbox 어댑터를 프로젝트에 추가해 보겠습니다.

composer require spatie/flysystem-dropbox

다음으로 애플리케이션의 서비스 프로바이더 중 하나의 boot 메서드 안에서 드라이버를 등록할 수 있습니다. 이를 위해 Storage 파사드의 extend 메서드를 사용하세요.

<?php namespace App\Providers; use Illuminate\Contracts\Foundation\Application; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; use League\Flysystem\Filesystem; use Spatie\Dropbox\Client as DropboxClient; use Spatie\FlysystemDropbox\DropboxAdapter; class AppServiceProvider extends ServiceProvider { /** * Register any application services. */ public function register(): void { // ... } /** * Bootstrap any application services. */ public function boot(): void { Storage::extend('dropbox', function (Application $app, array $config) { $adapter = new DropboxAdapter(new DropboxClient( $config['authorization_token'] )); return new FilesystemAdapter( new Filesystem($adapter, $config), $adapter, $config ); }); } }

extend 메서드의 첫 번째 인수는 드라이버의 이름이고, 두 번째 인수는 $app과 $config 변수를 받는 클로저입니다. 이 클로저는 Illuminate\Filesystem\FilesystemAdapter의 인스턴스를 반환해야 합니다. $config 변수에는 지정한 디스크에 대해 config/filesystems.php에 정의된 값이 들어 있습니다.

확장 기능의 서비스 프로바이더를 만들고 등록했다면, config/filesystems.php 설정 파일에서 dropbox 드라이버를 사용할 수 있습니다.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 10월 28일