파일 저장소

번역일: 2026년 7월 2일

파일 저장소

소개

Laravel은 Frank de Jonge가 만든 강력한 Flysystem PHP 패키지를 기반으로 파일 저장소 추상화 레이어를 제공합니다. Laravel의 Flysystem 통합은 로컬 파일시스템, SFTP, Amazon S3를 다루는 간단한 드라이버를 제공합니다. 더 좋은 점은 로컬 개발 환경과 운영 서버 사이에서 동일한 API를 사용하기 때문에, 저장 위치가 바뀌어도 코드를 수정할 필요가 없다는 것입니다.

설정

파일시스템 설정 파일은 config/filesystems.php에 위치합니다. 이 파일에서 모든 파일시스템 "디스크"를 설정할 수 있습니다. 각 디스크는 특정 저장소 드라이버와 저장 위치를 나타냅니다. 지원하는 각 드라이버의 예시 설정이 파일에 포함되어 있으니 참고하여 자신의 환경에 맞게 수정하세요.

local 드라이버는 Laravel 애플리케이션이 실행되는 서버의 로컬 파일시스템을 사용하고, 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', '파일 내용');

Public 디스크

config/filesystems.php 설정 파일에 포함된 public 디스크는 외부에서 접근 가능한 파일을 저장하기 위한 용도입니다. 기본적으로 public 디스크는 local 드라이버를 사용하며 파일을 storage/app/public 디렉터리에 저장합니다.

이 파일들을 웹에서 접근 가능하게 하려면, public/storage에서 storage/app/public으로의 심볼릭 링크를 생성해야 합니다. 이 방식을 사용하면 Envoyer와 같은 무중단 배포 시스템을 사용할 때 배포 간에 파일을 쉽게 공유할 수 있습니다.

심볼릭 링크는 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 설정이 포함되어 있습니다. 실제 S3 자격증명과 버킷 정보는 .env 파일의 환경 변수로 관리하는 것을 권장합니다. 아래 환경 변수들이 예시 설정과 대응됩니다.

AWS_ACCESS_KEY_ID=<your-key-id> AWS_SECRET_ACCESS_KEY=<your-secret-access-key> AWS_DEFAULT_REGION=ap-northeast-2 AWS_BUCKET=<your-bucket-name> AWS_USE_PATH_STYLE_ENDPOINT=false

NOTE

한국에서 S3를 사용한다면 리전을 ap-northeast-2 (서울 리전)로 설정하는 것이 일반적입니다. Naver Cloud Object Storage나 KT Cloud Storage처럼 S3 호환 API를 제공하는 국내 서비스를 사용할 경우에는 아래의 Amazon S3 호환 파일시스템 섹션을 참고하세요.

편의를 위해 이 환경 변수들은 AWS CLI의 네이밍 컨벤션을 따르고 있습니다.

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'), // 선택적 FTP 설정... // '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'), // 기본 인증 설정... 'username' => env('SFTP_USERNAME'), 'password' => env('SFTP_PASSWORD'), // SSH 키 기반 인증 설정... 'privateKey' => env('SFTP_PRIVATE_KEY'), 'passphrase' => env('SFTP_PASSPHRASE'), // 파일/디렉터리 권한 설정... 'visibility' => 'private', // `private` = 0600, `public` = 0644 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755 // 선택적 SFTP 설정... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), // 'maxTries' => 4, // 'passphrase' => env('SFTP_PASSPHRASE'), // 'port' => 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 설정 옵션을 사용하기 전에 Composer로 추가 Flysystem 패키지를 설치해야 합니다.

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

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

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

Amazon S3 호환 파일시스템

기본적으로 애플리케이션의 filesystems 설정 파일에는 s3 디스크 설정이 포함되어 있습니다. 이 설정은 Amazon S3뿐만 아니라 MinIO, Naver Cloud Object Storage, KT Cloud Storage 등 S3 호환 파일 저장 서비스와도 함께 사용할 수 있습니다.

일반적으로 사용할 서비스의 자격증명에 맞게 환경 변수를 업데이트한 후, endpoint 설정 값을 해당 서비스의 엔드포인트로 변경하면 됩니다. 이 값은 보통 AWS_ENDPOINT 환경 변수로 정의합니다.

'endpoint' => env('AWS_ENDPOINT', 'https://kr.object.ncloudstorage.com'),

MinIO

MinIO를 사용할 때 Laravel의 Flysystem 통합이 올바른 URL을 생성하려면 AWS_URL 환경 변수를 애플리케이션의 로컬 URL과 버킷 이름을 포함한 경로로 설정해야 합니다.

AWS_URL=http://localhost:9000/local

WARNING

MinIO를 사용할 때는 temporaryUrl 메서드를 통한 임시 스토리지 URL 생성이 지원되지 않습니다.

디스크 인스턴스 가져오기

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

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 메서드를 사용하면 사용자의 브라우저가 지정된 경로의 파일을 다운로드하도록 하는 응답을 생성할 수 있습니다. 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 호스트 커스터마이징

Storage 파사드를 통해 생성되는 URL의 호스트를 변경하고 싶다면, 디스크 설정 배열에 url 옵션을 추가하거나 변경할 수 있습니다.

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

임시 URL

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

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

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

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

특정 스토리지 디스크에 대해 임시 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 { /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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 드라이버에서만 지원됩니다.

클라이언트 사이드 애플리케이션에서 파일을 서버를 거치지 않고 직접 업로드할 수 있는 임시 URL이 필요하다면, temporaryUploadUrl 메서드를 사용할 수 있습니다. 이 메서드는 경로와 URL 만료 시간을 지정하는 DateTime 인스턴스를 받습니다. temporaryUploadUrl 메서드는 업로드 URL과 업로드 요청에 포함해야 하는 헤더를 포함하는 연관 배열을 반환합니다.

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

이 기능은 주로 React나 Vue.js 같은 프론트엔드 프레임워크에서 파일을 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 resourceput 메서드에 전달하면 Flysystem의 스트림 지원을 사용합니다. 큰 파일을 다룰 때는 파일 내용을 메모리에 모두 올리는 대신 스트림을 사용하는 것을 강력히 권장합니다.

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

저장 실패 처리

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

if (! Storage::put('file.jpg', $contents)) { // 파일을 디스크에 저장하지 못했습니다... }

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

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

파일 앞뒤에 내용 추가

prependappend 메서드를 사용하면 파일의 처음이나 끝에 내용을 추가할 수 있습니다.

Storage::prepend('file.log', '로그 시작 내용'); Storage::append('file.log', '로그 끝에 추가할 내용');

파일 복사 및 이동

copy 메서드로 기존 파일을 디스크의 새 위치에 복사할 수 있고, move 메서드로 기존 파일의 이름을 변경하거나 새 위치로 이동할 수 있습니다.

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

자동 스트리밍

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

use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; // 파일명에 고유한 ID를 자동으로 생성합니다... $path = Storage::putFile('photos', new File('/path/to/photo')); // 파일명을 수동으로 지정합니다... $path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

putFile 메서드와 관련하여 몇 가지 중요한 사항이 있습니다. 파일명이 아닌 디렉터리 이름만 지정한다는 점을 주목하세요. 기본적으로 putFile 메서드는 파일명으로 고유한 ID를 자동으로 생성합니다. 파일의 확장자는 파일의 MIME 타입을 검사하여 결정됩니다. 파일 경로는 putFile 메서드가 반환하므로, 데이터베이스에 저장하는 등의 용도로 활용할 수 있습니다.

putFileputFileAs 메서드는 저장 파일의 "가시성"을 지정하는 인자도 받습니다. 이는 Amazon S3와 같은 클라우드 디스크에 파일을 저장하고 해당 파일을 공개적으로 접근 가능하게 만들고 싶을 때 특히 유용합니다.

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

파일 업로드

웹 애플리케이션에서 파일 저장의 가장 일반적인 사용 사례 중 하나는 사진, 문서 등 사용자가 업로드한 파일을 저장하는 것입니다. Laravel에서는 업로드된 파일 인스턴스의 store 메서드로 매우 쉽게 업로드 파일을 저장할 수 있습니다. 파일을 저장하고 싶은 경로를 지정하여 store 메서드를 호출하면 됩니다.

<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class UserAvatarController extends Controller { /** * 사용자 아바타를 업데이트합니다. */ 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

인쇄 불가능한 문자나 유효하지 않은 유니코드 문자는 파일 경로에서 자동으로 제거됩니다. 따라서 파일 경로를 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' );

업로드된 파일의 기타 정보

업로드된 파일의 원래 이름과 확장자를 가져오려면 getClientOriginalNamegetClientOriginalExtension 메서드를 사용하세요.

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

WARNING

getClientOriginalNamegetClientOriginalExtension 메서드는 악의적인 사용자가 파일 이름이나 확장자를 조작할 수 있으므로 안전하지 않습니다. 업로드된 파일의 이름과 확장자를 얻을 때는 hashNameextension 메서드를 사용하는 것이 좋습니다.

$file = $request->file('avatar'); $name = $file->hashName(); // 고유하고 무작위한 이름을 생성합니다... $extension = $file->extension(); // MIME 타입을 기반으로 확장자를 결정합니다...

파일 가시성

Laravel의 Flysystem 통합에서 "가시성(visibility)"은 여러 플랫폼에서의 파일 권한을 추상화한 개념입니다. 파일은 public 또는 private으로 선언될 수 있습니다. 파일을 public으로 선언하면 다른 사람들이 해당 파일에 접근할 수 있음을 나타냅니다. 예를 들어 S3 드라이버를 사용할 때 public 파일의 URL을 가져올 수 있습니다.

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

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

파일이 이미 저장되어 있다면 getVisibilitysetVisibility 메서드로 가시성을 조회하거나 변경할 수 있습니다.

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

업로드된 파일을 다룰 때는 storePubliclystorePubliclyAs 메서드를 사용하여 public 가시성으로 파일을 저장할 수 있습니다.

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

로컬 파일과 가시성

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 클래스의 파일 생성 기능과 함께 사용하면 파일 업로드 테스트가 매우 간편해집니다. 예를 들어:

<?php use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; test('앨범에 사진을 업로드할 수 있다', function () { Storage::fake('photos'); $response = $this->json('POST', '/photos', [ UploadedFile::fake()->image('photo1.jpg'), UploadedFile::fake()->image('photo2.jpg') ]); // 하나 이상의 파일이 저장되었는지 확인합니다... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // 하나 이상의 파일이 저장되지 않았는지 확인합니다... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // 주어진 디렉터리가 비어있는지 확인합니다... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); });
<?php namespace Tests\Feature; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; use Tests\TestCase; class ExampleTest extends TestCase { public function test_앨범에_사진을_업로드할__있다(): void { Storage::fake('photos'); $response = $this->json('POST', '/photos', [ UploadedFile::fake()->image('photo1.jpg'), UploadedFile::fake()->image('photo2.jpg') ]); // 하나 이상의 파일이 저장되었는지 확인합니다... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // 하나 이상의 파일이 저장되지 않았는지 확인합니다... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // 주어진 디렉터리가 비어있는지 확인합니다... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); } }

기본적으로 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 { /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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은 Frank de Jonge가 만든 Flysystem PHP 패키지를 기반으로 강력한 파일시스템 추상화 레이어를 제공합니다. 이를 통해 로컬 파일시스템, SFTP, Amazon S3 등 다양한 저장소를 동일한 API로 다룰 수 있습니다. 로컬 개발 환경과 운영 서버 간에 저장소를 전환하더라도 코드를 변경할 필요가 없어, 환경에 따른 유연한 구성이 가능합니다.

설정

Laravel의 파일시스템 설정 파일은 config/filesystems.php에 위치합니다. 이 파일에서 "디스크(disk)"를 원하는 만큼 정의할 수 있습니다. 각 디스크는 특정 스토리지 드라이버와 저장 위치를 나타냅니다. 지원되는 드라이버별 예시 설정이 파일에 포함되어 있으므로, 필요에 맞게 수정하여 사용하면 됩니다.

  • local 드라이버: 애플리케이션이 실행 중인 서버의 로컬 파일시스템을 사용합니다.
  • 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', '파일 내용');

퍼블릭 디스크

public 디스크는 외부에서 접근 가능한 파일을 위해 사용합니다. 기본적으로 local 드라이버를 사용하며, 파일은 storage/app/public 디렉터리에 저장됩니다.

local 드라이버를 사용하는 public 디스크의 파일을 웹에서 접근 가능하게 하려면, storage/app/public 디렉터리를 public/storage로 가리키는 심볼릭 링크를 생성해야 합니다. 다음 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에 포함되어 있습니다. 일반적으로 아래 환경 변수를 .env 파일에 설정하면, 설정 파일이 이를 참조합니다.

AWS_ACCESS_KEY_ID=<액세스 키 ID> AWS_SECRET_ACCESS_KEY=<시크릿 액세스 키> AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET=<버킷 이름> AWS_USE_PATH_STYLE_ENDPOINT=false

NOTE

위 환경 변수 이름은 AWS CLI에서 사용하는 명명 규칙과 동일하므로, AWS CLI를 이미 사용 중이라면 익숙하게 느껴질 것입니다.

FTP 드라이버 설정

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

composer require league/flysystem-ftp "^3.0"

Laravel의 FTP 연동은 잘 동작하지만, 기본 config/filesystems.php에는 FTP 예시 설정이 포함되어 있지 않습니다. FTP 파일시스템이 필요한 경우 아래 예시를 참고하여 설정을 추가하세요.

'ftp' => [ 'driver' => 'ftp', 'host' => env('FTP_HOST'), 'username' => env('FTP_USERNAME'), 'password' => env('FTP_PASSWORD'), // 선택적 FTP 설정... // '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"

FTP와 마찬가지로 기본 설정 파일에 SFTP 예시 설정이 포함되어 있지 않습니다. SFTP 파일시스템이 필요한 경우 아래 예시를 참고하세요.

'sftp' => [ 'driver' => 'sftp', 'host' => env('SFTP_HOST'), // 기본 인증 설정... 'username' => env('SFTP_USERNAME'), 'password' => env('SFTP_PASSWORD'), // SSH 키 기반 인증 설정 (암호화 패스프레이즈 포함)... 'privateKey' => env('SFTP_PRIVATE_KEY'), 'passphrase' => env('SFTP_PASSPHRASE'), // 파일 / 디렉터리 권한 설정... 'visibility' => 'private', // `private` = 0600, `public` = 0644 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755 // 선택적 SFTP 설정... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), // 'maxTries' => 4, // 'passphrase' => env('SFTP_PASSPHRASE'), // 'port' => env('SFTP_PORT', 22), // 'root' => env('SFTP_ROOT', ''), // 'timeout' => 30, // 'useAgent' => true, ],

범위 지정 및 읽기 전용 파일시스템

범위 지정 디스크(Scoped Disk) 를 사용하면 특정 경로 접두사를 기준으로 파일 작업을 자동으로 제한할 수 있습니다. 예를 들어 S3 버킷 내 특정 폴더만 대상으로 하는 디스크를 별도로 정의할 수 있습니다. 사용하기 전에 아래 패키지를 설치해야 합니다.

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

scoped 드라이버를 사용해 기존 디스크에 경로 접두사를 적용한 새 디스크를 정의합니다. 이 디스크를 통한 모든 파일 작업은 지정된 접두사 경로 아래에서만 수행됩니다.

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

읽기 전용 디스크(Read-only Disk) 는 쓰기 작업을 허용하지 않는 파일시스템 디스크입니다. 사용하기 전에 아래 패키지를 설치해야 합니다.

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

이후 디스크 설정 배열에 read-only 옵션을 추가합니다.

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

Amazon S3 호환 파일시스템

기본 filesystems 설정 파일에는 s3 디스크 설정이 포함되어 있습니다. 이 디스크는 Amazon S3뿐만 아니라 S3 API와 호환되는 다양한 스토리지 서비스에서도 사용할 수 있습니다. 대표적인 호환 서비스는 다음과 같습니다.

호환 서비스를 사용할 때는 해당 서비스의 인증 정보로 환경 변수를 업데이트한 뒤, endpoint 설정값만 변경하면 됩니다. 이 값은 일반적으로 AWS_ENDPOINT 환경 변수로 정의합니다.

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

디스크 인스턴스 가져오기

Storage 파사드를 사용하면 설정된 디스크와 자유롭게 상호작용할 수 있습니다. 예를 들어 put 메서드로 기본 디스크에 아바타 이미지를 저장할 수 있습니다. disk 메서드를 명시적으로 호출하지 않으면, 모든 메서드 호출은 자동으로 기본 디스크를 대상으로 실행됩니다:

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

애플리케이션에서 여러 디스크를 함께 사용한다면, 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);

NOTE

온디맨드 디스크는 테스트 환경이나 멀티테넌트 구조처럼 디스크 경로를 동적으로 결정해야 하는 상황에서 특히 유용합니다.

파일 조회

get 메서드를 사용하면 파일의 내용을 문자열로 가져올 수 있습니다. 경로는 디스크의 "루트" 위치를 기준으로 하는 상대 경로로 지정해야 합니다.

$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 디렉터리에 저장해야 합니다. 또한 public/storage에서 storage/app/public으로 향하는 심볼릭 링크를 생성해야 합니다.

WARNING

local 드라이버에서 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 메서드를 사용하면 locals3 드라이버에 저장된 파일에 대한 임시 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 { /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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 생성은 s3local 드라이버에서만 지원됩니다.

클라이언트 측 애플리케이션에서 파일을 스토리지에 직접 업로드할 수 있는 임시 URL이 필요한 경우 temporaryUploadUrl 메서드를 사용할 수 있습니다. 파일 경로와 URL 만료 시각을 나타내는 DateTime 인스턴스를 인수로 전달하면, 업로드 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를 전달하면 Flysystem의 스트림 기능을 그대로 활용합니다. 파일 경로는 디스크에 설정된 "루트" 위치를 기준으로 하는 상대 경로로 지정해야 합니다.

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

쓰기 실패 처리

put 메서드(또는 기타 쓰기 작업)가 파일을 디스크에 저장하지 못하면 false를 반환합니다.

if (! Storage::put('file.jpg', $contents)) { // 파일을 디스크에 쓸 수 없습니다... }

쓰기 실패 시 예외를 발생시키고 싶다면, 파일시스템 디스크 설정 배열에 throw 옵션을 추가하면 됩니다. true로 설정하면 put 등의 쓰기 메서드가 실패할 때 League\Flysystem\UnableToWriteFile 예외를 던집니다.

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

파일 앞뒤에 내용 추가

prepend 메서드는 파일의 맨 앞에, append 메서드는 파일의 맨 끝에 내용을 추가합니다.

Storage::prepend('file.log', '앞에 추가할 텍스트'); Storage::append('file.log', '뒤에 추가할 텍스트');

파일 복사 및 이동

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

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

자동 스트리밍

파일을 스트림 방식으로 저장하면 메모리 사용량을 크게 줄일 수 있습니다. putFile 또는 putFileAs 메서드를 사용하면 Laravel이 자동으로 스트리밍을 처리해 줍니다. 이 메서드는 Illuminate\Http\File 또는 Illuminate\Http\UploadedFile 인스턴스를 받아 지정한 위치에 파일을 스트림으로 저장합니다.

use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; // 파일명을 자동으로 고유 ID로 생성... $path = Storage::putFile('photos', new File('/path/to/photo')); // 파일명을 직접 지정... $path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

putFile 메서드에서 중요한 점이 몇 가지 있습니다. 디렉터리 이름만 지정하고 파일명은 따로 지정하지 않았습니다. 기본적으로 putFile은 고유 ID를 파일명으로 자동 생성하며, 파일의 MIME 타입을 분석해 확장자를 결정합니다. 메서드가 반환하는 값은 생성된 파일명을 포함한 전체 경로이므로, 이 경로를 데이터베이스에 저장할 수 있습니다.

putFileputFileAs는 저장된 파일의 "공개 여부(visibility)"를 지정하는 인수도 받습니다. Amazon S3 같은 클라우드 디스크에 파일을 저장하고 공개 URL을 통해 접근할 수 있도록 하려면 특히 유용합니다.

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

파일 업로드

웹 애플리케이션에서 파일 저장의 가장 흔한 사례는 사용자가 업로드한 사진이나 문서를 저장하는 것입니다. Laravel은 업로드된 파일 인스턴스의 store 메서드를 통해 이 작업을 매우 간단하게 처리할 수 있습니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserAvatarController extends Controller { /** * 사용자 아바타 이미지를 업데이트합니다. */ public function update(Request $request): string { $path = $request->file('avatar')->store('avatars'); return $path; } }

이 예시에서도 디렉터리 이름만 지정하고 파일명은 별도로 지정하지 않았습니다. store 메서드는 기본적으로 고유 ID를 파일명으로 자동 생성하고, MIME 타입을 분석해 확장자를 결정합니다. 반환값은 생성된 파일명을 포함한 전체 경로이며, 데이터베이스에 저장해 두면 이후에 참조할 수 있습니다.

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

인쇄 불가능한 문자나 잘못된 유니코드 문자는 파일 경로에서 자동으로 제거됩니다. 따라서 파일 저장 메서드에 경로를 전달하기 전에 직접 경로를 검증하고 정제하는 것이 좋습니다. 경로는 내부적으로 League\Flysystem\WhitespacePathNormalizer::normalizePath 메서드를 통해 정규화됩니다.

디스크 지정

store 메서드는 기본적으로 애플리케이션의 기본 디스크를 사용합니다. 다른 디스크를 사용하려면 두 번째 인수로 디스크 이름을 전달하세요.

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

storeAs 메서드를 사용할 때는 세 번째 인수로 디스크 이름을 전달합니다.

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

업로드된 파일 정보 조회

업로드된 파일의 원본 이름과 확장자를 가져오려면 getClientOriginalNamegetClientOriginalExtension 메서드를 사용할 수 있습니다.

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

그러나 이 두 메서드는 보안상 안전하지 않습니다. 악의적인 사용자가 파일명이나 확장자를 조작해 전송할 수 있기 때문입니다. 보안을 위해서는 MIME 타입 기반으로 확장자를 판별하는 hashNameextension 메서드를 사용하는 것을 권장합니다.

$file = $request->file('avatar'); $name = $file->hashName(); // 고유하고 무작위한 파일명 생성... $extension = $file->extension(); // MIME 타입 기반으로 확장자 판별...

파일 공개 여부(Visibility)

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');

업로드된 파일을 저장할 때 공개 설정으로 저장하려면 storePubliclystorePubliclyAs 메서드를 사용하세요.

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

로컬 파일과 공개 여부

local 드라이버를 사용할 때 public visibility는 디렉터리에 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']);

특정 디스크에서 파일을 삭제하려면 disk 메서드를 함께 사용하세요.

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('앨범을 업로드할 수 있다', function () { Storage::fake('photos'); $response = $this->json('POST', '/photos', [ UploadedFile::fake()->image('photo1.jpg'), UploadedFile::fake()->image('photo2.jpg') ]); // 하나 이상의 파일이 저장되었는지 확인... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // 하나 이상의 파일이 저장되지 않았는지 확인... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // 특정 디렉터리의 파일 수가 예상 값과 일치하는지 확인... Storage::disk('photos')->assertCount('/wallpapers', 2); // 특정 디렉터리가 비어 있는지 확인... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); });

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') ]); // 하나 이상의 파일이 저장되었는지 확인... Storage::disk('photos')->assertExists('photo1.jpg'); Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); // 하나 이상의 파일이 저장되지 않았는지 확인... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); // 특정 디렉터리의 파일 수가 예상 값과 일치하는지 확인... Storage::disk('photos')->assertCount('/wallpapers', 2); // 특정 디렉터리가 비어 있는지 확인... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); } }

기본적으로 fake 메서드는 임시 디렉터리에 생성된 모든 파일을 테스트 종료 후 삭제합니다. 테스트가 끝난 뒤에도 파일을 유지하고 싶다면 fake 대신 persistentFake 메서드를 사용하세요. 파일 업로드 테스트에 대한 더 자세한 내용은 HTTP 테스트 문서의 파일 업로드 섹션을 참고하세요.

WARNING

image 메서드를 사용하려면 PHP의 GD 확장이 설치되어 있어야 합니다.

파일 저장소

커스텀 파일시스템

Laravel의 Flysystem 통합은 기본적으로 여러 "드라이버"를 제공하지만, 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 { /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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에서 해당 디스크에 대해 정의한 설정 값들이 담겨 있습니다.

NOTE

$config 배열에는 config/filesystems.php에서 해당 디스크에 정의한 모든 키-값 쌍이 그대로 전달됩니다. authorization_token과 같은 인증 정보를 디스크 설정에 추가하고, 클로저 안에서 $config 배열을 통해 읽어 오면 됩니다.

서비스 프로바이더에 드라이버를 등록한 뒤에는 config/filesystems.php 설정 파일에서 dropbox 드라이버를 사용하는 디스크를 선언할 수 있습니다:

'disks' => [ 'dropbox' => [ 'driver' => 'dropbox', 'authorization_token' => env('DROPBOX_AUTH_TOKEN'), ], ],

이제 애플리케이션 어디서든 Storage::disk('dropbox')를 통해 Dropbox 디스크를 사용할 수 있습니다.

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

번역일: 2026년 7월 2일