제품 — FluxFiles

기능

현대적인 파일 매니저에 필요한 모든 것

유연성이 필요한 개발자를 위해 구축.

멀티 스토리지

로컬, AWS S3, Cloudflare R2, SFTP — Flysystem v3. 설정만으로 전환.

어디든 임베드

iframe + postMessage SDK. 독립 UI는 Alpine.js(빌드 없음). 모든 프레임워크와 호환.

JWT 인증 + 스코핑

사용자별 경로 접두사, 권한, 디스크 화이트리스트, owner-only 모드 — 또는 viewer/editor/admin 역할 프리셋으로 시작.

ACL 역할 프리셋

viewer/editor/admin/superadmin 프리셋으로 토큰 발급 — 매개변수 하나로 올바른 권한·디스크·범위 클레임이 자동으로 구성되어 수동 조합이 필요 없습니다.

AI 자동 태그

Claude / OpenAI vision — alt 텍스트, 제목, 태그 자동 생성.

이미지 자르기

종횡비 프리셋이 있는 인라인 자르기 도구.

이미지 최적화

WebP 변형 자동 생성 — 썸네일, 중간, 대형.

사전 서명 업로드

브라우저에서 S3/R2로 직접 업로드 — 서버 대역폭 제로.

청크 업로드

10MB 초과 파일용 S3 멀티파트.

전체 텍스트 검색

이름, 제목, 태그 인덱스 검색 — 기본은 파일 기반, 선택적으로 자체 SQL 데이터베이스를 백엔드로 사용 가능.

DB 기반 메타데이터

선택적 FLUXFILES_STORAGE_BACKEND=db 모드 — 메타데이터, 검색/폴더 인덱스, 감사 로그가 JSON 사이드카 대신 자체 MySQL/PostgreSQL/SQLite에 저장됩니다. 파일 바이트는 두 경우 모두 스토리지에 그대로 남습니다.

일괄 작업

다중 선택 이동, 복사, 삭제, 다운로드.

크로스 디스크 전송

로컬 ↔ S3 ↔ R2 간 복사/이동.

휴지통

복구 가능한 삭제, 자동 정리.

SEO 메타데이터

제목, alt, 캡션, 태그 — 클라우드는 S3 객체 메타데이터, 로컬은 sidecar JSON. 전체 텍스트 검색.

다크 모드

UI 및 SDK에서 라이트/다크/자동.

16개 언어

기본 영어. SDK, URL (?locale=), FLUXFILES_LOCALE로 설정. 아랍어 RTL.

감사 로그

모든 쓰기 작업 기록.

속도 제한

사용자별 토큰 버킷 API 보호.

저장소 할당량

JWT 클레임을 통한 사용자별 제한.

BYOB 버킷

사용자 자체 S3/R2 — JWT 내 자격 증명 암호화(AES-256-GCM).

중복 감지

SHA-256 일치 시 강제 덮어쓰기 전까지 중복 업로드 건너뜀.

SDK 토큰 갱신

401 시 onTokenRefresh — 갱신 병합, 재시도, updateToken()으로 사전 갱신.

버킷 닥터

모든 S3/R2 디스크를 진단 — 자격 증명, 읽기/쓰기/삭제, 프리사인, CORS, 멀티파트 — 붙여넣기만 하면 되는 IAM·CORS 수정안 포함. BYOB 버킷을 몇 분 만에 온보딩.

SFTP 디스크

S3/R2에 이은 세 번째 드라이버 — VPS·공유 호스트를 파일 관리자로.

코드 / 설정 편집기

텍스트·설정 파일(wp-config.php, .env, nginx.conf)을 브라우저에서 편집.

압축 & 해제

선택 항목을 zip으로 받거나 제자리에서 해제 — zip-slip·폭탄 방어.

워터마크

미리보기 보호를 위해 텍스트·로고 워터마크를 즉시 적용 — 원본은 그대로.

온디맨드 WebP + AVIF

하나의 URL로 모든 크기 이미지를 캐시된 AVIF/WebP로 제공(Accept 협상), 반응형 srcset 포함.

SFTP 권한

cPanel 스타일 대화상자로 SFTP 디스크의 유닉스 권한(chmod) 변경.

SSH 터미널

무료 terminal_pty_url 클레임으로 SFTP 디스크에서 직접 운영하는 ttyd / gotty / wetty를 앱 내 터미널로 임베드할 수 있습니다. FluxFiles는 자체 셸을 제공하지 않습니다.

원클릭 Git 배포

파일 관리자에서 바로 SFTP 서버의 Git 저장소를 pull·reset — 고정된 형태이며 임의 셸이 아니고, hooks는 기본적으로 꺼져 있습니다.

사용량 대시보드

스토리지를 한눈에 — 할당량과 유형별·폴더별 분석.

미디어 미리보기

이미지·동영상·오디오·PDF 인라인 미리보기; 비공개 로컬 미디어 안전 스트리밍.

빠른 설치

2분 만에 시작

composer require fluxfiles/fluxfiles
cp vendor/fluxfiles/fluxfiles/.env.example vendor/fluxfiles/fluxfiles/.env
# vendor/fluxfiles/fluxfiles/.env  (read only from inside the package)
# Real environment variables win over this file and are preferred in production
# (Composer may remove a vendor .env on update).
FLUXFILES_SECRET=<output of: openssl rand -hex 32>
FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com
<?php
require_once 'vendor/autoload.php';
// embed.php is not autoloaded — require it for fluxfiles_token()
require_once 'vendor/fluxfiles/fluxfiles/embed.php';

$token = fluxfiles_token([
    'user'   => 'user-123',
    'perms'  => ['read', 'write', 'delete'],
    'disks'  => ['local'],     // s3 / r2 need their own credentials in .env
    'prefix' => 'user-123/',
    'ttl'    => 3600,
]);
php vendor/bin/fluxfiles serve --port=8080
# then open http://127.0.0.1:8080/public/
composer require fluxfiles/laravel
php artisan vendor:publish --tag=fluxfiles-config
php artisan storage:link
# .env — proxy mode is the default: the API runs through your Laravel routes,
# no separate FluxFiles server is needed.
FLUXFILES_SECRET=<output of: openssl rand -hex 32>

# Only for FLUXFILES_MODE=standalone (FluxFiles on its own server):
# FLUXFILES_MODE=standalone
# FLUXFILES_ENDPOINT=https://fm.yourdomain.com
<?php
// app/FluxFiles/TenantClaims.php — REQUIRED: without a claims resolver
// <x-fluxfiles> renders "not available".
namespace App\FluxFiles;

use FluxFiles\Laravel\ClaimsResolver;
use Illuminate\Contracts\Auth\Authenticatable;

class TenantClaims implements ClaimsResolver
{
    public function resolve(Authenticatable $user): array
    {
        return [
            'prefix' => 'users/' . $user->getAuthIdentifier(),
        ];
    }
}
// config/fluxfiles.php
'claims_resolver' => \App\FluxFiles\TenantClaims::class,
// Laravel 11 / 12 — bootstrap/app.php: exclude the proxy routes from CSRF (else 419)
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['api/fm/*']);
})

// Laravel 10 — app/Http/Middleware/VerifyCsrfToken.php
// protected $except = ['api/fm/*'];
<x-fluxfiles disk="local" mode="picker" height="600px" />
# Not yet on WordPress.org — install the release zip:
# 1. Download fluxfiles-<version>.zip from https://github.com/thai-pc/fluxfiles/releases
# 2. Plugins -> Add New -> Upload Plugin -> Activate
# 3. Settings -> FluxFiles: set the JWT secret (>= 32 bytes) and storage
# 4. Add the shortcode (or the FluxFiles block) to any page or post:
[fluxfiles]
npm install @fluxfiles/node   # Node >= 16
// Express / Next.js route — mint a token from your JS backend (no PHP)
import { createToken } from '@fluxfiles/node';

app.get('/fluxfiles-token', (req, res) => {
    const token = createToken({
        secret: process.env.FLUXFILES_SECRET, // >= 32 bytes
        userId: req.user.id,
        perms: ['read', 'write', 'delete'],
        prefix: `users/${req.user.id}`,
        ttl: 3600,
    });
    res.json({ token });
});
pip install fluxfiles-token
# FastAPI / Django / Flask route — mint a token from your Python backend (no PHP)
import os

from fastapi import Depends
from fluxfiles_token import create_token

@app.get("/fluxfiles-token")
def fluxfiles_token(user=Depends(current_user)):
    token = create_token(
        secret=os.environ["FLUXFILES_SECRET"],  # >= 32 bytes
        user_id=user.id,
        perms=["read", "write", "delete"],
        prefix=f"users/{user.id}",
        ttl=3600,
    )
    return {"token": token}
{
  "mcpServers": {
    "fluxfiles": {
      "command": "npx",
      "args": ["-y", "@fluxfiles/mcp"],
      "env": {
        "FLUXFILES_URL": "<your FluxFiles URL>",
        "FLUXFILES_TOKEN": "<scoped JWT>"
      }
    }
  }
}
# Claude Code (Node >= 18)
claude mcp add fluxfiles -e FLUXFILES_URL=<your FluxFiles URL> -e FLUXFILES_TOKEN=<scoped JWT> -- npx -y @fluxfiles/mcp
# Read-only by default. Opt in to writes:
FLUXFILES_MCP_WRITE=1
# and to move_to_trash:
FLUXFILES_MCP_DELETE=1

에이전트는 JWT가 허용하는 범위(디스크, 경로 접두사, 권한)만 다룰 수 있습니다. 범위가 좁은 읽기 전용 토큰을 발급하세요.

npm install fluxfiles
<!-- or load a pinned CDN build (never @latest) -->
<script src="https://unpkg.com/fluxfiles@1.0.1/fluxfiles.js"></script>
<div id="file-picker" style="height:600px"></div>

<script>
FluxFiles.open({
    endpoint: 'https://your-fluxfiles-host',
    token: 'eyJhbGci...',   // JWT minted by your backend
    disk: 'local',
    mode: 'picker',
    container: '#file-picker',
    onSelect: function(file) {
        console.log('Selected:', file.url);
    },
    async onTokenRefresh() {
        const r = await fetch('/api/auth/refresh-fluxfiles-token');
        const { token } = await r.json();
        return token;
    }
});
</script>
npm install @fluxfiles/react
import { FluxFilesModal } from '@fluxfiles/react';

<FluxFilesModal
    open={open}
    endpoint="https://your-api.com"
    token={token}
    onSelect={(file) => console.log(file)}
    onClose={() => setOpen(false)}
/>
npm install @fluxfiles/vue
<script setup>
import { FluxFilesModal } from '@fluxfiles/vue';
</script>

<FluxFilesModal
    v-model:open="open"
    endpoint="https://your-api.com"
    :token="token"
    @select="onSelect"
    @close="open = false"
/>
docker run -p 8080:80 \
  -e FLUXFILES_SECRET="$(openssl rand -hex 32)" \
  -e FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com \
  -v fluxfiles-data:/app/packages/core/storage \
  ghcr.io/thai-pc/fluxfiles:1.1
# UI: http://localhost:8080/public/
git clone https://github.com/thai-pc/fluxfiles.git
cd fluxfiles/packages/core
composer install
cp .env.example .env
# packages/core/.env — the placeholder secret is rejected, set a real one
FLUXFILES_SECRET=<output of: openssl rand -hex 32>
FLUXFILES_ALLOWED_ORIGINS=http://localhost:8080
# print dev tokens (run from the repo root)
php packages/core/tests/generate-token.php
php -S localhost:8080 router.php

# UI: http://localhost:8080/public/index.html?token=<token>
# ?token= is dev-only — it is stripped from the URL on boot.
# Real apps pass the JWT via the SDK / postMessage, never in a URL.

프로덕션 배포 시: 32바이트 이상의 FLUXFILES_SECRET과 FLUXFILES_ALLOWED_ORIGINS(또는 FLUXFILES_FRAME_ANCESTORS)를 설정하세요. 패키지 루트를 웹 문서 루트로 사용하고 nginx에서 /_fluxfiles/ 와 .env를 차단하세요. 메인 JWT를 URL에 넣지 마세요. 어댑터는 postMessage로 전달합니다. DEPLOYMENT.md

호환 프레임워크

Laravel WordPress React / Next.js Vue / Nuxt CKEditor 4 CKEditor 5 TinyMCE Summernote Any framework

통합

당신의 스택을 위한 드롭인 어댑터

공식 어댑터로 이미 사용 중인 프레임워크와 에디터에 FluxFiles를 네이티브로 임베드하세요.

CKEditor나 TinyMCE를 자신의 스토리지와 함께 사용하세요. CKBox나 Tiny Drive 구독이 필요 없습니다. FluxFiles는 파일을 찾아보고 삽입하는 툴바 버튼을 추가하며, 해당 제품들의 기능을 일대일로 대체하는 것은 아닙니다.

Laravel
PHP
WordPress
Plugin
React
Component
Vue / Nuxt
Component
CKEditor 4
Plugin
CKEditor 5
Plugin
TinyMCE
Plugin
Summernote
Plugin

임베드

몇 줄이면 임베드 완료

fluxfiles.js를 로드하고 서버에서 발급한 JWT를 전달하면, 완전한 파일 관리자(또는 파일 선택 모달)를 어떤 페이지에든 넣을 수 있습니다.

<!-- npm install fluxfiles, or a pinned CDN build -->
<script src="https://unpkg.com/fluxfiles@1.0.1/fluxfiles.js"></script>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',   // JWT from your backend
  disk: 'local',
  mode: 'picker',         // select & close
  theme: 'auto',
  onSelect(file) {
    // { url, key, name, size, disk, meta, variants }
    document.querySelector("#img").src = file.url;
  },
});
<div id="fm" style="height:600px"></div>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',
  mode: 'browser',          // full manager, stays open
  container: '#fm',         // omit for a modal overlay
  disks: ['local', 's3', 'r2'],
});

FluxFiles.on('FM_EVENT', (e) => console.log(e.event));
FluxFiles.navigate('/photos/2024');
import { FluxFilesModal } from '@fluxfiles/react';

function Picker({ open, setOpen, token }) {
  return (
    <FluxFilesModal
      open={open}
      endpoint="https://fm.example.com"
      token={token}
      onSelect={(file) => console.log(file.url)}
      onClose={() => setOpen(false)}
    />
  );
}

토큰은 서버에서 발급합니다 — 시크릿을 절대 브라우저로 보내지 마세요.

왜 FluxFiles인가

다른 도구가 잊은 것을 구축

스와이프하여 비교
기능 FluxFiles elFinder Laravel-FM RichFilemanager Responsive FM
S3 + R2 + Local + SFTP ✓ ⚠ ⚠ ⚠ ✗
모든 프레임워크에 임베드 ✓ ⚠ ✗ ⚠ ✗
JWT 인증 + 스코핑 ✓ ✗ ✗ ✗ ✗
SEO 메타데이터 ✓ ✗ ✗ ✗ ✗
AI 자동 태그 ✓ ✗ ✗ ✗ ✗
이미지 자르기 ✓ ✓ ✗ ✗ ✗
청크 업로드 ✓ ✗ ✗ ✗ ✗
전체 텍스트 검색 ✓ ✗ ✗ ✗ ✗
다크 모드 ✓ ✗ ⚠ ✗ ✗
16개 언어 ✓ ✓ ✗ ✗ ✗
PHP ≥ 8.1 ✓ ✓ ✗ ✓ ✓
활발히 유지보수 중 ✓ ✓ ⚠ ✗ ✗
현대적 UI ✓ ✗ ⚠ ✗ ✗
기능 FluxFiles Filestack Uploadcare Cloudinary Supabase Storage
셀프 호스팅(자체 인프라에서 실행) ✓ ✗ ✗ ✗ ⚠
BYOB — 각 고객의 자체 버킷 ✓ ⚠ ⚠ ✗ ✗
임베드 가능한 파일 관리자 UI ✓ ✓ ✓ ⚠ ✗
오픈 소스(MIT) ✓ ✗ ✗ ✗ ✓
스토리지/업로드별 SaaS 요금 없음 ✓ ✗ ✗ ✗ ⚠
멀티 스토리지(S3 / R2 / 로컬) ✓ ⚠ ✗ ✗ ✗
테넌트별 토큰 범위 기본 제공 ✓ ⚠ ⚠ ⚠ ⚠

비교는 일반적인 기본 제공 기능을 반영합니다. 각 제품은 고유 영역에서 뛰어납니다. FluxFiles의 틈새는 임베드 가능·셀프 호스팅·자체 버킷의 조합입니다.

November 2026 기준 비교이며, 비공식 자료입니다 — 수정이 필요하면 GitHub 저장소에 알려주세요.

FAQ

자주 묻는 질문

인증은 어떻게 동작하나요?
무상태 JWT(HS256)입니다. 권한·디스크·경로 범위·한도 같은 세분화된 클레임으로 서버에서 토큰을 발급하므로, 각 사용자는 허용된 것만 볼 수 있습니다.
사용자에게 FluxFiles 계정이 필요한가요?
아니요. FluxFiles는 당신의 앱이 발급한 JWT를 신뢰하므로 기존 인증에 바로 연결됩니다 — 별도의 사용자 시스템이 필요 없습니다.
실행하려면 무엇이 필요한가요?
PHP 8.1+(8.1–8.5 테스트됨)와 gd, curl, json, openssl, mbstring, fileinfo(압축 해제 기능에는 zip 추가) 및 Composer 2가 필요합니다. 또는 ghcr.io/thai-pc/fluxfiles Docker 이미지를 사용하세요. 디스크를 지정하고 시크릿과 허용 오리진을 설정한 뒤 실행하면 됩니다. 작은 VPS에서도 동작합니다.
사용자마다 다른 권한을 줄 수 있나요?
네. 세밀한 클레임(읽기/쓰기/삭제, 디스크, 경로 접두사, 업로드 한도, 허용 확장자, owner-only)으로 토큰을 발급하거나, viewer, editor, admin, superadmin 네 가지 내장 역할 프리셋 중 하나를 사용해 클레임을 일일이 나열하지 않고 한 줄로 토큰 범위를 지정할 수 있습니다.
데이터베이스를 직접 운영해야 하나요?
아니요. 기본적으로 모든 메타데이터(파일 정보, 검색 인덱스, 감사 로그)는 파일 옆에 JSON 사이드카 형태로 저장됩니다 — 별도로 준비하거나 백업할 것이 없습니다. 이 관리 정보를 직접 운영하는 MySQL, PostgreSQL, SQLite에 두고 싶다면, 선택적인 FLUXFILES_STORAGE_BACKEND=db 모드로 그쪽으로 옮길 수 있습니다. 어느 쪽이든 파일 바이트 자체는 항상 여러분의 스토리지에 남습니다.
사용량 제한이 있나요?
코어 자체에는 인위적인 제한이 없습니다 — 월간 활성 사용자 상한도, 강제 업그레이드 유도도 없습니다. 사용자별 저장 공간 할당량, 업로드 크기/확장자 제한, 내장 속도 제한기(기본값 분당 읽기 60회/쓰기 10회, 설정 가능)까지 모든 제한은 JWT 클레임으로 직접 설정합니다.