작성일: 2026-09-05. 상태: 아래 분류 작업 적용 완료. 검증 결과와 한계는 실행 기록을 따른다.
작업 기준 브랜치: refactor/runtime-safety-and-structure.
파일을 찾고, 함께 변경할 범위를 파악하고, 새 파일의 위치를 결정하기 쉬운 구조를 만든다. 기존 소스 폴더 통계와 분류 후보를 기준으로 하며, 이전 작업 결과와 보류 사항은 그대로 유효하다.
확인한 공식 자료에서는 폴더당 파일 수의 공통 상한을 찾지 못했다. 앞서 제안한 ‘30개 이상이면 검토’는 업계 표준이 아니며, 이번 계획에서는 개수에 따른 합격·불합격 기준을 두지 않는다.
| 조사 자료 | 확인한 관행 | 이번 적용 |
|---|---|---|
| LLVM Directory Layout | 기능별 라이브러리와 공개 헤더·구현·테스트 영역을 조합 | 기존 최상위 구조 안에서 도메인과 역할에 맞게 분류 |
| Chromium Source Structure | 하위 시스템별 분류, 여러 최상위 프로젝트에서 필요한 코드만 base에 추가 |
범용 폴더에 넣기 전에 실제 사용처와 소유 기능 확인 |
| Software Engineering at Google: Ownership | 디렉터리별 관리·리뷰 책임을 OWNERS로 명시 |
주요 영역의 책임과 진입점을 문서화. 담당자 지정 체계 도입은 팀 운영상 필요할 때 별도 결정 |
| Google Go Best Practices: Package size | 밀접한 코드는 함께 유지, 구별되는 개념 분리, 과도한 작은 파일 경계 | 파일 수를 맞추기 위한 분리 금지. 해당 자료는 Go 대상 권고이며 언어 독립 표준으로 취급하지 않음 |
- 기능과 변경 이유를 기준으로 묶는다. 같은 기능의 구현·전용 타입·단위 테스트를 함께 배치한다. 여러 기능을 검증하는 계약 테스트는 별도 테스트 영역 안에서 대상별로 묶는다.
- 기존 도메인 폴더를 먼저 활용한다.
utils/core/gridAnchorBounds처럼 소유 영역을 설명할 수 있는 파일은 기존utils/grid등을 우선 검토한다. 새로운core/common/shared안에 같은 혼합을 재현하지 않는다. - 각 이동 대상에 하나의 주 소유 영역을 정한다. 현재 import와 함께 수정되는 이력을 참고한다. 과거 대규모 일괄 이동의 동시 변경은 기능적 결합의 증거로 단정하지 않는다.
- 진입점과 내부 구현을 구별한다. 기존 공개 진입점은 유지하고 내부 파일은 같은 영역의 구체적인 모듈을 참조한다. 내부에서 자기 영역의 집계
index를 거쳐 돌아오는 참조를 점검한다. 모든 폴더에 재수출 파일을 만들지는 않는다. - 의존 방향을 유지한다. 하위 순수 모델·유틸리티가 상위 UI를 새로 참조하지 않게 한다. 경로 이동을 위해 Rust 가시성을 넓히거나 상태 소유자를 바꾸지 않는다.
- 깊이와 파일 수를 함께 평가한다. 한 파일만 감싸는 폴더, 의미 없는 역할별 층, 과도한 상대 경로가 생기면 분류안을 조정한다. 파일 수가 많아도 책임과 탐색 경로가 명확하면 유지 사유를 기록한다.
아래 목적지는 현재 파일 목록을 바탕으로 한 제안이다. 각 묶음 착수 시 실제 import·재수출·테스트 연결을 확인하고 파일별 이전 경로 → 새 경로 표를 확정한다. 유지하는 파일도 이유를 기록해 검토에서 누락되지 않게 한다. 파일 수는 기존 스캔의 직속 파일 수이며 테스트를 포함한다.
src,src-tauri/src전체 파일 목록과 폴더 통계를 같은 집계 방식으로 다시 저장한다.- 대상별 파일, 역할, 외부 사용처, 테스트, 경로를 참조하는 설정·스크립트를 목록화한다. 그래프에서 누락된 파일은 직접 읽어 확인한다.
- 각 영역의 주요 진입점과 허용 의존 방향을 이 문서의 실행 기록에 남긴다.
- 착수 시 타입 검사·린트·전체 테스트 결과와 기존 경고·skip 사유를 기록한다. 같은 소스에 대해 이미 확보한 결과가 있으면 기준 커밋을 명시해 활용한다.
완료 기준: 모든 후속 대상에 이동 또는 유지 판정 절차가 있고, 테스트·벤치마크 실행 대상의 기준 목록이 확보되어 있다.
대상: src/renderer/utils/core 39개.
| 현재 묶음 예시 | 목적지 제안 |
|---|---|
imageSource, imageWarmup, assetProbe와 테스트 |
utils/media/ |
typography, fontWeights, counterGlyphMetrics와 테스트 |
폰트 공통 기능은 utils/typography/, 카운터 전용 측정은 기존 utils/counter/와 사용처 대조 후 결정 |
gridAnchorBounds, gridViewportStyles, dragCursor |
기존 utils/grid/ |
counterStyles, counterAnimationPreview |
기존 utils/counter/ |
arithmeticExpression, numberStep |
공유 사용이면 utils/number/, 입력 컴포넌트 전용이면 해당 입력 폴더 |
keyEventBus, rawKeyEventBus, axisEventBus |
구독자와 발행자 확인 후 utils/input/ 등 입력 도메인으로 묶기 |
stableStringify, platform 등 나머지 |
사용처를 확인해 기존 적합 영역 또는 소수의 공용 파일로 유지. 잔여 항목 전체를 새 잡동사니 폴더로 이동하지 않음 |
완료 기준: 39개 파일 모두에 배치 근거가 있고, 공용 유틸리티가 상위 UI에 새로 의존하지 않는다. 기존 strict 검사 대상이 이동 때문에 빠지지 않는다.
| 대상 | 작업 |
|---|---|
components/main/common 33개 |
numberInput/, dropdown/, checkbox/에 구현·모델·runtime·단위 테스트를 묶는다. 단일 독립 컴포넌트는 상위에 둘 수 있다 |
components/main/Modal 34개 |
floatingPopup/, listPopup/, tooltip/ 후보를 확인한다. 여러 팝업이 공유하는 layer·chrome·exit 상태는 공통 소유자를 유지한다 |
components/main/Modal/content/pickers 37개 |
color/, font/, sound/ 묶음을 만든다. 공유 목록 UI는 상위에 두고 이미지·카운터 선택기는 독립 폴더가 필요한지 별도 판단한다 |
입력 컴포넌트 → 팝업 기반 UI → 선택기 순으로 작은 변경을 만든다. 순환 참조가 있는 묶음은 이동 전에 의존 방향을 확인한다. 구조 이동과 로직 재설계는 별도 변경으로 관리한다.
완료 기준: 구현과 전용 테스트가 함께 있으며, focus·popup layer·preview/commit·child window·닫기/취소·웹폰트 로드 취소 계약 테스트가 통과한다. lazy import와 mock 경로가 함께 반영되어 있다.
hooks/Grid41개:selection/,drag/,resize/,viewport/,contextMenu/후보로 묶는다. 키보드 처리·clipboard·commit 공용부는 실제 주 소유자를 확인한다. drag 종료와 편집 commit의 상태 소유권은 유지한다.PropertiesPanel/batch33개: geometry, style·typography, sound, note, counter, graph·knob의 기능 묶음을 우선 검토한다. 단순히 모든 파일을panels/sections/runtime으로 흩어놓지 않는다. 기능별 파일이 적거나 결합이 강하면 상위에 유지한다.- batch 조립부와 공통 commit runtime은 여러 기능이 공유하는 기존 소유 범위를 유지한다. 내부
../index참조가 있다면 구체적인 모듈 참조로 바꿀 수 있는지 확인한다.
완료 기준: 선택·혼합 drag·resize·undo/redo·batch preview/commit/cancel 계약이 유지된다. 폴더 분류 때문에 새로운 순환 import나 초기화 순서 의존이 추가되지 않는다.
이전의 낮은 우선순위는 실행 순서를 뜻하며, 이번 검토에서 제외하지 않는다.
| 대상 | 분류 제안과 확인 사항 |
|---|---|
api/modules 37개 |
editor/, plugin/, window/, app/, resources/ 후보. editor·history·gesture·preview의 관계를 유지한다. 공용 transport와 기존 API 진입점은 유지하고, 하나의 API만 남는 폴더는 필요성을 재검토한다 |
__tests__ 41개 |
editor/, panel/, popup/, rendering/, plugin/ 등 검증 계약별 분류. setup.ts는 설정이 참조하는 루트 위치를 유지하고 helper는 실제 소비 범위에 둔다 |
benchmarks 37개 |
controls/, grid/, overlay/, panel/, plugin/ 후보로 시나리오와 측정 화면을 함께 배치한다. 시나리오 하나만 있는 영역은 상위 유지도 가능하다. 공통 WebView 실행 진입점은 별도로 식별한다 |
함께 점검할 경로: package.json의 명시 테스트 경로와 직속 glob, vitest.config.ts, vite.config.*, scripts/run-interaction*, WebView 진입점, 소스를 readFileSync로 읽는 계약 테스트, mock·dynamic import, tsconfig.strict.json의 include/exclude.
완료 기준: 기존 테스트와 벤치마크 시나리오 목록이 새 경로와 일대일로 대응한다. 일반 테스트에서 성능 테스트가 skip되는 것만으로 검증을 끝내지 않고, 해당 환경 변수를 켜는 실행 명령으로 대상이 실제 실행됨을 확인한다. 측정 환경이 없으면 미검증 범위로 기록하며 완료된 것으로 표시하지 않는다. 경로 이동만으로 성능 개선을 주장하거나 기존 기준값을 다시 쓰지 않는다.
- 프론트
utils/plugin28개,components/shared26개,PropertiesPanel/layer23개와 전수 스캔의 나머지 폴더에 이동·유지 판정을 남긴다. 파일 개수만으로 자동 제외하지 않는다. - 백엔드
models14개,state13개,state/app_state12개,state/store11개를 시작으로 소유 책임을 확인한다.models의 키·편집 모델,app_state의 창·입력·생명주기 묶음은 후보이며 이동 확정 사항은 아니다. - 저장·복구·history 트랜잭션과 lock 경계, 자산 sweep·격리 정책, Rust
cfg와 플랫폼 구현 경계를 유지한다. 분류를 위해pub/pub(crate)범위를 넓히지 않는다.
완료 기준: 프론트·백엔드 전체 폴더에 검토 결과가 있고, 유지하는 폴더도 책임·탐색성·이동 비용 근거가 남는다. 백엔드를 옮긴 경우 관련 테스트와 Rust 품질 검사를 수행하고 플랫폼별 검증 범위를 명시한다.
- 영역별 검증 후 최종 소스에서
npm run type-check,npm run lint,npm run format,npm run format:check,npm test,npm run build를 수행한다. 포맷 변경과 기존 의도적 주석·Compiler 지시문을 검토한다. - CI 스크립트 변경 시
npm run test:ci와 관련 품질 검사를 수행한다. 벤치마크 경로 변경은 5단계의 실제 실행 검증을 포함한다. - Rust 변경 시
cargo check,cargo clippy --all-targets --locked -- -D warnings,cargo fmt, 관련 테스트를 수행한다. 최종 Rust 변경이 모두 모인 상태에서는 전체cargo test --locked도 수행한다. 플랫폼 전용 코드의 로컬 검증 한계를 기록한다. - strict 대상 파일의 이동 전후 목록을 대조해 검사 범위 축소가 없는지 확인한다. 새 strict 범위 확대와 타입 수정은 폴더 이동과 구분되는 후속 변경으로 기록한다.
- 같은 집계 방식으로 모든 소스 폴더를 재스캔한다. 전후 직속 파일·코드·테스트·하위 합계와 최대 깊이를 비교하고, 이동 목록을 대조해 파일 누락이 없는지 확인한다.
- 주요 영역의 책임·진입점·새 파일 배치 규칙을
AGENTS.md와 구조 문서에 반영한다. 파일 수 기반 CI 실패 규칙은 추가하지 않는다. 명확히 합의된 의존 경계가 있고 기존 도구로 표현 가능할 때만 좁은 import 제한을 추가한다.
최종 완료 기준은 파일 수 감소율이 아니라 배치 근거가 있는 구조, 누락 없는 실행 대상, 기존 동작과 검사 범위 유지, 재현 가능한 통계다. 새 경고·실패·미검증 사항은 기존 항목과 구분해 기록한다.
하나의 책임 묶음마다 이동·참조 경로 수정·해당 검증을 한 변경 단위로 만든다. 테스트 단언을 약화하거나 skip을 추가해 이동을 통과시키지 않는다. 기존 공개 진입점의 재수출은 필요한 범위만 유지하고, 모든 이전 내부 경로에 호환 파일을 남기지는 않는다.
이번 계획은 폴더 분류와 그에 필요한 참조·검증·문서 정비를 다룬다. 코드사이닝과 운영 업데이트 검증은 인증 심사 후 진행한다는 기존 결정을 따른다. 완료된 OBS panic 수정과 테스트 결정성 개선은 회귀 검증 대상으로 유지한다.
기준 커밋은 9a8ad48d다. 분류 결과와 전수 통계에 186개 폴더의 판정과 309개 파일의 이전→이후 경로를 기록했다. 전체 소스 1,224개(코드 1,190개), 파일명 기준 테스트 408개, 성능 테스트 18개 모두 이동 전후 일대일로 대응한다. strict 검사 대상 프로젝트 소스 69개도 동일하다.
| 변경 | 커밋 | 결과 |
|---|---|---|
| 유틸리티·입력·popup·picker 분류 | 79bbe257 |
utils/core 책임 분산, 공통 UI의 다파일 구현·테스트 묶음 |
| Grid·batch 분류 | f9558ad5 |
기능별 이동, batch 내부에서 상위 index를 참조하던 import를 실제 정의로 변경 |
| API·계약 테스트·벤치마크·공유 요소 분류 | 39562029 |
API 도메인·검증 대상별 분류, 실행 경로·mock·WebView import 갱신 |
| 패널 벤치마크 환경 보완 | aa226fa6 |
이동 전부터 누락된 jsdom WAAPI 모킹 추가, 원래 descriptor 복구 |
계획의 세부 배치는 사용처에 맞춰 조정했다. dragCursor는 Grid 외 UI도 사용하므로 utils/dom, stableHandlerSlots는 공유 훅의 보조 기능이므로 utils/ui, mixedValue는 batch 전용이므로 batch 상위에 두었다. 공통 note 길이 정책은 독립 파일을 감싸는 폴더를 만들지 않고 utils/noteLengthPolicy.ts로 옮겼다. shared 컴포넌트와 plugin 유틸리티도 실제 다파일 묶음이 확인되어 분류했다. 백엔드는 기존 가시성·상태 소유 경계를 유지하는 것으로 검토를 마쳤다.
- 기준선 Vitest: 373개 파일·3,604개 테스트 통과, 성능 테스트 18개 파일·18개 테스트는 기본 실행에서 skip.
- 유틸리티 이동 후: 관련 52개 파일·424개 테스트 통과.
- 입력·popup·picker 이동 후: 100개 파일·681개 테스트 통과, 경로를 조합해서 읽는 계약 테스트 1개가 이전 ColorPicker 경로로 실패했다. 파일명 목록을 수정한 뒤 해당 파일 6개 테스트 전체 통과.
- Grid·batch 이동 후: 관련 98개 파일·1,006개 테스트 통과.
- 전체 분류 후: 373개 파일·3,604개 테스트 통과, 기본 실행의 성능 테스트 18개 skip 유지. 이 skip과 별개로 아래 활성화 실행을 수행했다.
npm run type-check(일반·CI·strict), lint, format, format:check, Vite build 통과. 기존 SoundTrimModal 훅 의존성 경고 1건과 대형 청크 경고는 남아 있다.- CI 정책 테스트 15개 통과. Rust 소스·permissions는 변경하지 않았다. macOS WebView 검증에서 실제 Tauri 네이티브 개발 빌드·기동도 수행했다.
- TypeScript 999개 파일의 정적 import를 기존 경로와 재수출 정의까지 대조했으며, 이동 경로를 대응시킨 뒤 연결 대상 변경은 없었다. import와 경로 문자열을 제외한 토큰 차이도 검토해 포맷 변경과 별도 벤치마크 mock 보완임을 확인했다. 이는 모든 실행 순서의 동일성을 증명하는 검사는 아니다.
- 소스·스크립트·루트 tests에서 이전의 전체 소스 경로 참조가 남지 않았음을 확인했다.
__tests__/setup.ts, 공용 API 진입점, strict 설정은 유지했다.
18개 성능 시나리오의 활성화 명령을 모두 실행했다. jsdom 검증은 2회 측정·1회 warmup·요소 1개·burst 5의 실행 확인이며 성능 비교 자료가 아니다. 최초 실행에서 패널 토글의 Element.animate 부재가 발견되어 9a8ad48d 임시 checkout에서도 재현했다. 기존 버튼 단위 테스트와 같은 범위의 WAAPI 모킹을 별도 커밋으로 추가하고, 패널 토글의 sync/after-paint 양쪽 실행과 결과 파일 생성을 확인했다. 실제 페이드 성능은 이 jsdom 측정 범위 밖이다.
macOS WKWebView 단일·batch 패널은 기존 기본 규모(요소 500개, 40회 측정, 5회 warmup)의 4개 case를 실행해 harness 검증을 통과했다. 처음 시도한 축소 설정은 브라우저의 최소 반복 수 및 기존 성능 판정 조건과 맞지 않아 실패했으며, 판정 조건을 완화하지 않고 기본 규모로 검증했다.
상호작용 matrix의 최초 연속 실행에서는 BASE-03~BASE-09와 BASE-11의 양쪽 전략이 완료된 뒤 GRID-05의 DOM commit 대기가 실패했다. 이동 전 9a8ad48d를 별도 worktree에서 실행해 GRID-05/frame의 동일한 대기 실패도 재현했다. 이후 현재 소스에서 GRID-05·GRID-06·GRID-21의 두 전략씩 6개 run을 완료했다. 이 간헐적 WebView 측정 실패를 폴더 이동으로 해결했다고 주장하지 않는다.
최종 전체 matrix 연속 재실행도 GRID-05/legacy에서 같은 DOM commit 대기로 실패했다. 따라서 시나리오별 실행 경로 확인은 완료했지만 전체 matrix를 한 번에 통과하는 검증은 미완료다. 이전 소스에서도 실패하고 현재 소스의 분리 실행은 통과하는 측정 harness의 시작·이벤트 등록·스케줄 순서 문제를 후속 조사 대상으로 남긴다. 정확한 원인은 아직 확정하지 않았으며 시간 제한을 늘리거나 시나리오를 skip해 통과시키지 않았다. 실제 Windows WebView·ASIO 장치 검증은 이번 실행 범위에 포함하지 않았다. 기존 측정 결과 파일과 성능 tracker는 실행 전 내용으로 복구해 보존했다. 원시 실행 로그·임시 비교 자료는 /tmp/dmnote-source-organization/에 있으며, 지속 보존되는 근거는 커밋된 코드·테스트와 이 문서다.