HTML5 캔버스 기반의 웹 그림판입니다. TypeScript로 작성됐고 외부 런타임 의존성은 0개입니다 — 빌드 시 tsc 외에는 아무것도 설치하지 않아도 됩니다.
- 레이어 시스템 — 최대 5개 레이어, 추가 / 삭제 / 순서 변경 / 가시성 / 투명도, 이름 변경(두 번 클릭), dirty rect 기반 합성 렌더링
- 개선된 히스토리 — Command 패턴 (
PixelEdit,AddLayer,RemoveLayer,Reorder,SetLayerProps), 메모리 예산 기반 eviction, 히스토리 패널에서 목록 + 현재 위치 표시, JSON 직렬화/복원 - IndexedDB 저장 + 자동 저장 — 5초 주기 자동 저장 (포인터 누른 동안은 일시 정지), 마지막 프로젝트 자동 복원, 명명된 프로젝트 목록
- 벡터식 경량 JSON 저장 — 프로젝트 JSON은 빈 레이어는 픽셀 필드 자체를 생략하고, 채워진 레이어는 런 길이 인코딩(RLE)으로 압축해서 기록합니다. 1920×1280 흰 배경 프로젝트 기준 JSON 200 KB 미만(동일 조건에서 raw-RGBA 방식은 ~22 MB). 저장 시 히스토리(undo 버퍼)는 영속화하지 않아서 재실행 후 되돌리기 스택은 비어 있지만, JSON이 가벼워 저장/불러오기가 빠릅니다.
- PNG 가져오기 + 배경 제거 — 크로마 키 알고리즘 (유클리드 RGB 거리) + 안티에일리어스 feather 가장자리
- HTML5 Canvas 단일 파이프라인 — SVG/WebGL 없이 모두 캔버스
- DPR 인식 — 디스플레이 픽셀 비율과 무관하게 내부 프로젝트 해상도 유지, 이미지 품질 손실 없음
- 통합 입력 — 키보드 / 마우스 / 터치를 단일 PointerEvent 어댑터로 처리
- 폴드7 펼친 화면 (~2208×1840) — 스크롤 없이 사용 가능. 토글 가능한 우측 패널로 캔버스 영역 최대화 가능
| 카테고리 | 도구 |
|---|---|
| 기본 | 펜슬 ✏️, 지우개 🩹, 채우기 🪣, 스포이드 💧, 글자 🅣 |
| 도형 | 직선 📏, 사각형 ▭ ▬, 정사각형 □ ■, 타원 ⬭ ⬬, 원 ○ ●, 삼각형 △ ▲ |
| 디럭스 페인트 풍 | 스프레이 💨, 그라디언트 🌈, 스머지 👆, 패턴 브러시 ✦, 좌우/상하 대칭 미러, 컬러 사이클링 |
도구 패널은 좌측에 2열 그리드로 배치되어 20개 도구가 모두 한눈에 보입니다.
- 그라디언트 🌈 — 디럭스 페인트 스타일. 클릭한 점을 시드로 연결된 영역(connected region)만 그라디언트로 채웁니다. fill bucket과 같은 flood-fill 알고리즘에 같은 허용오차(tolerance) 슬라이더를 씁니다. 두 개의 분리된 도형이 있을 때 한 도형 안을 클릭하면 다른 도형에는 영향을 주지 않습니다. 빈 도형 안에서도 동작합니다 — 외곽선만 있는 사각형의 안을 클릭하면 안쪽 빈 영역만 그라디언트로 채워집니다. 색이 있던 픽셀은 알파를 보존하고 빈 픽셀은 그라디언트 색의 알파를 적용해서 visible하게 만듭니다.
- 스포이드 💧 — 클릭한 위치의 색을 추출합니다. 활성 레이어만이 아니라 모든 가시 레이어를 합성한 색을 읽습니다 (사용자가 실제로 보고 있는 색). 색을 고르면 좌측 팔레트의 컬러 피커가 즉시 갱신되어 시각적 피드백을 줍니다.
- 문지르기 (smudge) 👆 — 픽셀을 문질러 색을 섞습니다. 이미 그려진 영역에 적용하면 인접 색들이 서로 섞입니다. 빈 캔버스에서 시작해도 동작합니다 — 시작 픽셀이 비어 있으면 현재 브러시 색을 carry로 잡고, 빈 영역을 지나갈 때 그 색을 그대로 찍어 남깁니다. 색이 있는 영역에 도달하면 carry와 섞이며 그 색이 점진적으로 carry에 흡수됩니다.
- 스프레이 💨 / 패턴 ✦ — 메뉴 바의 굵기 슬라이더 라벨이 도구별로 자동 변경됩니다 (
굵기→범위→크기). 스프레이는 슬라이더로 흩뿌리는 반경을, 패턴은 칠해지는 영역의 크기를 조절합니다. 패턴 도구를 선택하면 메뉴 바에 패턴 종류 선택(dots / hatch / cross) dropdown이 나타납니다. - 글자 🅣 — 클릭한 위치에 텍스트를 스탬프처럼 찍습니다. 클릭 시 입력 프롬프트가 뜨고, 입력한 문자열이 현재 전경색 + 굵기 슬라이더에 비례한 폰트 크기 (
brushSize × 6, 최소 12px)로 활성 레이어에 렌더링됩니다. 한 번의 클릭이 한 개의 히스토리 커밋이 되어 Ctrl+Z로 되돌릴 수 있습니다.
B펜슬,E지우개,L직선,R사각형 (Shift+R칠해진 사각형),C원 (Shift+C칠해진 원),F채우기,G그라디언트,S스프레이,K스포이드,U스머지,P패턴,T글자[]브러시 굵기 감소/증가X전경/배경 색 교체Ctrl+Z실행 취소,Ctrl+Y(또는Ctrl+Shift+Z) 다시 실행Ctrl+S프로젝트 저장,Ctrl+Shift+N새 레이어,Delete활성 레이어 비우기M미러 토글
메뉴 바에는 단축키 외에도 큼지막한 ↶ / ↷ 버튼이 있고, 우측 히스토리 패널에도 같은 버튼이 있어 마우스/터치만으로도 실행 취소/다시 실행이 가능합니다. 우측 패널의 레이어/히스토리 섹션은 각각 ▼/▶ 버튼으로 접거나 펼칠 수 있고, 우측 패널 전체는 메뉴 바 좌측의 📑 패널 버튼이나 우상단의 원형 버튼으로 숨길 수 있습니다.
상태 표시줄(footer)에는 현재 활성 도구의 아이콘 + 한글 라벨 + 단축키 + 한 줄 설명이 표시되어, 처음 사용하는 사람도 어떤 도구를 골랐고 무엇을 할 수 있는지 즉시 알 수 있습니다. 도구 버튼에 마우스를 올리면 같은 정보가 툴팁으로도 나옵니다. 레이어 추가 / 5개 초과 같은 상태 변화도 status bar에 잠깐(약 1.2~2.5초) 표시되어 즉시 피드백을 줍니다.
- 행 한 번 클릭 = 그 레이어 활성화 (이름 input은 readonly이므로 어디를 눌러도 row click이 발화)
- 이름 두 번 클릭 = 이름 편집 모드, 다른 곳 클릭 또는 Enter = 저장
- 👁 토글 = 가시성, 슬라이더 = 투명도, ↑↓ = 순서 변경, ✕ = 삭제
+ 새 레이어버튼 = 추가 (최대 5개, 5개일 때 disabled)- 패널 헤더 ▼/▶ = 접기/펼치기
# 1. TypeScript 컴파일 + 테스트 실행 + release/ 폴더에 배포본 복사
./build.sh
# 2. release 폴더의 index.html을 정적 서버로 띄우거나 직접 브라우저로 엽니다.
# 예) python3 -m http.server -d release 8000빌드 산출물은 release/ 폴더에 index.html, style.css, js/, img/ 형태로 정리됩니다 (~360KB).
tsc # 컴파일
node dist/src/test/main.js # 테스트 실행160개 이상의 테스트 케이스가 다음을 검증합니다:
- 단위 테스트: util / core (Layer, LayerStack, Canvas) / history / 도구별 픽셀 검증
- 회귀 테스트: 기존 결함이 다시 들어오지 않도록 (
reset/replace리스너 보존, 펜슬 perf, 도형 commit,pointerleave처리 등) - 통합 테스트: 실제 production
InputAdapter에 모의PointerEvent를 dispatch해서 도구 → 히스토리 → 레이어 픽셀까지 end-to-end 파이프라인 검증 - UI 테스트: LayerPanel 토글 상태 머신
- Strict 모의 캔버스: 잘못된
drawImage호출 등 실제 브라우저가 throw하는 모든 호출을 모의에서도 throw — "테스트는 통과하는데 실제 기기는 망가짐" 종류의 버그를 사전 차단
painter/
├── README.md # 이 파일
├── history.md # 다중 에이전트 작업 이력
├── build.sh # tsc → 테스트 → release/ 복사
├── tsconfig.json
├── legacy/ # 원본 vanilla JS 페인터 (참고용)
├── src/
│ ├── index.html
│ ├── style.css
│ ├── img/ # 도구 / 컬러 아이콘
│ ├── app/ # PainterApp 오케스트레이터, main 엔트리
│ ├── core/ # Canvas, Layer, LayerStack
│ ├── history/ # Command, CommandHistory, Commands
│ ├── tools/ # 모든 도구 + ToolRegistry, StrokeUtil
│ ├── input/ # InputAdapter, Shortcuts
│ ├── storage/ # IndexedDBStore, AutoSaver
│ ├── io/ # PngImporter, BackgroundRemover, ProjectCodec
│ ├── ui/ # Toolbar, Palette, LayerPanel, HistoryPanel, ProjectPanel
│ ├── util/ # Rect, Color, Uid, Events
│ └── test/ # 단위/통합/회귀/UI 테스트 + 모의 (Canvas, IndexedDB, DOM)
└── release/ # 빌드 산출물 (gitignored)
이 프로젝트는 한 명의 관리자(코디네이터) 에이전트가 기획 / 개발 / 검증 에이전트의 작업을 조율하는 방식으로 진화해 왔습니다. 라운드별 의사 결정과 발견된 버그, 수정 내용은 history.md에 한글로 정리되어 있습니다.
MIT License