본문으로 건너뛰기
5분 AI.
TRENDSHIFT MONTHLY #2 딥다이브 · 2026-10-23 분석

photocraft 딥다이브
— 사진 편집기 안에서 배우는 Rust 엔진 설계

이미지 편집기

빠른 판단

무엇
레이어·선택·마스크·필터와 PSD 입출력 엔진을 데스크탑·웹·CLI에서 공유하는 Rust 이미지 편집기
이럴 때
사진·PSD를 편집할 때
입력
사진·PSD·PSB 또는 자체 .pcraft 문서
결과
레이어·마스크·필터 편집을 반영한 저장 문서·이미지
PhotoCraft는 Rust로 만든 오픈소스 사진·이미지 편집기입니다. 레이어를 합성하는 CPU·GPU 렌더러, 실행 취소, PSD 입출력, 데스크톱·브라우저 UI와 자동화가 하나의 엔진을 중심으로 연결됩니다. 이 글에서는 버튼 하나가 픽셀과 저장 파일로 바뀌는 과정을 따라가며, 큰 프로그램을 나누고 검증하는 방법을 배웁니다. 저장소 storytold/photocraft · ⭐71,526 · 포크 12,630 · Rust · v0.14.0. 수치는 2026-10-23 GitHub 기준, 코드 설명은 커밋 ff47388 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

화면보다 먼저, 편집 상태를 바꾸는 엔진을 이해하기

PhotoCraft는 사진을 열어 레이어·선택 영역·마스크·브러시·필터로 편집하고, 결과를 저장하는 프로그램입니다. Photoshop과 비슷한 작업 흐름을 목표로 하며, PSD/PSB와 자체 형식 .pcraft를 다룹니다. 중요한 학습 대상은 기능 목록보다 같은 편집을 여러 인터페이스에서 재사용하는 구조입니다.

PhotoCraft에서는 화면의 버튼도 터미널의 명령도 같은 문서 모델과 편집 엔진으로 들어갑니다. 데스크톱은 egui/eframe로 화면을 그리고, 브라우저판은 Rust를 WebAssembly로 컴파일합니다. CLI는 창을 띄우지 않고 작업하며, MCP 서버는 외부 에이전트가 엔진을 호출할 수 있게 합니다.

용어
엔진과 셸
엔진은 “레이어를 만들고 픽셀을 바꾸는 규칙”을 맡습니다. 셸은 창·메뉴·파일 선택창처럼 사용자가 엔진을 만나는 입구입니다. crates/engine과 crates/ui-egui의 분리는 이 차이를 코드로 표현합니다.
한 문장 비유

화면·CLI·MCP는 주문 창구, 엔진은 같은 주방입니다

매장 계산대에서 주문하든 배달 주문을 받든, 조리법은 주방에 있어야 합니다. 화면 버튼 안에 조리법을 넣으면 배달 창구를 만들 때 같은 기능을 다시 작성하게 됩니다.

PhotoCraft는 filter.blur.gaussianBlur 같은 명령 ID로 주문을 받습니다. 각 창구는 입력을 전달하고, 엔진은 조건 검사·편집·기록을 처리합니다.

1-1. 사진 한 장을 편집할 때 생기는 일

  1. 열기: 코덱과 입출력 계층이 파일을 읽어 Document와 레이어로 바꿉니다.
  2. 편집: Session이 명령과 JSON 매개변수를 받아 문서 상태를 바꿉니다.
  3. 표시: 합성기가 레이어·마스크·효과를 계산해 화면에 보여 줄 픽셀을 만듭니다.
  4. 되돌리기: 이전 문서 상태를 보관한 History가 실행 취소를 담당합니다.
  5. 저장: 편집 가능한 문서 저장과 PNG 같은 평면 이미지 내보내기가 각각 수행됩니다.

1-2. 처음에는 이 세 파일만 잡아도 됩니다

파일처음 찾을 식별자읽으며 답할 질문
crates/doc/src/lib.rsDocument, LayerContent편집기가 무엇을 상태로 보관하는가?
crates/engine/src/commands.rsCommandSpec, find기능 이름과 실행 함수는 어떻게 연결되는가?
crates/engine/src/lib.rsSession::execute, Session::edit편집 한 번을 어떻게 일관된 상태 변경으로 묶는가?
학습 목표
작은 기능 하나로 큰 제품의 뼈대를 배우기

전체 기능을 외울 필요는 없습니다. 흐림 필터 하나가 입력·검증·알고리즘·이력·렌더링을 통과하는 길을 설명할 수 있다면, 다른 기능을 읽을 기준점이 생깁니다.

2왜 주목받는가

트렌딩의 맥락과 소스에서 확인되는 매력

2-1. Rust로 완성형 편집기를 만들겠다는 큰 목표

2026-10-23 기준 TrendShift Monthly #2이며, GitHub 스타는 71,526개입니다. 저장소 생성일은 2026-09-30으로 분석일까지 23일이 지났습니다. 이 짧은 공개 기간에 비해 편집기·파일 형식·자동화·배포를 함께 다룬다는 점이 눈길을 끕니다. 다만 저장소 생성일은 모든 코드의 개발 시작일을 증명하지 않습니다.

어떤 홍보나 커뮤니티가 유입을 만들었는지는 코드만으로 확정할 수 없습니다. 여기서 짚을 수 있는 기술적 매력은 Rust 중심의 제품 구조, 편집 엔진의 재사용, 파일 호환성 검증을 공개했다는 점입니다.

2-2. 경쟁 도구와 비교할 때의 기준

비교 기준은 학습 가능한 내부 구조와 편집 자동화입니다. Photoshop은 저장소가 명시한 기능·동작의 비교 목표이고, image는 PhotoCraft도 사용하는 Rust 영상 입출력 라이브러리입니다. 세 도구는 범위가 다르므로, 코드 공개나 명령 인터페이스의 장점을 편집 품질의 우열로 확대하면 안 됩니다.

비교 대상PhotoCraft에서 얻는 장점함께 따질 조건
Photoshop레이어·PSD 작업의 내부 구현을 Rust 소스로 읽고, CLI·MCP와 엔진의 연결까지 추적할 수 있음비슷한 메뉴가 있어도 동작·색상·PSD 교환이 같은 수준인지는 별도 검증 필요
Rust image코덱 활용을 넘어 레이어·실행 취소·문서 저장·UI까지 제품 전체를 학습할 수 있음간단한 이미지 변환만 필요하면 작은 라이브러리를 직접 쓰는 편이 범위가 작음
화면 조작 중심 작업동일 편집을 명령 ID와 JSON으로 표현해 반복 작업과 자동화에 재사용GUI 전용 기능과 파일 접근 권한은 셸·자동화 계층에서 추가 처리됨

2-3. “에이전트가 조작하는 앱”을 코드로 배울 수 있다

crates/automation/src/server.rs의 Backend는 Headless와 Bridge를 구분합니다. 전자는 프로세스 안의 엔진을 사용하고, 후자는 실행 중인 데스크톱 앱과 통신합니다. 따라서 자동화의 두 경로를 같은 도구 인터페이스 아래에서 비교할 수 있습니다.

자동화하기 쉬운 제품은 핵심 동작을 화면 밖에서도 호출할 수 있도록 설계되어 있습니다. 명령 목록에는 enabled, disabledReason, needsDocument가 포함됩니다. 단순히 실행 버튼을 제공하는 것에서 한 걸음 더 나아가, 호출자가 다음 행동을 판단할 정보를 줍니다.

2-4. README의 숫자와 소스의 현재 모습

항목문서의 표현커밋 ff47388에서 확인되는 사실
크레이트 수README: 24개crates/*/Cargo.toml은 26개, apps/는 4개
테스트README: 1,700개 이상Rust 전체의 #[test] 표시는 10,656개. 실행·통과 개수와는 다름
코드 규모아키텍처 문서: 약 53.9만 줄, 과거 집계Rust 물리 줄 수 661,699줄 / 1,502파일. 테스트·예제·생성 데이터 포함
라이선스GitHub 대표 표기: Apache-2.0Cargo.toml과 README: MIT OR Apache-2.0

문서의 숫자는 서로 다른 시점과 집계 범위를 반영합니다. 특히 테스트 표시 수는 비동기 테스트 속성이나 매크로 생성 사례를 모두 세는 방식이 아니며, 무시되거나 환경에 따라 건너뛰는 테스트도 구별하지 않습니다.

기능 범위 읽기
메뉴가 연결됐다는 사실과 완성도는 구분해야 합니다

README와 ROADMAP.md는 초기 알파 단계라고 명시합니다. docs/gaps.md에는 PSD 교환·전문 작업·생성형 기능 등의 빈틈이 남아 있습니다. 스타 수와 메뉴 연결률은 전문 편집기의 안정성이나 호환성을 증명하는 수치가 아닙니다.

3기술 스택 전체 지도

서버·웹 프레임워크 분류에 억지로 맞추지 않고 읽기

이 프로젝트에서 “백엔드”는 주로 기기 안에서 실행되는 편집 코어를 뜻합니다. 별도 원격 API 서버나 데이터베이스가 기본 편집 경로의 중심에 있지 않습니다. 프론트엔드는 Rust 네이티브 UI이며, 웹판도 같은 UI를 WebAssembly로 실행합니다.

3-1. 백엔드에 해당하는 편집·문서 코어

아래의 “선언”은 Cargo.toml의 버전 요구 범위이고, “잠금”은 루트 Cargo.lock에 기록된 구체 버전입니다. 예를 들어 선언 "1"은 정확히 1.0.0만 쓰라는 뜻이 아닙니다.

역할선언 → 잠금 버전실제 사용 위치
문서·엔진내부 패키지 0.14.0crates/doc, engine, ops
직렬화serde "1" → 1.0.229
serde_json "1" → 1.0.151
루트 선언; 명령 JSON·설정·문서 메타데이터
CPU 병렬 처리rayon "1" → 1.12.0raster, compose, algo 등의 네이티브 경로
일반 이미지 코덱image "0.25" → 0.25.10crates/codecs/Cargo.toml; 필요한 형식을 기능 플래그로 선택
GPU 합성wgpu "30" → 30.0.1crates/gpu/Cargo.toml; std, wgsl 활성화
문자 배치parley "0.11" → 0.11.1
skrifa "0.44" → 0.44.0
crates/text/Cargo.toml; 글꼴·글리프·줄 배치
자동화rmcp "3.5" → 3.5.0
tokio "1" → 1.53.1
crates/automation/Cargo.toml; MCP·비동기 통신
플러그인 실행wasmi "2.0.0" → 2.0.0crates/plugins/Cargo.toml; 제한을 둔 WebAssembly 해석기
자체 파일 형식blake3 "1.8" → 1.8.7
ruzstd "0.9.1" → 0.9.1
crates/format/Cargo.toml; 콘텐츠 해시와 zstd 처리

3-2. 프론트엔드와 플랫폼 경계

영역실제 구성읽을 지점
데스크톱 UIegui/eframe "0.36" → 0.36.2apps/photocraft/Cargo.toml: wgpu, accesskit, wayland, x11, persistence
공용 UIcraft-ui Git 리비전 5c874d5 고정루트 의존성: storytold/craft-libs의 불변 커밋 참조
파일 선택창rfd "0.17" → 0.17.2플랫폼 서비스와 UI 계층에서 OS 기능 연결
브라우저 연결wasm-bindgen "0.2" → 0.2.129apps/photocraft-web/Cargo.toml: web-sys, js-sys도 사용
웹 시작점eframe::WebRunnerapps/photocraft-web/src/web.rs::start에서 캔버스와 PhotocraftApp 연결

“Rust로 만든 웹 UI”는 브라우저 기능을 쓰지 않는다는 뜻이 아니라, UI와 엔진의 주 구현 언어가 Rust라는 뜻입니다. JavaScript·TypeScript 소스 파일은 이 리비전에서 발견되지 않지만, 웹 빌드에는 바인딩과 부트스트랩이 필요합니다. Tauri나 Electron을 쓰는 구조와도 다릅니다.

용어
워크스페이스·크레이트·기능 플래그
워크스페이스는 여러 Rust 패키지를 함께 관리하는 상위 묶음입니다. 크레이트는 Rust 코드의 컴파일 단위이며, Cargo 패키지는 이를 배포·빌드하는 단위입니다. 기능 플래그는 선택 의존성이나 기능을 켜는 스위치입니다. PhotoCraft의 heif가 그 예입니다.

3-3. 인프라·빌드·CI

설정 파일확인되는 값과 역할
Cargo.tomledition = "2024", rust-version = "1.95", resolver = "3". 릴리스는 lto = "thin", codegen-units = 1
.cargo/config.tomlxtask = "run -p xtask --". 레이어 검사·통계·코퍼스 등을 Cargo 하위 명령처럼 제공
.github/workflows/ci.ymlLinux·조건부 Windows 테스트, cargo fmt, 엄격한 Clippy, cargo xtask layers, 점수표 일치 검사. Rust는 stable 추적
.github/workflows/release.ymlrelease 브랜치에서 공용 워크플로 storytold/craft-common-actions의 @main 호출
Dockerfile웹판 자체 호스팅용 2단계 빌드. Rust 기본값 1.99, Trunk 0.21.14, nginx 1.28-alpine; 런타임은 nginx 사용자와 8080 포트
packaging/web/package.sh개별 파일 기본 상한 24 MiB, 압축 WebAssembly 분할 기본값 20 MiB. 호스트 파일 제한에 맞춰 배포
.github/workflows/docs.ymlmdbook 0.4.52로 book/ 문서 빌드

Rust 최소 요구 버전 1.95, Docker의 기본 빌더 1.99, CI의 stable은 서로 다른 설정입니다. 공용 릴리스 워크플로는 이 저장소 바깥의 @main을 따라가므로, 로컬 YAML만으로 서명·공증·모든 타깃의 전체 절차까지 확정할 수는 없습니다.

4아키텍처 심화 분석

흐림 필터 하나를 따라가며 보는 네 가지 설계 패턴

4-1. 전체 시스템 구조도

아래에서 위쪽은 사용자의 입구, 가운데는 편집 상태와 계산, 아래쪽은 파일 경계입니다. 브라우저판의 ui-egui와 엔진은 WebAssembly 안에서 실행됩니다. MCP의 브리지 경로만 실행 중인 앱의 로컬 제어 채널을 거칩니다.

+----------------+ +----------------+ +----------------+ | desktop app | | web app (WASM) | | CLI / MCP | | eframe | | WebRunner | | Headless | +-------+--------+ +-------+--------+ +-------+--------+ | | | +---- ui-egui ------+ | | | +-------------+-------------+ v +-------------------------------+ | engine: Session | | execute / start -> dispatch | | CommandSpec / validation | +---------------+---------------+ | +-------------------+------------------+ v v v +----------------+ +----------------+ +----------------+ | doc / raster | | ops: History | | algo / paint | | Document | | Arc snapshots | | text / vector | | LayerContent | | undo / redo | | CPU processing | | 256x256 tiles | +----------------+ +----------------+ +-------+--------+ | +-----+-----------------+ v v +----------------+ +----------------+ | compose: CPU | | gpu: wgpu | | reference | | compose.wgsl | +----------------+ +----------------+ file boundary: io <-> doc format <-> doc | | v v psd / codecs .pcraft bundle raw / affinity manifest + tiles + blobs MCP Bridge -> loopback control -> UI / engine handlers

이 그림은 주요 호출과 데이터 흐름을 요약합니다. 상자 사이의 모든 선이 직접적인 Cargo 의존성을 뜻하지는 않습니다. 실제 계층 규칙은 xtask/src/layers.rs의 TABLE과 검사 코드에 있습니다.

4-2. 설계 패턴 ① 명령 레지스트리와 공통 실행 경로

CommandSpec은 기능 ID·메뉴 위치·단축키·매개변수 설명과 실행 함수를 함께 보관합니다. crates/engine/src/commands.rs::registry는 OnceLock으로 레지스트리를 한 번 만들고, HashMap으로 ID를 찾아 명령 배열의 항목에 연결합니다.

// crates/engine/src/commands.rs — 실행 계약과 주요 필드 발췌
type Run = fn(&mut Session, &Value) -> Result<Value>;
type Enabled = fn(&Session) -> std::result::Result<(), String>;

pub struct CommandSpec {
    pub id: &'static str,
    pub label: &'static str,
    // menu, shortcut, params 필드는 생략
    pub enabled: Enabled,
    pub run: Run,
    pub journal: bool,
}

명령 메타데이터를 읽으면 메뉴와 자동화 도구를 구성할 수 있고, enabled로 문서·레이어·선택 영역 같은 실행 조건을 검사할 수 있습니다. 다만 params는 설명 문자열이므로, 이것만으로 JSON이 완전히 타입 안전해지는 것은 아닙니다.

Session::execute는 crates/engine/src/jobs.rs의 Session::dispatch를 호출합니다. 여기서 명령 존재 여부, 매개변수가 JSON 객체 또는 null인지, 실행 권한과 공통·필터 매개변수 규칙을 검사합니다. Session::admit는 활성 조건과 작업 충돌 등도 판단합니다.

filter.blur.gaussianBlur + {"radius": 4} | v Session::execute -> dispatch | +-- find: 명령 ID 조회 +-- authorize: 제한된 세션의 권한 검사 +-- param_check / validate_params +-- admit: 현재 상태에서 실행 가능한지 검사 | v 필터 명령 -> filters::params_for -> FilterParams | v algo 계산 -> 문서·이력 갱신 -> 다시 그리기

crates/engine/src/filters/validation.rs::fields에서 Gaussian Blur의 radius 범위는 0.1..=1000.0입니다. crates/engine/src/filters.rs::params_for에는 기본값 1.0과 범위 제한도 있습니다. 신규 호출 검증과 예전 스마트 필터 기록의 관대한 해석이 공존하므로, 변환 함수의 제한만 보고 잘못된 입력이 모두 허용된다고 해석하면 안 됩니다.

용어
명령 패턴과 실행 취소 기록
명령 패턴은 “무엇을 할지”를 ID·매개변수·실행 함수로 묶는 방식입니다. 명령 재생용 journal과 이전 문서를 보관하는 History는 목적이 다릅니다. PhotoCraft의 실행 취소를 “역명령을 하나씩 실행하는 방식”으로 이해하면 실제 구현과 어긋납니다.

4-3. 설계 패턴 ② 문서 모델을 데이터로 표현하기

crates/doc/src/lib.rs는 레이어 종류를 열거형으로 표현합니다. 레이어가 픽셀 배열이라는 가정으로 시작하지 않기 때문에, 문자·도형·조정·스마트 오브젝트도 같은 레이어 트리에 놓을 수 있습니다.

// crates/doc/src/lib.rs — LayerContent
pub enum LayerContent {
    Raster(Surface),
    Group(Group),
    Adjustment(Adjustment),
    Fill(Fill),
    Text(TextLayer),
    Shape(ShapeLayer),
    Smart(SmartObject),
}

Layer는 content 외에도 opacity, fill_opacity, mask, effects 등을 가집니다. 조정 레이어의 설정을 바꾸는 것과 픽셀을 직접 덮어쓰는 것을 데이터 차원에서 구분하므로, 비파괴 편집과 파일 저장이 같은 모델을 공유할 수 있습니다.

색상도 비슷합니다. crates/color/src/lib.rs의 PixelFormat은 mode, sample, alpha를 보관하고, SampleType은 실제로 U8·U16·F32 세 가지입니다. 설계 문서의 초기 계획에 등장하는 F16을 현재 문서 저장 타입에 추가된 것으로 읽으면 안 됩니다.

4-4. 설계 패턴 ③ 타일 단위 복사와 문서 스냅샷

crates/raster/src/lib.rs의 Surface는 BTreeMap<TileCoord, Arc<Tile>>에 존재하는 타일만 저장합니다. 타일 한 변은 256픽셀이며, 없는 타일은 표면의 기본 픽셀로 읽습니다. 기본 픽셀은 일반 레이어의 투명색일 수도, 마스크의 흰색일 수도 있습니다.

// crates/raster/src/lib.rs — tile_mut의 실제 구현
pub fn tile_mut(&mut self, c: TileCoord) -> &mut Tile {
    let fmt = self.format;
    let dp = &self.default_pixel;
    let arc = self.tiles.entry(c)
        .or_insert_with(|| Arc::new(Tile::filled(&fmt, dp)));
    Arc::make_mut(arc)
}

Arc::make_mut은 타일을 혼자 소유하면 그대로 수정하고, 다른 문서 상태와 공유 중이면 타일을 복사한 뒤 수정합니다. 오류를 반환하는 별도 경로 try_tile_mut에는 메모리 예약 실패를 다루는 코드도 있습니다.

비유

도화지를 작은 타일로 조립하고, 과거 버전에는 같은 타일을 가리키는 목록을 보관한다고 생각해 보세요. 한 구석을 칠할 때 전체 도화지 대신 바뀐 타일만 새로 만들면, 이전 그림은 그대로 남습니다.

Session::edit는 이전 Arc<Document>를 보관하고 문서를 복제한 다음, 편집 클로저가 성공하면 새 문서를 활성 문서에 반영합니다. 새 이력 단계가 필요하면 History::record가 이전 문서를 기록합니다. 문서 복제는 레이어·타일 참조를 복사하는 비용이 있으므로 상수 시간이라고 단정할 수 없습니다.

실행 취소를 싸게 만드는 핵심은 “이전 픽셀을 다시 계산하기”보다 “바뀌지 않은 픽셀을 함께 보관하기”에 있습니다. 같은 coalesce 키를 쓰는 연속 편집은 하나의 이력 단계로 묶을 수 있습니다. 슬라이더를 조금씩 움직일 때 수십 개의 실행 취소 단계가 생기는 문제를 줄이는 장치입니다.

4-5. 설계 패턴 ④ CPU를 기준 구현으로 두고 GPU 검증하기

crates/compose/src/lib.rs::render는 레이어 트리를 부동소수점 RGBA 버퍼로 합성합니다. render_tiled는 네이티브에서 독립 타일을 병렬 계산하고, 웹 타깃에서는 직렬 계산합니다. GPU 쪽은 crates/gpu/src/lib.rs::Compositor와 crates/gpu/src/compose.wgsl이 맡습니다.

crates/gpu/tests/it/parity.rs는 두 합성 결과의 RGB에 알파를 미리 곱한 뒤, RGB와 알파의 최대 차이를 비교합니다. 실제 허용 오차 상수는 1.0 / 255.0 + 4.0 * f32::EPSILON입니다. 파일 첫 주석의 “2/255”보다 실행되는 상수와 검사 코드를 우선해 읽어야 합니다.

// crates/gpu/tests/it/parity.rs — 실제 허용 오차
const TOL: f32 = 1.0 / 255.0 + 4.0 * f32::EPSILON;

기준 구현은 최적화 결과가 맞는지 판단하는 비교점이 됩니다. GPU가 없으면 일부 테스트는 건너뛰며, WGPU_BACKEND를 명시한 경우에는 해당 백엔드를 못 쓰면 실패하도록 분기합니다. CPU·GPU 일치는 이 둘의 일관성을 뜻하며, Photoshop과의 일치까지 증명하지는 않습니다.

5디렉토리 구조 해부

26개 라이브러리를 한 번에 읽지 않는 길 찾기
photocraft/ +-- Cargo.toml / Cargo.lock 공통 설정과 의존성 잠금 +-- apps/ | +-- photocraft/ 데스크톱 실행·OS 연동 | +-- photocraft-cli/ 창 없는 변환·명령·배치·MCP | +-- photocraft-web/ 브라우저 시작·파일·저장소 연결 | +-- photocraft-web-boot/ 분할된 웹 실행 파일 부팅 +-- crates/ | +-- geom / cms / color / raster | +-- doc / ops | +-- algo / paint / text / vector | +-- compose / gpu / format | +-- psd / codecs / raw / heif / affinity | +-- io / plugins / engine | +-- ui-egui / automation | +-- tablet / ipp / testkit +-- xtask/ 검사·코퍼스·통계·성능 도구 +-- docs/ / book/ 설계·개발·사용자 문서 +-- perf/ / scorecard/ 성능 기준과 품질 체크 항목 +-- examples/plugins/ 플러그인 예제 +-- packaging/ OS별 패키지·웹 호스팅 설정 +-- .github/workflows/ CI·릴리스·문서 작업 +-- assets/ 아이콘·글꼴 등과 자산별 라이선스

5-1. 계층 번호는 “아래에서 위로” 읽습니다

계층주요 크레이트책임
L0geom, cms, color, raster좌표·색상·타일 등 기초 표현. L0 내부에도 명시된 의존 순서가 있음
독립 L0psd, raw, affinity, heif, tablet, ipp, codecs독립 사용을 지향. codecs → heif는 선택 의존성 예외
L1doc문서·레이어·효과 등 편집 상태
L2ops, algo, paint, text, vector이력·필터·브러시·문자·벡터 계산
L3compose, gpu, format합성·GPU·자체 형식. gpu는 compose를 활용 가능
L4 → L5io, plugins → engine입출력·확장을 연결하고 세션·명령 관리
L6ui-egui, automation, testkitUI·자동화·테스트 도우미. testkit에는 별도 사용 규칙 적용

xtask/src/layers.rs의 UI_MIN_LAYER = 6은 하위 코어가 egui·eframe·winit 같은 UI 의존성을 끌어들이지 않게 합니다. TABLE에는 미래·예약 항목도 있으므로, 표에 이름이 있다는 사실만으로 해당 폴더가 존재한다고 판단하지 않습니다.

5-2. 줄 수로 보면 어디가 큰가

범위Rust 물리 줄 수처음 읽을 때의 전략
crates/ui-egui235,614줄 / 444파일대상 도구·대화상자 하나만 고르기
crates/engine141,687줄 / 304파일명령 ID로 등록·실행·테스트 찾기
crates/algo58,745줄 / 148파일수학 전체보다 입력·출력·경계 조건 먼저 읽기
crates/io45,092줄 / 132파일문서 모델과 외부 형식의 대응 살피기
crates/raster2,130줄 / 2파일작은 편이므로 타일 공유의 출발점으로 적합
apps/photocraft-cli2,080줄 / 6파일창 없이 엔진까지 이어지는 경로 읽기

이 집계에는 각 폴더의 테스트·예제·빌드 코드가 포함됩니다. UI 계층이 논리적으로 얇다는 말은 줄 수가 작다는 뜻이 아닙니다. 도구·패널·다국어·플랫폼 상태가 많아도 핵심 편집 규칙을 엔진에 모은다는 뜻으로 읽으면 됩니다.

6학습 포인트 (기술별)

문법·알고리즘·제품 설계를 연결하는 읽기와 작은 실험

6-1. Rust: 소유권을 데이터 보존 규칙으로 이해하기

Arc는 여러 상태가 같은 데이터를 소유하게 하고, &mut는 변경 경계를 드러냅니다. Result와 ?는 실패를 호출자에게 전달합니다. Session::edit에서 f(&mut doc, &mut active)?가 새 문서를 반영하기 전에 있다는 점을 찾아보세요.

실습 아이디어: 문서를 복제한 뒤 편집 함수가 오류를 반환하는 경우를 손으로 따라가 보세요. 활성 문서 교체와 History::record에 도달하는지 표시하면, “계산 중간의 실패가 기존 작업에 무엇을 남기는가”라는 질문을 코드로 답할 수 있습니다.

용어
안전한 Rust와 오류 없는 제품
안전한 Rust는 메모리 안전을 돕지만, 입력 검증·메모리 부족·잘못된 파일 처리를 자동으로 완성하지 않습니다. 루트의 unsafe_code = "forbid"에도 펜 입력용 crates/tablet는 OS 연동을 위한 격리된 예외를 둡니다. “Rust 중심”이라는 설명을 “모든 의존성·OS 동작까지 검증됐다”로 확대하지 마세요.

6-2. 병렬 처리: 독립적인 타일이 좋은 작업 단위가 되는 이유

crates/raster/src/lib.rs::convert_tiles는 네이티브에서 par_iter_mut를 사용합니다. 각 작업이 자기 타일의 바이트만 바꾸므로 공유 쓰기를 줄일 수 있습니다. 같은 함수의 웹 분기는 for 반복을 사용합니다.

실습 아이디어: 병렬 루프 안에서 읽는 값과 쓰는 값을 두 열로 적어 보세요. 이웃 픽셀을 읽는 필터에는 주변 영역이 필요하므로, 타일 경계가 계산 결과를 바꾸지 않게 입력 범위를 설계해야 합니다. “타일마다 나눴다”만으로 모든 영상 알고리즘이 독립적인 것은 아닙니다.

6-3. 영상 알고리즘: 흐림의 수식과 계산 비용

crates/algo/src/blur.rs::gaussian은 작은 반경에서 가우시안 커널과 conv_sep를 사용합니다. 가로·세로로 나누는 합성곱은, 한 픽셀마다 2차원 이웃 전체를 훑는 방식보다 필요한 계산을 줄일 수 있습니다.

box_widths는 반경이 4.0보다 크면 세 번의 박스 필터 근사를 선택합니다. 러닝 합은 창이 한 칸 움직일 때 빠진 값을 빼고 새 값을 더합니다. 큰 반경에서 창 안의 모든 값을 매번 다시 더하지 않도록 하는 최적화입니다.

비유

연속된 100일의 평균을 매일 구할 때, 전날 100개를 다시 더할 필요는 없습니다. 합계에서 가장 오래된 하루를 빼고 오늘 값을 더하면 됩니다. 박스 흐림의 움직이는 창도 같은 계산을 이용합니다.

실습 아이디어: 흑백 한 줄 [0, 0, 1, 0, 0]에 폭 3 평균을 직접 계산해 보세요. 경계 바깥을 0으로 볼 때와 가장자리 값으로 반복할 때를 비교한 다음, crates/algo/src/blur.rs의 경계 처리와 알파 미리 곱하기 흐름을 읽어 보세요.

6-4. 색상 관리: 픽셀 숫자와 눈에 보이는 색의 차이

crates/cms/src/lib.rs의 Profile, Transform, Intent는 ICC 프로파일과 색상 변환을 담당합니다. 같은 RGB 숫자라도 sRGB인지 Display P3인지에 따라 뜻하는 색이 달라집니다. 따라서 채널 깊이만 높인다고 색상 관리까지 해결되지는 않습니다.

실습 아이디어: PixelFormat::channels와 bytes_per_pixel로 RGBA8·RGBA16·CMYKA8의 저장 바이트 수를 계산하세요. 이어서 문서의 색 공간에서 표시 장치로 가는 변환이 합성 처리와 어디에서 분리되는지 찾아보세요.

6-5. 파일 형식: 파싱·모델 변환·저장 안정성은 서로 다른 문제

crates/psd는 형식 수준의 구조를 읽고 쓰며, crates/io는 이를 편집 가능한 문서 모델과 대응시킵니다. 알려지지 않은 블록을 보존하더라도, 편집기가 그 블록의 시각 효과를 계산할 수 있다는 뜻은 아닙니다.

crates/format/src/lib.rs의 자체 형식은 manifest.json과 콘텐츠 해시를 이름으로 쓰는 타일·바이너리 묶음으로 구성됩니다. PcraftWriter는 디렉토리 저장 시 기존 타일·바이너리를 재사용하고, 누락되거나 손상된 항목은 다시 씁니다. ZIP 저장에서는 압축 결과를 재사용하더라도 아카이브 전체를 다시 만듭니다.

.pcraft (ZIP 파일 또는 디렉토리) +-- manifest.json 문서·레이어 구조 +-- tiles/<blake3>.zst 압축한 픽셀 타일 +-- blobs/<blake3>.zst ICC·EXIF·보존 블록 등 +-- thumb.png 선택적 미리보기 +-- composite/preview.png 선택적 평면 미리보기

crates/format/src/atomic.rs::atomic_write_with_progress는 대상 옆에 임시 파일을 만들고, 쓰기·동기화 이후 교체합니다. Windows에서는 기존 파일 교체 후 디렉토리 동기화 실패를 오류로 반환하고, Unix에서는 디렉토리 동기화 오류를 무시합니다. “파일 저장”이라는 버튼 아래에 플랫폼별 실패 조건이 있다는 사실을 배우기 좋은 지점입니다.

실습 아이디어: 동일한 PSD의 바이트 일치, 재저장 전후 렌더 일치, Photoshop의 합성 이미지와 일치를 별개의 시험 항목으로 나눠 보세요. 각각의 성공이 나머지 성공을 보장하지 않는 이유를 한 문장씩 적으세요.

6-6. MCP·플러그인: 확장성과 실행 권한을 함께 설계하기

crates/automation/src/server.rs::command_list와 command_run을 함께 읽으면, 도구가 명령을 발견하고 실행하는 흐름이 보입니다. 로컬 제어 채널은 crates/automation/src/security.rs에서 256비트 토큰, 요청 한 줄 1 MiB, 동시 연결 16개, 배치 256단계 같은 한계를 정합니다.

플러그인은 별도 경계입니다. crates/plugins/src/runtime.rs::Plugin::load는 호스트 임포트를 가진 모듈을 거절하며, 기본 한계는 모듈 32 MiB·선형 메모리 512 MiB·재귀 깊이 1,024입니다. WebAssembly 명령 실행량에 따라 연료를 소모하고, 네이티브에서는 기본 60초 시간 제한도 사용합니다.

실습 아이디어: MCP는 누가 어떤 파일을 만질 수 있는지, 플러그인 런타임은 어떤 계산 자원을 쓸 수 있는지 구분해 표로 그려 보세요. 웹판에서는 단조 시계 대신 연료 제한으로 실행을 묶는 차이도 찾아보세요.

이 저장소에서 가져갈 가장 큰 기술은 기능을 추가할 때 데이터·계산·화면·검증의 경계를 함께 생각하는 습관입니다.

7하드웨어 / 시스템 요구사항

명시된 플랫폼 조건과 이미지 메모리 계산을 구분하기
항목확인되는 조건학습할 때의 해석
Windows설치 문서: Windows 10 이상. 소스 빌드는 MSVC와 C++ 빌드 도구 요구공식 설치 파일 사용과 개발 도구 준비는 서로 다른 요구사항
macOSpackaging/macos/Info.plist.in의 최소 버전 11.0실제 GPU·드라이버·글꼴 환경의 동작 보장 범위와는 별개
Linux개발 문서: X11·Wayland·GL·GTK 개발 패키지빌드용 헤더와 설치된 앱의 런타임 의존성을 구별
Rustrust-version = "1.95", edition 2024도구 버전이 낮으면 문법·표준 라이브러리 단계에서 막힐 수 있음
GPUwgpu 기반, CPU 캔버스 경로와 --safe-gpu 제공CPU 캔버스를 써도 창 표시에는 동작 가능한 그래픽·소프트웨어 어댑터가 필요
브라우저WebAssembly와 WebGPU 또는 WebGL2 경로네이티브의 Rayon 병렬 경로와 동일한 성능을 가정하지 않기
RAM·저장 공간설치·개발 문서는 고정된 최소 RAM·VRAM·여유 디스크 수치를 제시하지 않음문서 크기·깊이·레이어·효과·빌드 산출물에 따라 달라짐

7-1. 24 MP 사진의 메모리를 계산해 보기

6000 × 4000은 2,400만 픽셀입니다. 픽셀 전체가 채워진 단일 레이어를 단순 배열로 계산하면 다음과 같습니다. 아래 값은 채널 데이터만의 이론값이며 타일 가장자리 여유, 이력, 메타데이터, 임시 버퍼는 제외합니다.

포맷계산순수 픽셀 데이터
RGBA824,000,000 × 4채널 × 1바이트약 91.6 MiB
RGBA1624,000,000 × 4채널 × 2바이트약 183.1 MiB
RGBA32F24,000,000 × 4채널 × 4바이트약 366.2 MiB

합성용 부동소수점 버퍼, 필터 작업 버퍼, GPU 복사본까지 더해지면 실제 메모리는 커집니다. 빈 레이어는 희소 타일 덕분에 가볍지만, 전체를 바꾸는 필터는 많은 타일을 새로 만들 수 있습니다. 메모리 요구량은 사진 파일의 압축된 용량보다 풀린 픽셀과 동시에 보관하는 상태 수에 더 크게 좌우됩니다.

7-2. 기본 메모리 설정을 최소 사양으로 읽지 않기

crates/engine/src/prefs.rs::Performance::default에는 메모리 예산 8192, 이력 50단계, 효과 캐시 768, GPU 예산 0이 설정됩니다. 바이트 변환에 1 << 20을 쓰므로 메모리 예산 8192는 8 GiB에 해당하고, GPU의 0은 자동 설정을 뜻합니다. 이 값은 설치에 필요한 최소 RAM을 선언한 수치가 아닙니다.

처음 실험할 때는 512 × 512 이미지와 적은 레이어로 결과의 정확성을 확인하세요. 이후 해상도·깊이·레이어 수 중 하나만 늘리면 어느 자원이 비용을 만드는지 관찰하기 쉽습니다. CPU CLI 실습과 GPU 화면 실습을 나누면 환경 문제를 좁히는 데도 도움이 됩니다.

성능 숫자의 범위
저장된 벤치마크는 현재 버전의 최소 사양표가 아닙니다

docs/scorecard.md의 성능 기준선은 2026-10-05, 커밋 f5d2bcb54a, Apple M4 Pro·48 GB 환경의 기록입니다. 높은 시스템 부하도 함께 기록되어 있습니다. 이를 v0.14.0의 모든 장치 성능으로 일반화할 수 없습니다.

8직접 해볼 수 있는 실습 과제

읽기에서 시작해 작은 실험과 검증으로 확장하는 네 과제

시간은 학습 계획용 대략값입니다. 먼저 소스 읽기로 흐름을 설명하고, 실행 실습은 개발 환경을 준비한 뒤 작은 합성 이미지로 진행하면 됩니다. 코드를 추가하는 과제는 별도의 학습용 사본에서 진행하세요.

과제 1 · 입문 · 30~60분

흐림 필터의 이동 경로를 여섯 칸으로 그리기

목표: 명령 ID에서 알고리즘까지 파일을 찾아가는 능력을 익힙니다.

순서: apps/photocraft-cli/src/lib.rs::run_cmds에서 출발해 Headless::command_run, Session::execute, dispatch, filters::params_for, crates/algo/src/blur.rs::gaussian 순으로 위치를 찾습니다.

완료 조건: 여섯 칸에 각각 입력·출력·실패 조건을 적고, “문서가 없는 경우”와 “반경이 잘못된 경우”가 어느 단계에서 거절되는지 설명합니다.

# 저장소 루트에서 명령 등록 지점과 알고리즘 찾기
rg -n 'gaussianBlur|params_for' crates/engine
rg -n 'fn gaussian|fn box_widths' crates/algo/src/blur.rs

# CLI 실행 파일이 준비되어 있다면 명령 메타데이터 확인
photocraft-cli commands --json --filter gaussian
과제 2 · 중급 · 1~2시간

타일 공유가 언제 끊어지는지 확인하기

목표: 복제한 표면이 픽셀을 바로 복사하지 않는다는 것을 확인합니다.

순서: 한 픽셀을 칠하고 표면을 복제한 뒤 Arc::ptr_eq로 타일 공유를 확인합니다. 복제본을 수정한 다음 두 타일이 서로 다른지 확인합니다.

완료 조건: 수정 전·후의 참조 관계를 그림으로 제출합니다. 좌표 255와 256도 시험해, 타일 경계를 넘을 때 타일 수가 어떻게 달라지는지 설명합니다.

다음은 photocraft-raster, photocraft-color, photocraft-geom을 사용할 수 있는 Rust 학습 코드에 넣는 예시입니다.

use std::sync::Arc;
use photocraft_color::PixelFormat;
use photocraft_geom::{Rect, TileCoord};
use photocraft_raster::Surface;

fn observe_cow() -> Result<(), &'static str> {
    let c = TileCoord::new(0, 0);
    let mut a = Surface::new(PixelFormat::RGBA8);
    a.fill_rect(Rect::new(1, 1, 2, 2), &[1.0, 0.0, 0.0, 1.0]);
    let mut b = a.clone();

    assert!(Arc::ptr_eq(
        a.tile(c).ok_or("원본 타일 없음")?,
        b.tile(c).ok_or("복제본 타일 없음")?,
    ));
    b.fill_rect(Rect::new(1, 1, 2, 2), &[0.0, 1.0, 0.0, 1.0]);
    assert!(!Arc::ptr_eq(
        a.tile(c).ok_or("원본 타일 없음")?,
        b.tile(c).ok_or("복제본 타일 없음")?,
    ));
    Ok(())
}
과제 3 · 중급 · 1~2시간

같은 명령으로 정상 입력과 잘못된 입력 비교하기

목표: JSON 매개변수와 검증 규칙의 관계를 이해합니다.

순서: 작은 문서에 픽셀 레이어와 붓 자국을 만들고 흐림을 적용합니다. 다음 예시의 반경을 -1, "4", 1001로 바꿔 오류를 관찰합니다.

완료 조건: 입력별 허용 여부를 표로 정리하고, crates/engine/src/filters/validation.rs의 실제 규칙과 대조합니다. 잘못된 호출 뒤 파일이 만들어졌는지, 정상 호출과 오류 출력이 어떻게 다른지도 기록합니다.

photocraft-cli run \
  --new '{"width":128,"height":128,"background":"transparent"}' \
  --cmd layer.new.layer --params '{"name":"Ink"}' \
  --cmd paint.stroke \
  --params '{"points":[[32,64,1],[96,64,1]],"size":16,"color":"#ff0000"}' \
  --cmd filter.blur.gaussianBlur --params '{"radius":4}' \
  --out exercise.png

정상 호출에서는 붓 자국의 가장자리가 부드러워집니다. 실패 사례마다 새 출력 이름을 사용하면 이전 결과와 혼동하지 않고 확인할 수 있습니다.

과제 4 · 고급 · 3~5시간

CPU·GPU 차이를 찾는 작은 테스트 문서 설계하기

목표: 빠른 구현을 검증하는 기준과 입력을 직접 설계합니다.

순서: crates/gpu/tests/it/parity.rs의 check와 hdr_adjustments_preserve_range_and_cpu_parity를 읽습니다. 반투명 레이어 둘과 마스크 하나를 가진 작은 문서 사례를 추가해 두 합성 결과를 비교합니다.

완료 조건: 최악의 픽셀 차이와 좌표를 기록하고, 허용 오차를 넘는 경우 원본 픽셀·알파·색 공간을 조사합니다. GPU 미탑재로 건너뛴 경우와 실제 비교를 통과한 경우를 구분합니다.

실습의 완료 기준은 “실행됐다”에서 끝내지 말고, 어떤 입력에서 어떤 상태가 보존되는지 설명하는 데 두세요. 고급 과제에서는 허용 오차를 키워 통과시키기 전에 기준 구현·출력 표현·비교 방식을 먼저 확인해야 합니다.

9관련 기술 심화 학습 로드맵

6주 동안 작은 엔진에서 실제 제품의 경계까지 넓히기

주당 4~6시간을 가정한 계획입니다. Rust가 처음이라면 1주차를 늘리고, 그래픽스 경험이 있다면 3~4주차에서 직접 손으로 계산하는 시간을 늘리세요. 각 주는 읽을 개념, 소스 근거, 설명 가능한 산출물로 묶었습니다.

주차공부할 개념소스와 결과물
1주차Rust 소유권·참조·열거형·Resultcrates/color/src/lib.rs, crates/doc/src/lib.rs. 픽셀 형식과 레이어 종류를 쉬운 말로 설명하는 한 장
2주차Cargo 워크스페이스·계층·명령 패턴xtask/src/layers.rs, crates/engine/src/commands.rs. 과제 1의 명령 이동 경로와 의존성 그림
3주차Arc·복사 시 쓰기·트랜잭션·실행 취소crates/raster/src/lib.rs, crates/ops/src/lib.rs. 과제 2의 공유 실험과 실패 시 상태 분석
4주차합성곱·알파·색 공간·병렬 처리crates/algo/src/blur.rs, crates/cms/src/lib.rs. 과제 3의 입력 표와 작은 필터 계산
5주차wgpu·WGSL·차등 테스트·파일 왕복crates/gpu/tests/it/parity.rs, crates/io/tests/it/corpus.rs. 과제 4의 비교 기록과 호환성 시험 구분
6주차플랫폼 추상화·MCP·WASM·배포crates/automation/src/server.rs, apps/photocraft-web, Dockerfile. 데스크톱·CLI·웹에서 공통인 기능과 다른 서비스 표

9-1. 길을 잃었을 때 돌아올 세 질문

  1. 데이터: 이 기능이 바꾸는 값은 Document 안에 있는가, UI 상태에 있는가?
  2. 계산: 같은 입력으로 화면 없이 결과를 만들 수 있는가?
  3. 검증: 정확성을 무엇과 비교하며, 실패 후 어떤 상태를 보존하는가?

새로운 라이브러리를 많이 외우는 것보다, 기능 하나의 데이터 흐름과 검증 기준을 끝까지 설명하는 편이 오래 남습니다.

확장 과제
다른 도메인에도 옮길 수 있는 구조

노트 앱이라면 Document를 노트 상태로, 필터를 문서 변환 명령으로 바꿔 생각해 보세요. 명령 레지스트리·실행 취소·직렬화·플랫폼 셸의 분리는 사진 편집 밖에서도 활용할 수 있습니다.

10핵심 키워드 사전

이름을 외우기보다 소스에서 맡은 역할과 연결하기
용어쉬운 설명
크레이트Rust 코드의 컴파일 단위. 이 저장소는 문서·필터·UI 등의 책임을 여러 Cargo 패키지로 나눕니다.
워크스페이스여러 패키지의 버전·의존성·빌드 설정을 함께 관리하는 묶음입니다.
잠금 파일허용 범위에서 실제 선택된 의존성 버전을 기록한 Cargo.lock입니다.
명령 레지스트리명령 ID와 설명·실행 함수를 모은 목록. 메뉴와 자동화가 같은 기능을 찾는 기준점입니다.
세션열린 문서와 활성 문서, 명령 실행 등 편집 활동의 현재 상태를 관리하는 객체입니다.
레이어픽셀·문자·도형·조정 등을 개별적으로 다루는 문서 구성 요소입니다.
마스크어디에 얼마나 효과를 적용하거나 보이게 할지 정하는 값입니다.
비파괴 편집원본 픽셀을 바로 덮어쓰는 대신 조정·필터 설정을 남겨 나중에 다시 바꾸는 편집입니다.
타일·희소 저장작은 픽셀 블록으로 나누고 실제 필요한 블록만 저장하는 방식입니다.
Arc참조 개수를 관리해 여러 소유자가 같은 데이터를 함께 보관하게 하는 Rust 타입입니다.
복사 시 쓰기읽을 때는 공유하고, 공유 데이터를 바꿀 때 필요한 부분을 복사하는 방식입니다.
스냅샷특정 시점의 상태. 실행 취소나 배경 계산이 안정적인 문서를 참조할 때 쓰입니다.
합성기레이어·마스크·불투명도·효과를 계산해 최종 픽셀을 만드는 부품입니다.
알파·미리 곱하기알파는 불투명도입니다. RGB에 알파를 미리 곱한 표현은 투명 경계의 계산에 쓰입니다.
ICC 프로파일픽셀 숫자가 어떤 색을 뜻하는지 설명해, 장치·색 공간 사이 변환에 쓰는 데이터입니다.
기준 구현·오라클다른 구현이나 출력이 맞는지 판단할 비교점입니다. CPU 합성 결과나 외부 기준 이미지가 해당합니다.
차등 테스트같은 입력을 둘 이상의 구현에 넣고 결과 차이를 검사하는 테스트입니다.
왕복 검사읽고 저장한 뒤 다시 읽어, 정한 기준에 따라 정보나 렌더 결과가 유지되는지 보는 검사입니다.
콘텐츠 주소데이터 내용의 해시를 식별자로 사용하는 방식. 같은 타일의 중복 저장을 줄이는 데 쓰입니다.
WebAssembly브라우저나 별도 런타임에서 실행할 수 있는 바이너리 코드 형식입니다.
WGSL·wgpuWGSL은 GPU 셰이더 언어이고, wgpu는 GPU 장치·버퍼·파이프라인을 다루는 Rust API입니다.
MCP외부 에이전트가 앱의 도구 목록을 발견하고 호출할 수 있게 하는 공통 프로토콜입니다.
샌드박스·연료실행 권한·메모리·계산량을 제한하는 경계와, 계산량을 소모성 예산으로 세는 장치입니다.

11참고 링크

원문 소스·공식 기술 문서·날짜가 붙은 상태 정보

11-1. 저장소와 구현 근거

11-2. 관련 기술 공식 자료

11-3. 현재 상태 (2026-10-23 확인)

항목값과 해석
저장소storytold/photocraft · TrendShift Monthly #2
스타 / 포크71,526 / 12,630 · 2026-10-23 GitHub 기준
생성일2026-09-30
주 언어Rust
최신 릴리스v0.14.0 · 2026-10-23
최근 push2026-10-23 · GitHub 기준
분석 기준 커밋ff47388 · Release 0.14.0 · 2026-10-22 20:06:08 −04:00, 한국 시간 2026-10-23 09:06:08
구성라이브러리 패키지 26개 + 앱 4개 + xtask. 예제 플러그인·퍼징 패키지는 별도
Rust 규모1,502파일 / 661,699줄. 주석·빈 줄·테스트·예제·생성 데이터 포함
기타 코드 규모WGSL 1파일 / 1,520줄, Shell 15파일 / 1,271줄, PowerShell 3파일 / 322줄, Python 2파일 / 275줄
설정·웹 진입 파일TOML 55파일 / 3,574줄, YAML 15파일 / 998줄, HTML 2파일 / 60줄. JavaScript·TypeScript 파일 0개
테스트 표시#[test] 10,656곳. 정적 문자열 집계이며 실제 실행·통과 개수와 다름
라이선스GitHub 대표 표기 Apache-2.0. 소스 패키지는 MIT OR Apache-2.0; 자산·브랜드 규정은 별도
성숙도README·로드맵 기준 초기 알파. PSD·전문 작업·생성형 기능 등의 격차 공개

코드 규모는 커밋 ff47388의 저장소 전체에서 .git을 제외하고 확장자별로 센 물리 줄 수입니다. 비어 있지 않은 마지막 줄에 줄바꿈이 없어도 한 줄로 셉니다. 파일 수나 줄 수는 기능 수·품질·개발 기간을 직접 나타내지 않습니다.

쓰기 전에 알아둘 한계
소스 학습용 기준과 실제 사용의 검증 범위

버전과 실행 환경: 코드 경로·상수·의존성은 커밋 ff47388 기준입니다. 빌드 성공, OS별 동작, 모든 실습 코드의 실행 결과와 현재 성능은 환경별 확인이 필요합니다. 날짜가 다른 README·설계 문서·점수표의 숫자를 한 시점의 실측치로 합치지 마세요.

PSD의 세 기준: 형식 파서의 바이트 보존, 문서 재저장 전후 렌더 일치, Photoshop 기준 이미지와 일치는 다른 시험입니다. crates/io/tests/it/corpus.rs의 Photoshop 코퍼스 시각 일치 하한은 190, 왕복 일치 하한은 258입니다. 하한은 해당 시험의 회귀 방지 기준이며 모든 PSD의 성공률이 아닙니다. 점수표에는 분모 256과 왕복 하한 258이 함께 나오는 불일치도 있어, 이를 백분율로 환산하지 않습니다.

지능형 선택: crates/algo/src/segment/subject.rs::select_subject_with는 SLIC·현저성·GrabCut을 결합한 전통 영상처리 경로입니다. 소스도 의미론적 분할기가 아니라고 밝힙니다. “Select Subject”라는 이름으로 학습 모델이나 생성형 AI가 구현됐다고 추정하면 안 됩니다.

재사용 범위: LICENSE-MIT, LICENSE-APACHE, NOTICE, ATTRIBUTION.md를 함께 확인하세요. 글꼴·아이콘에는 각자의 라이선스가 있고, docs/brand/LICENSE-brand.txt의 브랜드 자산은 코드 라이선스 적용 대상과 구분됩니다.