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

lightcraft 딥다이브
— 원본을 지키며 빛을 다시 계산하는 Rust 암실

사진 편집기

빠른 판단

무엇
원본과 보정 설정을 분리해 RAW 현상·사진 정리·렌더링을 수행하는 Rust 비파괴 사진 편집기
이럴 때
로컬 사진을 별점·앨범으로 관리하고 노출·화이트밸런스·마스크·자르기를 조정해 새 이미지로 내보낼 때
입력
원본 사진·RAW와 보정 설정
결과
재편집 가능한 설정 데이터와 현상·보정한 출력 이미지
사진 편집기에서 슬라이더 하나를 움직이면 어떤 일이 벌어질까요? LightCraft는 사진 원본과 편집 설정을 분리하고, 같은 Rust 엔진으로 데스크탑·CLI·브라우저·MCP를 연결하는 비파괴 사진 편집기입니다. 이 글은 화면보다 그 뒤의 데이터 모델, 명령, 렌더링, 저장 복구를 따라갑니다. 저장소 storytold/lightcraft · ⭐14,376 · 포크 5,526 · 주 언어 Rust · v0.13.0 · 2026-10-23 GitHub 기준. 소스 설명은 커밋 23e1812 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

사진 파일을 고치는 대신, 사진을 해석하는 방법을 저장한다

LightCraft는 사진 라이브러리 관리와 RAW 현상을 한 엔진에 묶은 Rust 사진 편집기입니다. 별점·앨범으로 사진을 정리하고, 노출·화이트밸런스·마스크·자르기를 조정한 뒤 새 이미지로 내보냅니다. macOS·Windows·Linux용 앱과 WebAssembly 웹 앱, CLI, MCP 진입점이 있습니다.

편집의 중심은 바뀐 픽셀 파일이 아니라, 원본을 다시 그리는 데 필요한 설정 데이터입니다. crates/develop/src/settings.rs의 DevelopSettings는 직렬화 가능한 값입니다. 카탈로그가 이 값을 보관하고, crates/pipeline이 원본과 설정을 받아 결과를 계산합니다.

용어
비파괴 편집 · RAW 현상
비파괴 편집은 원본 픽셀을 덮어쓰지 않고 조정값을 따로 보관하는 방식입니다. RAW는 카메라 센서에서 얻은 데이터를 담는 파일 계열입니다. RAW 현상은 이를 해독하고 색·밝기를 계산해 사람이 볼 이미지로 만드는 과정입니다.
핵심 비유

원본은 식재료, 편집 설정은 레시피, 내보내기는 완성된 요리입니다

노출을 바꾸는 일은 식재료를 매번 갈아버리는 작업과 다릅니다. “조명을 한 단계 밝게”라는 레시피를 바꾸고, 원본에서 다시 요리합니다. 같은 재료로 작은 시식 접시인 미리보기와 큰 접시인 최종 출력을 만들 수 있습니다.

레시피를 저장하면 실행 취소, 프리셋, 여러 사진에 같은 조정 적용, 이전 버전 재현이 자연스럽게 연결됩니다. 그래서 이 저장소를 읽는 출발점은 필터 수학보다 설정을 어떻게 표현하는가입니다.

처음 읽을 때 잡아둘 세 가지 경계

경계실제 부품초보자가 볼 질문
입력과 설정crates/raw·crates/codecs / crates/develop파일을 읽는 일과 편집 의도를 표현하는 일이 분리되는가?
설정과 계산pipeline::render화면 없이도 같은 사진을 만들 수 있는가?
계산과 화면crates/engine / crates/ui-eguiGUI를 바꿔도 편집 규칙이 남는가?

여기서 “백엔드”는 웹 서버를 뜻하지 않습니다. 사용자 컴퓨터 안의 처리 엔진입니다. 기본 사진 편집에 계정이나 별도 데이터베이스 서버를 두는 구조가 아니며, 카탈로그는 자체 파일 저널을 사용합니다.

2왜 주목받는가

인기의 원인은 단정하지 않고, 관심을 끄는 설계와 학습 가치를 구분한다

2-1. 짧은 공개 기간에 드러난 큰 구현 범위

2026-09-30 생성된 저장소가 2026-10-23 GitHub 기준 스타 14,376개와 포크 5,526개를 기록했고, TrendShift Monthly #21에 선정되었습니다. 카메라 파일 해독부터 사진 관리, GPU 계산, 웹 빌드까지 한 저장소에 들어 있습니다. 다만 이 숫자만으로 성장 원인이나 실제 사용자 수를 알아낼 수는 없습니다.

관심을 끌 만한 지점은 코드로 설명할 수 있습니다. 한 언어로 사진 처리의 바닥부터 화면과 자동화 인터페이스까지 연결한 구조가 이 저장소의 뚜렷한 학습 가치입니다. Rust 파일은 테스트·예제 등을 포함해 719개, 물리적 줄 수로 약 32만 줄입니다. “작은 필터 데모”보다 여러 실행 환경을 가진 제품의 설계를 공부하기에 가깝습니다.

2-2. 비슷한 도구와 무엇을 비교해야 할까?

비파괴 편집 자체는 새로운 발명이 아닙니다. darktable 공식 소개도 비파괴 처리와 부동소수점 파이프라인을 설명합니다. Lightroom Classic 공식 문서에는 노출·색·마스크 등 현상 도구가 정리되어 있습니다. 비교의 초점은 화질 순위보다 개발자가 구조를 이해하고 확장할 수 있는 경로에 둡니다.

비교 대상겹치는 목적LightCraft를 공부할 이유판단의 한계
Lightroom Classic사진 관리와 현상 도구관련 규칙을 Rust 소스에서 추적하고 CLI·MCP로 연결하는 과정을 볼 수 있음소스 공개가 색 재현·카메라 호환성의 동등함을 뜻하지 않음
darktable오픈소스 비파괴 RAW 처리자체 RAW 해독기, Cargo 계층, WASM 호스트를 함께 읽을 수 있음비파괴·부동소수점 처리는 LightCraft만의 장점이 아님
직접 만든 필터 CLI이미지를 읽어 변환 후 저장변환 함수를 라이브러리·실행 취소·캐시·저장 복구까지 확장하는 사례코드량이 커서 처음부터 전부 읽는 방식은 비효율적

2-3. 사람이 쓰는 앱과 도구가 호출하는 엔진의 접점

crates/mcp/src/lib.rs에는 실행 중인 앱에 연결하는 Remote와 화면 없이 엔진 세션을 여는 Headless가 함께 있습니다. MCP를 붙이기 위해 편집 기능을 다시 만드는 대신, 이미 존재하는 명령을 다른 통로로 노출합니다. 이것은 사진 앱뿐 아니라 문서 편집기·CAD·데이터 도구에도 적용할 수 있는 설계입니다.

성숙도 읽기
기능의 존재와 작업 결과의 신뢰성은 별개입니다

README와 ROADMAP.md는 프로젝트를 알파로 분류합니다. README의 약 82% 기능 범위와 약 61% 전체 실무 준비도는 프로젝트 자체 평가이며, 독립적인 품질 점수가 아닙니다. RAW가 열려도 내장 JPEG 미리보기인지 센서 데이터 현상인지에 따라 편집 가능성이 달라집니다.

3기술 스택 전체 지도

Cargo 선언 범위와 Cargo.lock에 고정된 실제 버전을 나누어 읽는다

3-1. 전체를 묶는 Cargo 워크스페이스

루트 Cargo.toml은 members = ["crates/*", "apps/*", "xtask"], resolver = "3", edition = "2024", 공통 버전 0.13.0을 선언합니다. 라이브러리 크레이트 29개, 앱 3개, 빌드 도구 1개로 총 33개 멤버이며, 루트까지 매니페스트는 34개입니다.

용어
크레이트 · 워크스페이스 · 잠금 파일
크레이트는 Rust의 라이브러리나 프로그램 단위입니다. 워크스페이스는 여러 크레이트를 함께 관리하는 묶음입니다. Cargo.toml의 "0.36"은 허용 버전 범위이고, Cargo.lock의 0.36.2는 이 소스에서 선택된 정확한 버전입니다.

3-2. 처리 엔진: 파일·색·연산·저장

영역선언 → 잠금 버전구체적인 역할
직렬화serde "1" → 1.0.229
serde_json "1" → 1.0.151
설정·명령 인자·카탈로그 기록을 데이터로 변환. JSON의 float_roundtrip 기능 활성화
병렬 계산rayon "1" → 1.12.0CPU에서 픽셀 행과 독립 작업을 나누어 처리
일반 이미지image "0.25" → 0.25.10기본 기능을 끄고 PNG·JPEG·TIFF·WebP를 선택. 자체 코덱 계층과 함께 사용
JPEG·색 관리zune-jpeg "0.5" → 0.5.15
moxcms "0.9" → 0.9.1
JPEG 해독과 ICC 색 변환. 잠금 파일에는 전이 의존성의 다른 moxcms 버전도 공존
메타데이터quick-xml "0.38" → 0.38.4XMP 등 XML 구조를 읽고 쓰는 기반. 다른 경로의 0.41.0도 잠금 파일에 존재
날짜·오류chrono "0.4.45" → 0.4.45
thiserror "2" → 2.0.21
촬영 시각 처리와 오류 타입 정의. 수동 달력 계산과 무분별한 패닉을 줄이는 기반
선택적 모델 추론candle-core =0.9.2
faer-core =0.17.1
SAM 3 분할과 노이즈 제거 계산. 가중치 파일은 별도이며, 런타임·플랫폼 기능에 따라 포함
테스트 도구proptest "1" → 1.11.0여러 입력을 자동 생성해 파서·자료구조의 성질 검증

표의 공통 선언은 루트 Cargo.toml, 행렬 연산 의존성은 crates/denoise/Cargo.toml 기준입니다. RAW 처리는 C 라이브러리를 감싼 단일 호출로 끝나지 않습니다. crates/raw 안에서 파일별 해독, 디모자이킹, 색 변환을 직접 나눕니다.

3-3. 프론트엔드: 같은 egui 화면, 서로 다른 호스트

부품선언 → 잠금 버전읽을 위치와 역할
공통 UIegui "0.36" → 0.36.2crates/ui-egui: 패널, 그리드, 슬라이더, 메뉴와 상태 표시
앱 호스트eframe "0.36" → 0.36.2데스크탑 창과 웹 캔버스를 연결. 네이티브는 wgpu·glow, 웹은 glow 기능 사용
GPU 현상wgpu "30" → 30.0.1crates/gpu: Metal·Vulkan·DX12 연산, WGSL 셰이더
브라우저 경계wasm-bindgen "0.2.129" → 0.2.129Rust와 브라우저 API 연결. web-sys·js-sys도 함께 사용
파일 선택rfd "0.17" → 0.17.2네이티브 파일 대화상자. 하위 처리 계층에 섞지 않는 UI 의존성

웹에서도 Rust 엔진과 egui 화면을 재사용하고, 브라우저 API를 호스트에서 연결합니다. apps/lightcraft-web/Cargo.toml은 공통 crates/ui-egui와 engine을 참조합니다. 웹 화면은 WebGL2로 그리지만, 사진 현상 연산에는 아직 WebGPU 경로가 없습니다. 화면을 GPU로 표시하는 일과 사진 필터를 GPU로 계산하는 일을 구분해야 합니다.

apps/lightcraft-web/src/workers.rs의 worker_main은 별도 Web Worker에서 WASM을 실행합니다. OPFS 또는 IndexedDB에 원본·라이브러리를 저장하고, Worker를 만들 수 없으면 메인 스레드 처리로 돌아갑니다. HTML과 JavaScript는 얇은 시작 코드이며, package.json·pyproject.toml·go.mod·Dockerfile은 없습니다.

3-4. 인프라·빌드: 배포 서버보다 실행 파일과 WASM

파일실제 설정배울 점
.cargo/config.tomlxtask = "run --package xtask --"빌드·검증 작업도 Rust 명령으로 묶기
루트 프로필dist: thin LTO, 코드 생성 단위 1
web: fat LTO, opt-level = "s"
배포 크기와 계산 속도의 서로 다른 목표
웹의 예외 설정pipeline·raster·crates/raw 등은 opt-level = 3전체 크기 최적화 속에서도 픽셀 연산 성능 보존
.github/workflows/release.ymlrelease 브랜치와 수동 실행, 공용 재사용 워크플로 호출저장소의 YAML만으로 외부 워크플로 내부 동작까지 확정할 수는 없음
.github/workflows/freebsd.yml공용 FreeBSD 워크플로, craft-fonts 커밋 고정플랫폼별 검증과 폰트 입력을 명시적으로 관리
flake.nix·nix/package.nix네이티브 바이너리와 패키징 환경 구성빌드 환경도 코드로 설명. Nix CI 설정은 전체 빌드 대신 flake 평가를 명시
읽는 요령
버전은 세 군데를 함께 봅니다

루트 공통 선언, 각 크레이트의 기능 선택, Cargo.lock의 해석 결과를 연결하세요. 예를 들어 루트의 Rust 최소 버전은 1.90이지만 crates/ui-egui/Cargo.toml은 1.95를 요구합니다. README가 전체 앱 빌드에 Rust 1.95 이상을 안내하는 이유입니다.

4아키텍처 심화 분석

노출 슬라이더 한 번의 여정을 네 가지 설계 패턴으로 읽는다

전체 지도를 볼 때는 의존성과 실행 시 데이터 흐름을 나누세요. UI가 엔진을 호출하지만, 엔진은 UI 타입을 몰라야 합니다. xtask/src/layers.rs의 TABLE과 UI_MIN_LAYER = 5가 이 규칙을 코드로 표현합니다.

실행 진입점 +------------------+------------------+----------------------+ | 데스크탑 앱 | CLI / MCP | 브라우저 WASM | | eframe + egui | Headless/Remote | egui + Web Workers | +--------+---------+---------+--------+----------+-----------+ | | | +-------------------+-------------------+ v engine::Session::execute(id, params) / \ v v catalog::Op 적용 호스트의 렌더 요청 | | | | +-- 실행 취소 v v 원본 해독 raw/codecs Journal + Store | catalog.snap / catalog.log v DevelopSettings + RenderRequest | v pipeline::plan / \ v v CPU 기준 구현 GPU WGSL ^ | +-- 실패 시-+ | v 화면 / export 인코더 의존성 계층의 주요 경로 (위 계층이 아래 계층 사용) 진입점 apps/lightcraft · apps/lightcraft-cli · apps/lightcraft-web L5 ui-egui · mcp L4 engine L3 catalog · gpu · preview · merge · segment L2 pipeline · lensdb L1 raw · codecs · meta · develop · denoise · faces 등 L0 geom · color · raster · tiff · fetch · sysmem · heif

도식은 핵심 경로를 요약한 것입니다. 브라우저의 저장은 호스트가 메모리 상태와 비동기 저장소를 연결하며, 네이티브 FsStore의 파일 동기화와 동일한 방식으로 처리하지는 않습니다. 앱과 xtask는 계층 검사에서 예외로 분류됩니다. 또한 창 조작 명령과 파일 대화상자 같은 UI 전용 동작은 호스트에 남습니다.

4-1. 패턴 ① 명령 레지스트리: 입구를 늘려도 편집 규칙은 한곳에

crates/engine/src/cmd/mod.rs의 CommandSpec에는 id·label·menu·shortcut·params·enabled·run 등이 들어갑니다. 버튼의 제목과 실행 가능 여부, 실제 처리 함수를 한 명세에 모은 구조입니다.

// crates/engine/src/cmd/mod.rs의 함수 타입
pub type Run = fn(&mut Session, &Value) -> Result<Value>;
pub type Enabled = fn(&Session) -> std::result::Result<(), String>;

crates/engine/src/lib.rs의 Session::execute는 find_command(id)로 명세를 찾고, 실행 조건을 검사한 뒤 run_command로 보냅니다. 사진 ID를 명시하는 일부 명령은 선택 상태 검사를 건너뛰지만, 해당 명령이 대상 사진을 직접 검증합니다.

노출 조정의 실제 명령은 crates/engine/src/cmd/develop.rs의 develop.set입니다. 제어 ID와 숫자를 받아 controls::set을 호출하고, Session::set_develop으로 변경을 전달합니다. UI와 CLI가 “노출을 바꾸는 서로 다른 규칙”을 가질 필요가 줄어듭니다.

# CLI 사용 예시: 예제 사진을 불러와 같은 엔진 명령 호출
lightcraft-cli run --demo develop.set control=light.exposure value=0.7

# 같은 호출을 표현한 엔진 명령 데이터
{"command":"develop.set","params":{"control":"light.exposure","value":0.7}}

run_command는 명령 주위의 패닉 방어, 자동 버전, XMP 자동 쓰기, 영속 저장을 묶습니다. 저장 실패 시 NotSaved를 돌려주는 경로도 있습니다. 명령 실행 성공과 디스크 저장 성공을 구분하는 책임까지 공통 진입점이 다룹니다.

4-2. 패턴 ② 값으로 표현한 편집 + 역연산으로 되돌리기

DevelopSettings는 화면 위젯이 아닌 데이터입니다. to_json·from_json·merged를 제공하고, 숫자 제어는 crates/develop/src/controls.rs의 CONTROLS에서 ID·범위·기본값을 공유합니다.

// ControlSpec::clamp의 실제 구현
pub fn clamp(&self, v: f64) -> f64 {
    if v.is_finite() { v.clamp(self.min, self.max) } else { self.default }
}

// 학습용 사용 예시: light.exposure의 범위는 -5..=5
let mut settings = DevelopSettings::default();
let known = controls::set(&mut settings, "light.exposure", 99.0);
assert!(known);
assert_eq!(settings.light.exposure, 5.0);

crates/catalog/src/lib.rs의 Catalog::apply는 연산을 적용하고 역연산을 반환합니다. Op::SetDevelop의 경우 새 설정을 넣으면서 이전 설정을 담은 반대 연산을 만듭니다. Session::commit은 정방향 연산을 저장 대기열에 넣고, 역연산을 실행 취소 스택에 보관합니다.

비유

“노출을 0.0에서 0.7로 바꿨다”는 메모와 함께 “돌아갈 때는 0.0으로”라는 귀환 표를 받는 방식입니다. 화면 전체를 복사해 보관하는 것보다 변경의 의미가 분명합니다. 파일 이동처럼 외부 상태가 바뀌는 연산은 별도 복구 정보도 필요하므로 모든 동작이 숫자 교체만큼 단순하지는 않습니다.

슬라이더를 드래그할 때는 begin_interaction으로 시작 상태를 잡고, set_develop으로 미리보기를 갱신합니다. end_interaction은 마지막 값을 한 번의 실행 취소 단계로 확정합니다. 중간값이 100번 바뀌어도 사용자의 한 번의 드래그라는 행동 단위를 살리는 설계입니다.

설정의 의미도 버전으로 관리합니다. crates/develop/src/settings.rs의 ProcessVersion과 Process::V1·V2는 계산법이 바뀌어도 오래된 편집을 해석할 기준을 보존합니다. 파일 형식의 버전과 렌더링 알고리즘의 버전은 서로 다른 문제입니다.

4-3. 패턴 ③ CPU 기준 구현 + 단계 캐시 + GPU 대체 경로

crates/pipeline/src/lib.rs의 plan은 설정·원본·출력 크기를 결합해 공통 Plan을 만듭니다. 여기에는 기하 변환, 출력 크기, 세부 효과의 배율, geo·lin_key 같은 단계별 캐시 키가 들어갑니다.

render_cached는 render_impl을 통해 중간 결과를 재사용합니다. StageCache의 기본 capacity는 2입니다. 노출만 바뀌었다면 원본 재샘플링과 노이즈 제거를 반복할 필요가 없고, 관련된 뒷단 계산을 다시 수행합니다.

원본 + 자르기 + 출력 크기 --> 재샘플링 결과 화이트밸런스 + 보정 + NR --> 선형 이미지 효과별 반경·설정 --> 공간 처리용 중간 평면 노출·톤·색 --> 픽셀별 마무리 --> 출력 노출만 변경: 앞의 재사용 가능한 단계는 유지, 관련 뒷단 재계산 자르기 변경: 좌표와 샘플이 바뀌므로 앞단부터 새 결과 필요

빠른 편집의 핵심은 모든 계산을 더 빨리 하는 것뿐 아니라, 바뀌지 않은 계산을 다시 하지 않는 데 있습니다. 이것은 이미지 처리 밖에서도 빌드 시스템, 스프레드시트, 파생 상태 계산에 연결되는 생각입니다.

GPU는 CPU 구현과 Plan을 공유합니다. crates/engine/src/media.rs의 develop_image는 조건이 맞으면 lightcraft_gpu::try_render를 시도하고, 사용할 수 없거나 실패하면 pipeline::render_cached 또는 render로 이어집니다. 일부 효과는 GPU 처리 안에서도 CPU 단계를 섞으므로 완전한 단일 GPU 경로라고 이해하면 안 됩니다.

README와 테스트의 차이
“1/255 이내”를 모든 픽셀의 보장으로 읽지 마세요

README는 GPU와 CPU의 차이를 1/255 이내라고 소개합니다. 실제 crates/gpu/tests/equivalence.rs의 check는 8비트 sRGB 채널 차이에 대해 MEAN_LSB = 0.5 미만, MAX_LSB = 3 이하를 검사합니다. 이는 테스트 입력에서 사용하는 허용 기준이며 모든 사진·GPU의 동등성을 증명하지 않습니다.

4-4. 패턴 ④ 연산 저널 + 스냅샷: 다시 열 수 있어야 편집기다

crates/catalog/src/journal.rs의 Journal은 두 파일을 관리합니다. catalog.snap은 특정 순번까지의 전체 상태, catalog.log는 그 뒤에 추가한 연산입니다. 각 로그는 seq·crc·op를 가진 JSON 한 줄입니다.

스냅샷: seq = 40의 라이브러리 상태 로그: seq = 41, 42, 43의 연산 재시작: 스냅샷 읽기 --> 41 --> 42 --> 43 재적용 마지막 43번 기록이 쓰다 끊겼다면: 완전한 42번까지 복구 --> 잘린 꼬리 정리 --> 다음 기록 준비

encode_record는 연산 JSON의 CRC-32를 만들고, Journal::open은 스냅샷보다 뒤의 기록만 재생합니다. 손상된 마지막 줄과 중간에 생긴 손상을 구분하며, 더 새로운 형식은 파일을 손대지 않고 거부합니다. CRC는 우발적인 손상 검출용이지 위조 방지용 암호가 아닙니다.

Store 트레이트는 read·append·write_atomic 등을 정의합니다. FsStore::write_atomic은 임시 파일 쓰기, 동기화, 이름 교체를 수행합니다. 메모리용 MemStore도 같은 인터페이스를 구현하여 파일 손상을 가정한 테스트를 쉽게 만듭니다.

기본 SnapshotPolicy는 로그 2,000개 또는 16 MiB를 스냅샷 전환 판단에 사용합니다. 저장소가 백그라운드 쓰기를 지원하면 snapshot_in_background는 복사한 상태를 저장하는 동안 새 연산을 계속 받고, poll은 스냅샷 이후 기록만 로그에 남깁니다. 지원하지 않는 저장소에서는 동기식 스냅샷으로 처리합니다. 저장 도중 어느 시점에 멈춰도 무엇을 재생할지 정하는 것이 핵심입니다.

구조를 이해했는지 확인
노출 0.7이라는 값의 여정을 말로 설명해 보세요

명령 인자 → 제어 명세와 범위 검사 → 새 DevelopSettings → 카탈로그 연산과 역연산 → 저널 저장 → 렌더 계획과 캐시 → CPU/GPU 계산 → 화면 또는 내보내기. 이 흐름을 따라갈 수 있으면 처음 보는 대형 앱에서도 책임의 경계를 찾을 수 있습니다.

5디렉토리 구조 해부

파일 목록을 외우기보다, 질문마다 들어갈 입구를 정한다
lightcraft/ ├── Cargo.toml / Cargo.lock 공통 설정과 의존성 버전 ├── apps/ │ ├── lightcraft/ 데스크탑 시작·창·제어 채널 │ ├── lightcraft-cli/ 명령 호출·변환·MCP·스냅샷 │ └── lightcraft-web/ WASM·브라우저 저장·Worker ├── crates/ │ ├── geom/ color/ raster/ 좌표·색·픽셀 버퍼 │ ├── tiff/ raw/ codecs/ 컨테이너와 이미지 해독·인코딩 │ ├── meta/ develop/ 메타데이터·편집 설정 │ ├── pipeline/ gpu/ CPU 기준·GPU 현상 │ ├── catalog/ preview/ 라이브러리 저장·미리보기 │ ├── engine/ 세션·명령·가져오기·내보내기 │ ├── ui-egui/ mcp/ 화면과 자동화 인터페이스 │ ├── denoise-core/ denoise/ 노이즈 제거 데이터·추론 │ ├── faces/ segment/ 얼굴·SAM 3 분할 │ ├── merge/ inpaint/ 사진 병합·영역 채우기 │ ├── lensdb/ geocode/ 렌즈 정보·위치 이름 │ └── 기타 크레이트 scenes·slideshow·fetch·heif 등 ├── xtask/ 검증·웹 빌드·계층 검사 ├── docs/ 프로토콜·기능·한계 문서 ├── assets/ 아이콘·폰트·프로필과 출처 ├── packaging/ 운영체제별 배포 구성 ├── nix/ · flake.nix Nix 환경·패키지 └── .github/workflows/ 플랫폼·패키징·릴리스 작업
하고 싶은 질문먼저 읽을 파일이어 읽을 곳
슬라이더 범위는?crates/develop/src/controls.rscrates/engine/src/cmd/develop.rs
편집은 어디에 저장?crates/catalog/src/model.rscrates/catalog/src/journal.rs
CPU는 무엇을 계산?crates/pipeline/src/lib.rscrates/pipeline/src/local.rs·crates/pipeline/src/finish.rs와 해당 테스트
GPU 실패 후에는?crates/engine/src/media.rscrates/gpu/src/lib.rs·crates/gpu/tests/fallback.rs
RAW를 어떻게 읽나?crates/raw/src/lib.rscrates/raw/src/vendor/·crates/raw/src/demosaic/·crates/raw/src/color.rs
MCP 도구는 어디서?crates/mcp/src/tools.rscrates/mcp/src/server.rs·crates/mcp/src/backend.rs·crates/mcp/src/headless.rs
웹에 사진이 남는가?apps/lightcraft-web/src/store.rsapps/lightcraft-web/src/backend.rs·apps/lightcraft-web/src/files.rs·apps/lightcraft-web/src/backup.rs

crates/engine 약 92,000줄과 crates/ui-egui 약 82,800줄을 처음부터 모두 읽기보다, 약 2,800줄의 crates/develop에서 설정과 제어 ID를 파악하는 편이 좋습니다. 다음으로 명령 한 개와 관련 테스트를 이어 읽으면 읽을 범위를 좁힐 수 있습니다.

저장소 문서의 숫자는 작성 시점에 따라 다릅니다. docs/architecture.md의 약 202,000줄·505파일·라이브러리 24개는 2026-10-10의 집계입니다. 커밋 23e1812의 319,925줄·719개 Rust 파일·라이브러리 29개와 구분해야 합니다.

비유

대형 병원 지도를 외우기보다 “접수는 어디, 검사는 어디, 기록은 어디”를 아는 것과 같습니다. 파일 이름은 위치이고, 타입과 함수 사이의 호출은 실제 동선입니다. 디렉토리 트리 다음에는 반드시 명령 하나의 동선을 따라가 보세요.

6학습 포인트 (기술별)

라이브러리 이름보다, 이 코드에서 배울 판단을 정리한다

6-1. Rust: 큰 데이터를 복사하지 않고 안전하게 공유하기

crates/raster/src/lib.rs는 Rgb32f = Image<[f32; 3]>와 Rgba8 = Image<[u8; 4]>를 구분합니다. 전자는 계산용 선형 RGB, 후자는 주로 표시용 결과입니다. 타입 이름만으로 어느 단계의 버퍼인지 드러내는 장점을 먼저 보세요.

엔진과 캐시는 큰 이미지에 Arc<Rgb32f>를 사용합니다. Arc::clone은 픽셀 전체 복사와 다르지만, 내부 이미지를 clone하면 실제 데이터가 복사될 수 있습니다. pipeline::render_impl의 Arc::unwrap_or_clone과 캐시 유무 분기를 나란히 읽어보세요.

용어
Arc · Mutex · 소유권
Arc는 여러 소유자가 같은 값을 공유하도록 참조 수를 관리합니다. Mutex는 공유 상태에 한 번에 하나의 실행 흐름만 들어가도록 잠급니다. “공유할 수 있다”와 “동시에 수정해도 된다”는 다른 조건입니다.

작은 실습: 픽셀 버퍼를 가리키는 Arc 두 개를 그림으로 표시한 뒤, “참조만 복제한 위치”와 “픽셀을 복제한 위치”를 구분하세요. 디스크 캐시, 중간 단계 캐시, 원본 캐시가 각각 어떤 값을 소유하는지도 함께 적어봅니다.

6-2. 색 과학: 화면 숫자와 빛의 양을 구분하기

crates/color/src/transfer.rs의 srgb_to_linear는 화면에 저장된 sRGB 값을 빛의 계산에 맞는 선형 값으로 바꿉니다. sRGB의 0.5는 선형 밝기 약 0.214에 해당합니다. 검정과 흰색을 선형 공간에서 반씩 섞은 값 0.5를 다시 sRGB로 바꾸면 약 0.735가 됩니다.

// transfer.rs의 실제 변환 함수
pub fn srgb_to_linear(v: f32) -> f32 {
    if v <= 0.04045 { v / 12.92 }
    else { ((v + 0.055) / 1.055).powf(2.4) }
}

작은 실습: 두 숫자를 그대로 평균하는 경우와 선형 변환 후 평균하는 경우를 종이에 비교하세요. 이후 crates/raw/src/color.rs의 카메라 색 → 선형 Rec.2020 변환, crates/pipeline/src/output.rs의 출력 색 공간으로 학습 범위를 넓힙니다.

6-3. 데이터 모델: 과거 파일을 오늘도 읽게 만들기

DevelopSettings::merged는 부분 JSON을 병합한 뒤 제어 범위를 다시 맞춥니다. 누락 필드의 기본값과 ProcessVersion의 과거 해석 규칙은 저장 데이터가 오래 살아남게 하는 장치입니다. 반대로 카탈로그는 지원하지 않는 새 형식을 거부합니다. 설정 필드를 유연하게 읽는 방식과 카탈로그 형식을 엄격히 보호하는 방식을 구분하세요.

작은 실습: 노출만 있는 설정, 빈 설정, 미래 처리 버전이 있는 설정을 나란히 놓고 어떤 값이 기본값을 받으며 어떤 정보가 보존되는지 표로 정리합니다. “기본값을 추가했더니 예전 사진의 모습이 바뀐다”는 문제를 버전 모델이 어떻게 막는지도 설명해 봅니다.

6-4. 병렬 처리: 빠르게 계산하는 일과 화면을 멈추지 않는 일

crates/raster/src/lib.rs의 par_rows는 기능 설정에 따라 Rayon의 par_chunks_mut 또는 순차 chunks_mut를 사용합니다. 서로 겹치지 않는 행 조각을 나눠 작업한다는 점이 안전성과 병렬성의 연결점입니다.

한편 crates/preview/src/pool.rs의 JobPool은 작업 우선순위와 중복을 관리합니다. 네이티브의 PreviewCache::put_deferred_at은 새 이미지를 포함한 디스크 쓰기 대기량이 128 MiB를 넘으면 그 이미지의 디스크 쓰기를 생략합니다. 결과를 메모리 캐시에 넣되, 느린 저장 장치 때문에 대기열이 끝없이 늘어나지 않게 합니다.

작은 실습: 현재 보이는 썸네일 10개와 화면 밖 썸네일 1,000개의 처리 순서를 설계해 보세요. 사용자가 빠르게 다음 사진으로 이동할 때, 오래된 결과가 현재 화면을 덮지 않도록 필요한 ID도 적습니다.

6-5. WASM·MCP: 엔진을 새 실행 환경에 연결하기

apps/lightcraft-web/src/workers.rs는 컴파일된 WASM 모듈을 Worker에 전달하고, 작업을 JSON으로 보내며 결과 RGBA 버퍼를 전송합니다. 공유 메모리 기반 WASM 스레드를 요구하는 구조와 다릅니다. Worker마다 모듈 인스턴스와 캐시가 있으므로 메시지·메모리 비용까지 생각해야 합니다.

MCP 쪽 Server::handle_line은 JSON-RPC 메시지를 받아 Backend로 연결합니다. crates/mcp/src/server.rs의 기본 프로토콜 버전은 2025-06-18입니다. tools.rs의 command_tool_name은 엔진 명령 ID를 도구 이름으로 바꿉니다.

작은 실습: develop.set을 GUI 버튼, CLI 인자, MCP 인자로 각각 표현하고, 끝에서 같은 엔진 명령에 도착하는 지점을 그려보세요. 새 인터페이스를 만드는 실력은 핵심 기능을 복제하는 대신 기존 규칙에 연결하는 능력에서 드러납니다.

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

공식 설정, 문서의 측정 사례, 학습용 제안을 섞지 않는다
항목소스·문서에서 확인되는 조건학습 시 판단
Rust공통 선언 1.90, UI 크레이트 1.95. README는 전체 빌드 1.95 이상 안내앱 빌드는 1.95 이상을 기준으로 준비
운영체제macOS·Windows·Linux 네이티브. macOS 패키징 최소 버전은 11.0. FreeBSD 구성도 존재각 운영체제의 모든 버전·기기가 검증되었다는 의미는 아님
사진 계산 GPUmacOS Metal, Windows 기본 DX12, Linux 계열 Vulkan. 실패 시 CPU 경로독립 GPU를 학습의 필수 조건으로 볼 필요는 없음
macOS 배포 설정packaging/macos/Info.plist.in의 LSMinimumSystemVersion = 11.0packaging/macos/package.sh의 배포 타깃도 11.0. 개별 기기 검증과는 구분
화면 표시 GPU네이티브 eframe 렌더러, 웹 WebGL2사진 계산의 CPU 대체 경로가 창 표시까지 무조건 보장하지는 않음
웹 빌드wasm32-unknown-unknown, 잠금 버전과 맞는 wasm-bindgen CLI버전 불일치는 xtask web의 점검 대상
브라우저 저장OPFS 또는 IndexedDB, 비동기 저장과 백업 기능저장 공간 제한·삭제 가능성을 고려해 원본과 백업 보관
모델 기능SAM 3 가중치 약 3.4 GB 별도 설치, 사용 시 메모리 수 GB 필요첫 실습은 JPEG와 일반 조정으로 시작. 모델 기능은 선택
언어·폰트추가 CJK 글꼴은 craft-fonts 빌드 입력. README의 UI 언어 목록에 한국어 없음한국어 해설과 앱의 한국어 UI 지원은 별개

7-1. 사진 한 장도 메모리로 풀면 커집니다

Rgb32f의 픽셀은 RGB 세 채널에 각각 4바이트를 씁니다. 6,000 × 4,000, 즉 24 MP 이미지를 한 벌만 펼쳐도 다음 크기입니다.

6,000 × 4,000 × 3 × 4바이트
= 288,000,000바이트
≈ 274.7 MiB

# 원본 파일 크기나 앱 전체 메모리 사용량과는 다른 값
# 중간 결과·마스크·GPU 버퍼·출력 버퍼가 추가될 수 있음

crates/engine/src/memory.rs의 default_budget은 RAM을 읽을 수 있으면 그 1/4을 256 MiB~1.5 GiB 범위로 제한해 기본 예산으로 사용합니다. RAM을 읽지 못하면 1.5 GiB를 쓰며, LIGHTCRAFT_MEMORY_MB로 바꿀 수 있습니다.

캐시 예산은 프로세스 전체 메모리 사용량의 상한이 아닙니다. 소스 설명에서도 작업 중 이미지, UI 텍스처, 할당자, GPU 드라이버의 메모리를 구분합니다. 배치 내보내기는 추가 작업 메모리를 사용할 수 있으므로 예산 숫자만 보고 대용량 병렬 처리를 결정하면 안 됩니다.

7-2. 최소 사양을 꾸며내지 않는 학습 환경 제안

공통 CPU 모델·RAM·VRAM 최소값 대신, 학습용 제안으로 RAM 8 GB급에서는 작은 JPEG 한 장부터, 16 GB 이상에서는 RAW 한 장씩 처리하는 순서를 권합니다. 이는 공식 최소 사양이나 동작 보장이 아닙니다. 모델 추론·고해상도 배치에는 별도 여유가 필요합니다.

빌드도 메모리를 사용합니다. README에는 16 GB 환경에서 동시 릴리스 링크 중 메모리 부족 사례가 기록되어 있습니다. xtask/src/main.rs의 ci_jobs는 사용 가능 메모리를 1,536 MiB로 나눠 빌드 병렬 수를 잡고, 테스트 스레드는 최대 4개로 제한합니다. 일반 cargo build에 이 정책이 자동 적용되는 것은 아닙니다.

# 독자가 소스 빌드를 선택할 때 참고할 명령
CARGO_BUILD_JOBS=1 cargo build --release -p lightcraft -p lightcraft-cli

7-3. 성능 숫자는 조건과 함께 읽기

README의 24 MP RAW·Apple M4 Pro 사례는 슬라이더 약 4 ms, 2.5 MP 보기 약 30 ms, 전체 크기 내보내기 약 0.3초입니다. 프로젝트 문서의 측정 사례이며, 보편적인 보장 수치가 아닙니다. 입력 형식, 캐시 상태, 효과, 출력 크기, GPU에 따라 시간이 달라집니다.

플랫폼 차이
브라우저와 네이티브의 기능이 모두 같지는 않습니다

브라우저의 사진 연산은 CPU 기반이고, Worker가 실패하면 메인 스레드로 돌아갑니다. 모델 기반 새 마스크 생성과 네이티브의 GPU 경로를 웹에서 그대로 기대하면 안 됩니다. 저장된 모델 마스크를 렌더링하는 기능과 모델을 실행해 새로 만드는 기능도 다릅니다.

docs/web.md는 명령 이후 브라우저 저장이 뒤따르는 지연 구간을 명시합니다. 이 구간에서 탭이 종료되면 마지막 명령을 잃을 수 있습니다. 네이티브가 저장을 완료한 뒤 성공을 응답하는 정책과 구분하세요.

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

입문부터 고급까지 네 과제, 각 단계에 관찰할 결과와 완료 기준을 둔다

읽기만으로 풀 수 있는 부분부터 시작하고, 실행·수정 실습은 독립된 연습용 사본과 복사한 사진으로 진행하세요. 아래 명령은 독자가 사용할 예시입니다. 원본 촬영본 대신 재생성 가능한 입력을 사용하면 결과를 비교하기 쉽습니다.

과제 1 · 입문

노출 0.7이 어디까지 전달되는지 지도 만들기

목표: 명령 ID, 제어 ID, 설정 필드를 구분합니다. develop.set은 행동 이름이고, light.exposure는 바꿀 숫자의 이름입니다.

  1. crates/develop/src/controls.rs에서 노출의 최소 -5, 최대 5, 기본 0, 간격 0.01을 찾습니다.
  2. crates/engine/src/cmd/develop.rs에서 제어 ID를 검사하고 값을 적용하는 분기를 찾습니다.
  3. Session::set_develop에서 설정이 연산으로 바뀌는 지점까지 화살표를 그립니다.

산출물: 파일 경로 세 개와 함수 이름, 각 단계의 입력·출력을 담은 한 장짜리 표. 완료 기준: “슬라이더를 없애도 명령이 동작하는 이유”를 코드 위치와 함께 설명할 수 있습니다.

# 설치된 CLI가 있을 때 제어 명세를 살펴보는 예시
lightcraft-cli controls --json
lightcraft-cli commands --json

# 사진 사본을 작은 결과로 내보내는 예시
lightcraft-cli render sample.jpg -o practice.jpg --size 1600 \
  --set light.exposure=0.7
과제 2 · 중급

정상 값·범위 밖 값·알 수 없는 제어를 구분하기

목표: controls::set과 ControlSpec::clamp의 책임을 검증합니다. UI에서 막는 것과 엔진 데이터 단계에서 막는 것의 차이를 배웁니다.

  1. 연습용 테스트에서 노출 0.7, 99.0, f64::NAN을 차례로 전달합니다.
  2. 존재하지 않는 light.noSuchControl도 전달하고 반환값을 비교합니다.
  3. DevelopSettings::merged로 부분 JSON을 병합했을 때 범위 보정이 이어지는지 읽습니다.

산출물: 입력, 성공 여부, 저장된 노출 값을 담은 검증 표와 작은 테스트. 완료 기준: 알려진 제어의 큰 값은 5.0, NaN은 기본값 0.0, 알 수 없는 제어는 false가 되는 이유를 구분합니다.

// 연습용 테스트의 핵심. 필요한 타입·모듈은 별도 import
let mut s = DevelopSettings::default();
assert!(controls::set(&mut s, "light.exposure", 99.0));
assert_eq!(s.light.exposure, 5.0);
assert!(controls::set(&mut s, "light.exposure", f64::NAN));
assert_eq!(s.light.exposure, 0.0);
assert!(!controls::set(&mut s, "light.noSuchControl", 1.0));
과제 3 · 중급

한 번의 드래그를 한 번에 실행 취소하기

목표: 값이 바뀐 횟수와 사용자가 느끼는 행동 횟수를 구분합니다. begin_interaction·set_develop·end_interaction을 읽고 작은 세션 실험을 구성하세요.

  1. 초기 설정을 기록하고 상호작용을 시작합니다.
  2. 노출을 0.1, 0.3, 0.7로 바꾼 뒤 상호작용을 종료합니다.
  3. undo_step 한 번으로 처음 설정, redo_step 한 번으로 마지막 설정이 되는지 확인합니다.
  4. 별도 경우로 cancel_interaction을 호출했을 때 원래 값으로 돌아가는 경로를 비교합니다.

산출물: “시작·중간·종료·취소” 상태표와 재현 가능한 테스트. 완료 기준: 미리보기 변경과 영구 기록의 시점이 다르다는 것을 설명합니다.

과제 4 · 고급

저널이 쓰다 끊겼을 때 어디까지 복구하는지 증명하기

목표: 정상 종료만 통과하는 저장 코드에서 한 단계 나아갑니다. crates/catalog/src/tests_torn_append.rs와 crates/catalog/src/tests_journal.rs를 참고하고, 실제 사진 라이브러리 대신 MemStore를 사용하세요.

  1. 스냅샷 이후 유효한 연산 두 개를 추가한 메모리 저장소를 만듭니다.
  2. 복사한 로그의 마지막 줄 끝을 잘라 손상된 마지막 기록을 만듭니다.
  3. Journal::open의 재생 결과와 LoadReport를 비교합니다.
  4. 새 연산을 다시 추가하고 열어, 잘린 조각 뒤에 기록이 붙지 않는지 확인합니다.

산출물: 정상 로그·잘린 로그·복구 후 상태의 비교표. 완료 기준: 마지막 기록 손상과 중간 기록 손상의 처리 차이, 성공을 반환하기 전에 저장을 완료해야 하는 이유를 설명합니다.

과제마다 결과 사진만 남기지 말고 입력 조건과 기대 결과를 적으세요. 특히 캐시·영속성·병렬 처리는 겉으로 같은 화면이 보여도 내부 상태가 틀릴 수 있어, 관찰 가능한 기준을 먼저 세우는 연습이 중요합니다.

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

주 4~6시간을 가정한 6주 과정, 읽은 것을 작은 결과물로 남긴다

Rust가 처음이라면 공식 입문서의 소유권·구조체·열거형·오류 처리를 먼저 익히세요. 아래 일정은 제안이며, 이미지 수학을 모두 배운 뒤 코드를 읽을 필요는 없습니다. 한 주에 새로운 개념 하나와 검증 가능한 결과물 하나를 연결하는 편이 지속하기 쉽습니다.

주차학습 주제LightCraft에서 읽을 곳남길 결과물
1주소유권, 참조, Result, Cargocrates/raster/src/lib.rs, 루트 매니페스트픽셀 버퍼·소유권 그림, 크레이트 의존성 지도
2주Serde, 스키마, 범위 검사crates/develop/src/settings.rs·crates/develop/src/controls.rs과제 1·2의 입력 검증 표와 테스트
3주명령 패턴, 역연산, 상호작용crates/engine/src/cmd/mod.rs·crates/engine/src/lib.rs과제 3의 상태 전이와 실행 취소 실험
4주선형 RGB, 재샘플링, 캐시 의존성crates/color/src/transfer.rs·crates/pipeline/src/lib.rssRGB 혼합 비교, 슬라이더별 무효화 범위 표
5주로그 재생, 원자적 쓰기, 복구crates/catalog/src/journal.rs·crates/catalog/src/store.rs과제 4와 장애 발생 시점별 복구 표
6주실행 환경 경계와 공통 규칙crates/mcp/src/server.rs·apps/lightcraft-web/src/workers.rs·GPU 테스트관심 분야 하나의 호출 흐름과 검증 계획

6주 뒤 선택할 심화 방향

진도 판단
파일을 몇 개 읽었는지보다 설명할 수 있는 경계를 셉니다

입력 검증은 어디서 끝나는가, 상태 변경은 어디서 확정되는가, 실패 시 누가 책임지는가, 어떤 입력이 캐시 키를 바꾸는가를 답할 수 있다면 학습이 진행된 것입니다. 함수 이름을 외우는 것보다 이 질문을 다른 앱에도 적용해 보세요.

10핵심 키워드 사전

코드에서 다시 만나게 될 말들을 쉬운 문장과 위치로 연결한다
용어쉬운 설명이 저장소의 연결점
RAW카메라 센서 데이터와 해석 정보를 담는 파일 계열. 제조사·압축 방식마다 해독이 다름raw::decode·RawImage
비파괴 편집원본을 덮어쓰는 대신 조정값을 따로 저장하고 필요할 때 다시 계산DevelopSettings
CFA·디모자이킹센서의 색 필터 배열에서 주변 표본을 이용해 RGB 색을 복원하는 과정crates/raw/src/demosaic/
선형 RGB숫자 변화가 빛의 양 변화에 대응하도록 표현한 RGB. 물리적인 혼합·노출 계산의 기반srgb_to_linear·Rgb32f
색 영역어떤 색 좌표계로 얼마나 넓은 색 범위를 표현하는지 나타내는 개념선형 Rec.2020 내부 처리와 출력 색 공간
ICC 프로필화면·이미지·프린터의 색 특성을 설명해 색 변환에 사용하는 데이터moxcms·crates/engine/src/display.rs
크레이트Rust에서 분리해 컴파일하고 의존성을 관리하는 라이브러리·프로그램 단위crates/의 각 Cargo.toml
특성 선택Cargo 기능 플래그로 선택적 코드·의존성을 포함하는 것heif·denoise·sam
직렬화메모리의 구조체를 JSON 같은 저장·전달 형식으로 바꾸는 것Serde와 DevelopSettings::to_json
명령 패턴사용자의 행동을 ID·인자·실행 함수로 표현하는 설계CommandSpec·Session::execute
역연산변경 이전 상태로 돌아가게 하는 반대 방향의 연산Catalog::apply 반환값
저널변경을 순서대로 덧붙여 두는 기록. 다시 읽어 상태를 재구성할 수 있음catalog.log
스냅샷특정 시점의 전체 상태. 오래된 변경을 처음부터 재생하는 비용을 줄임catalog.snap
원자적 교체중간에 섞인 파일 대신 이전 내용이나 새 내용 중 하나가 보이게 교체Store::write_atomic
CRC-32기록이 우발적으로 손상됐는지 검사하는 짧은 값. 암호 인증과는 다름encode_record
LRU 캐시오래 사용하지 않은 항목부터 비워 제한된 공간을 관리하는 캐시crates/preview/src/lru.rs
캐시 무효화입력이 달라졌을 때 이전 계산 결과를 재사용하지 않도록 판단하는 일Plan::geo·lin_key
대체 경로빠른 처리 방식이 불가능하거나 실패했을 때 다른 구현으로 완료하는 경로GPU 실패 뒤 CPU 렌더
LSB표현 가능한 가장 작은 한 단계. 8비트 채널에서는 정수 값 1의 차이CPU/GPU 동등성 테스트의 오차 단위
WASM·Web Worker브라우저용 실행 코드 형식과, 화면의 주 실행 흐름 밖에서 일을 처리하는 수단웹 앱과 worker_main
OPFS·IndexedDB사이트 단위로 파일이나 구조화 데이터를 보관하는 브라우저 저장 기능웹 라이브러리의 지속 저장
MCP·JSON-RPC도구를 연결하는 프로토콜과, 이름·인자·응답으로 원격 호출을 표현하는 메시지 형식mcp::Server와 표준 입출력
XMP 사이드카사진 옆에 메타데이터·편집 정보를 따로 보관하는 XML 기반 파일crates/engine/src/sidecar.rs·meta
처리 버전같은 설정값을 어떤 계산법으로 해석할지 고정하는 버전ProcessVersion·Process

처음부터 모든 약어를 외울 필요는 없습니다. “사진 원본은 무엇인가 → 설정은 무엇인가 → 계산 결과는 무엇인가 → 저장 기록은 무엇인가”로 묶으면 비슷해 보이는 타입과 파일도 역할을 나눠 이해할 수 있습니다.

11참고 링크

코드 근거로 돌아갈 수 있는 읽기 목록과 분석 기준일의 상태

11-1. 저장소와 핵심 소스

11-2. 프로젝트의 공식 설명과 한계

11-3. 관련 기술의 공식 학습 자료

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

항목상태기준
관심 지표스타 14,376 · 포크 5,5262026-10-23 GitHub 기준
생성일2026-09-30GitHub 저장소 메타데이터
최근 푸시2026-10-23GitHub 저장소 메타데이터
최신 릴리스v0.13.0 · 2026-10-23GitHub 릴리스 기준
분석 커밋23e1812 · 2026-10-22 · Release 0.13.0커밋 기록의 날짜. 릴리스 게시일과 구분
주 언어RustGitHub 기준, 코드 실측과도 일치
Rust 규모719파일 · 319,925줄커밋 기준 물리적 줄 수, 테스트·예제·빌드 코드·주석·빈 줄 포함
GPU 셰이더WGSL 11파일 · 1,920줄언어별 확장자 실측
보조 언어Shell 12파일·1,027줄
Python 5파일·577줄
Nix 2파일·337줄
언어별 확장자 실측
웹 시작 코드JavaScript 1파일·15줄
HTML 1파일·49줄
WASM 빌드가 생성하는 접착 코드는 집계에 포함하지 않음
패키지 구성라이브러리 29개 · 앱 3개 · xtask 1개루트 Cargo 워크스페이스 구성
라이선스GitHub 표시 Apache-2.0
공통 소스 선언 MIT OR Apache-2.0
Cargo.toml·LICENSE-MIT·LICENSE-APACHE·NOTICE 확인
개발 단계알파README·ROADMAP의 자체 분류

줄 수는 파일 확장자별 물리적 행의 합입니다. 실행되는 제품 코드만의 크기, 구현 품질, 개발 시간과 같지 않습니다. crates/engine 92,033줄, crates/ui-egui 82,846줄, crates/raw 38,311줄에는 해당 디렉토리의 테스트·예제도 포함됩니다.

쓰기 전에 알아둘 한계
실습에는 좋은 재료지만, 모든 사진의 완벽한 현상을 보장하지 않습니다

호환성과 성숙도: 카메라·압축 변형에 따라 센서 데이터 대신 내장 JPEG를 표시할 수 있습니다. 색 보정, 카메라별 검증, HDR 화면 표시 등 미완성 영역이 남아 있습니다. 기능 체크 표시와 실제 촬영본의 품질을 구분하고, 원본·카탈로그 백업을 보관하세요.

수치의 범위: 기능 완성도·속도는 프로젝트 문서의 평가와 측정 사례입니다. 정적 소스와 테스트의 존재가 모든 플랫폼에서의 통과나 실측 성능을 뜻하지 않습니다. GPU·모델·사진 표본이 없으면 관련 테스트가 건너뛰어질 수 있으며, xtask는 그 사유를 보고하도록 설계되어 있습니다.

로컬 처리의 선택적 예외: 기본 편집은 로컬 중심이지만, crates/denoise/src/remote.rs에는 선택적 원격 노이즈 제거가 있습니다. 이를 설정하면 이미지 타일이 다른 컴퓨터로 전송되며 연결 자체는 암호화되지 않습니다. SAM 3 원격 실행도 별도 선택 기능입니다. “어떤 설정에서도 네트워크 전송이 없다”는 설명은 정확하지 않습니다.

라이선스 범위: 공통 워크스페이스의 이중 라이선스와 달리 crates/segment/Cargo.toml은 Apache-2.0을 명시합니다. 모델 가중치와 브랜드 자산에는 별도 조건이 있으므로, 소스 라이선스를 모든 배포물에 그대로 적용하면 안 됩니다.

구현의 경계: “순수 Rust”는 시스템 의존성까지 없다는 뜻이 아닙니다. 운영체제 API·그래픽 드라이버를 사용하고, crates/sysmem에는 macOS 메모리 반환용 FFI가 격리되어 있습니다. 외부 공용 CI 워크플로의 내부 구현이나 전체 변경 이력은 이 커밋의 소스 범위를 넘어섭니다.