347 lines
19 KiB
Markdown
347 lines
19 KiB
Markdown
# SK 라인 이동 인수인계서
|
|
|
|
> 최초 작성일: 2026-04-22
|
|
> 최근 갱신일: 2026-05-06 (옵션 B 분리 — SK 라인 전용으로 축소)
|
|
> 대상: 후임 AI 엔지니어 또는 개발 관리자
|
|
> 작업 브랜치: `hotfix/dev_yscha`
|
|
|
|
> **이 문서의 범위**: Straight Skeleton(SK) 라인 이동·계산 트랙만 다룹니다.
|
|
> SK 외 작업은 다음 별도 문서를 보세요:
|
|
> - `UI_MODAL_HANDOVER.md` — 모달/다이얼로그/계산기 토글
|
|
> - `API_STABILITY_HANDOVER.md` — API 안정화 / 인증 / 세션
|
|
> - `MODULE_CIRCUIT_HANDOVER.md` — 모듈/배치/회로/처마/티켓 시리즈
|
|
> - `HANDOVER_INDEX.md` — 진입점 인덱스
|
|
|
|
---
|
|
|
|
## 0. 이 문서가 뭔가요?
|
|
|
|
이 프로젝트(Qcast Front)에서 가장 까다롭고 위험한 부분인 **"지붕 모양을 바꾸면 내부 선이 깨지는 문제"** 를 어떻게 풀어왔는지 정리한 인수인계 문서입니다.
|
|
|
|
이 문서를 읽고 나면:
|
|
1. 지붕 도면이 어떤 부품으로 구성돼 있는지 알 수 있고,
|
|
2. 어떤 버그가 있었고 어떻게 풀었는지 따라갈 수 있고,
|
|
3. 다음에 손대면 안 되는 코드와, 손대도 되는 코드를 구분할 수 있게 됩니다.
|
|
|
|
### 같이 봐야 하는 자료
|
|
- 메모리 인덱스: `~/.claude/projects/-Users-cha-Works-Projects-qcast3-qcast-front/memory/MEMORY.md`
|
|
- 모든 정책 결정·임계값·"왜 이렇게 했는지" 가 항목별로 들어 있습니다.
|
|
- **작업을 시작하기 전에 반드시 한 번 통독하세요.**
|
|
|
|
---
|
|
|
|
## 1. 도메인 1분 설명 — 지붕 도면은 어떻게 구성되나
|
|
|
|
이 앱은 **건물 지붕을 그리는 CAD 같은 도구**입니다. 사용자가 건물 외곽선을 그리면, 시스템이 자동으로 지붕(처마)과 그 안쪽 선(용마루 등)을 계산해 그려줍니다.
|
|
|
|
### 1.1 화면에 나오는 선 3종류
|
|
|
|
| 선 이름 | 색깔 | 누가 만드나? | 어디에 저장되나? |
|
|
|---------|------|--------------|------------------|
|
|
| **wallLine (외벽선)** | 초록색 | **사용자가 직접** 그리고 움직임 | `wall.lines`, `wall.baseLines` |
|
|
| **roofLine (처마선)** | 검정색 | 외벽선에서 일정 거리(offset)만큼 바깥쪽으로 자동 계산됨 | `roof.points` |
|
|
| **innerLine (내부선)** | 빨강 등 | 자동 계산 + 사용자가 추가한 용마루/추녀 | `roof.innerLines`, `canvas.skeletonLines` |
|
|
|
|
> 핵심: 사용자가 만지는 건 **외벽선** 뿐이고, 나머지는 자동 계산입니다. 그래서 외벽선이 움직이면 처마와 내부선이 다 같이 다시 계산됩니다.
|
|
|
|
### 1.2 "Straight Skeleton" (줄여서 SK) 가 뭔가요?
|
|
|
|
지붕 안쪽의 용마루·추녀 같은 선을 자동으로 그어주는 **수학 알고리즘**입니다. 다각형(외벽선)을 입력으로 주면, 안쪽으로 똑같은 속도로 줄어드는 가상의 선들이 만나는 자취가 나옵니다. 이게 곧 용마루 라인이 됩니다.
|
|
|
|
이 프로젝트의 가장 큰 어려움은 **이 SK 알고리즘이 입력 값에 매우 예민**해서, 외벽선을 조금만 잘못 움직여도 결과가 무너진다는 점입니다.
|
|
|
|
### 1.3 외벽선 이동의 4가지 방향
|
|
|
|
사용자가 외벽선을 잡고 끌면, 시스템은 그걸 다음 4가지로 분류합니다:
|
|
|
|
- 위치: `top` (위) / `bottom` (아래) / `left` (왼쪽) / `right` (오른쪽)
|
|
- 방향: `in` (안쪽으로) / `out` (바깥쪽으로)
|
|
|
|
이 8개 조합을 다 처리해야 합니다.
|
|
|
|
### 1.4 "오버 이동" 이란?
|
|
|
|
외벽선을 너무 많이 바깥쪽으로 끌면, 안쪽 도형이 뒤집혀 버립니다(예: ㄴ자가 ㄱ자가 됨). 이걸 **"경계를 넘었다(crossed)"** 고 부릅니다.
|
|
|
|
이 프로젝트의 핵심 골칫거리가 바로 이겁니다:
|
|
- 사용자가 실수로/일부러 너무 많이 끌었을 때
|
|
- 그동안 사용자가 힘들게 추가한 보조선들이 사라지면 안 됨
|
|
- 동시에 SK 자동 계산은 정상적으로 다시 동작해야 함
|
|
|
|
### 1.5 4가지 판정 상태
|
|
|
|
이동이 들어오면 시스템이 다음 중 하나로 판정합니다:
|
|
|
|
| 판정 | 의미 |
|
|
|------|------|
|
|
| `ok` | 안전한 이동, 정상 처리 |
|
|
| `on-boundary` | 평행한 위치까지 딱 맞게 도달, 정상 처리 |
|
|
| `crossed` | 경계를 넘었음(뒤집힘), **특별 경로**로 처리 |
|
|
| `unknown` | 판정 불가, 일단 기존 흐름 진행 |
|
|
|
|
판정 함수: `verifyMoveBoundary()` (`src/util/skeleton-utils.js:510`)
|
|
|
|
---
|
|
|
|
## 2. 어떤 문제가 있었나 (수정 전 증상)
|
|
|
|
1. 외벽선을 우측 상단으로 강하게 끌면, 화면에 **있지도 않은 가짜 수직선**이 그려졌습니다.
|
|
2. 외벽선을 **두 번 연속 이동**하면, 첫 번째 이동 결과가 사라지고 도형이 무너졌습니다.
|
|
3. 경계를 넘는 이동을 하면 **사용자가 그동안 추가한 보조선들이 다 사라졌습니다**. — 이게 사용자가 가장 싫어하던 문제입니다.
|
|
|
|
---
|
|
|
|
## 3. 왜 이런 문제가 났나 (근본 원인)
|
|
|
|
### 3.1 좌표 기록이 짧게 잘려서 저장됐음
|
|
|
|
이동 후에 도형의 좌표를 `lastPoints` 라는 변수에 저장하는데, 이 저장 과정에서 **점 일부가 빠졌습니다**. 그래서 그다음 이동이 들어왔을 때 원본(`roof.points`) 과 **점 개수가 안 맞아서** 매칭이 깨졌습니다.
|
|
|
|
### 3.2 보정 함수가 항상 "원본"만 기준으로 비교했음
|
|
|
|
오버 이동을 감지하는 함수(`calcOverCorrectedPoints`) 가 1차 이동 결과를 무시하고 항상 원래 도형하고만 비교했습니다. 그래서 2차 이동에서 오버가 감지되면 **1차 이동도 같이 되돌려버리는** 부작용이 있었습니다.
|
|
|
|
### 3.3 화면을 먼저 지운 뒤 다시 그렸음
|
|
|
|
`drawHelpLine` 함수가 SK 재계산을 하기 **전에** 캔버스를 먼저 다 지웁니다. 그래서 SK 재계산 도중에 문제가 생겨도 **화면은 이미 텅 빈 상태**가 됩니다.
|
|
|
|
> **(2026-05-06 현재)** 위 분석은 옛 정책 기준입니다. 지금은 `crossed` 일 때 `drawSkeletonRidgeRoofFromBaseLines` 라는 **오버 전용 그리기 함수**로 갈아타서, 화면이 비어 있어도 곧바로 다시 채워줍니다. 또 §6.1 의 클램프 정책 덕분에 애초에 `crossed` 자체가 잘 발생하지 않습니다.
|
|
|
|
---
|
|
|
|
## 4. 어떻게 풀었나 (2026-04-22 시점 작업)
|
|
|
|
### 4.1 새로 추가한 함수들 (기존 함수는 절대 안 건드림)
|
|
|
|
| 함수 | 위치 | 한마디로 |
|
|
|------|------|---------|
|
|
| `buildRawMovedPoints(roofId, canvas)` | `skeleton-utils.js:385` | 점을 가공/축소 없이 **원본 그대로** 이동시킨 결과를 만들어 줌 |
|
|
| `verifyMoveBoundary(roofId, canvas)` | `skeleton-utils.js:510` | "이 이동이 안전한가?" 를 4단계 (`ok`/`on-boundary`/`crossed`/`unknown`)로 판정해 줌 |
|
|
| `calcOverCorrectedPointsSafe(...)` | `skeleton-utils.js:3222` | 기존 보정 함수가 "이전 이동 결과"를 잊어버리는 문제를 우회. 이전 결과를 합쳐서 가짜 원본을 만들어 넘김 |
|
|
|
|
### 4.2 안전장치(가드) 3곳
|
|
|
|
옛 정책에서는 "위험 감지 → 즉시 멈춤" 이었습니다. **현재(2026-05-06)는** 멈추는 대신 **다른 경로로 가게** 바뀌었습니다.
|
|
|
|
| 어디 | 무슨 일을 하나 |
|
|
|------|---------------|
|
|
| `skeleton-utils.js:1071` (skeletonBuilder 안) | `verifyMoveBoundary` 결과 확인 → `crossed` 면 **경고만 찍고 그대로 진행** |
|
|
| `skeleton-utils.js:1331` (catch 블록) | 에러가 나도 전체 상태를 리셋하지 않음. 해당 지붕만 false 처리 |
|
|
| `QPolygon.js:467` (drawHelpLine 진입부) | 결과를 `__verdict` 변수에 기록만. 용마루 분기에서 `crossed` 면 **오버 전용 함수**로 분기 |
|
|
|
|
### 4.3 좌표 저장을 풀 길이로 보존
|
|
|
|
`skeletonBuilder` 끝부분(`skeleton-utils.js:1315`)에서:
|
|
```js
|
|
const rawMovedFull = buildRawMovedPoints(roofId, canvas)
|
|
if (rawMovedFull && rawMovedFull.length === (roof.points?.length ?? 0)) {
|
|
canvas.skeleton.lastPoints = rawMovedFull // 점 개수 보존
|
|
} else {
|
|
canvas.skeleton.lastPoints = roofLineContactPoints // 안 맞으면 기존 방식
|
|
}
|
|
```
|
|
|
|
### 4.4 처음 검증 결과 (2026-04-22)
|
|
- 1차 이동 820 → 정상.
|
|
- 2차 이동 1912 (경계 넘는 이동) → 옛 정책에서는 화면이 그대로 유지됐음.
|
|
- **현재는** 같은 입력에서 오버 전용 함수로 분기해서 SK 가 정상 재계산됩니다. 그리고 §6.1 의 클램프 정책이 들어와서 실서비스에서는 애초에 `crossed` 가 잘 안 납니다.
|
|
|
|
---
|
|
|
|
## 5. 아직 안 끝난 일 / 추적 중
|
|
|
|
### 5.1 사용자가 추가한 라인을 따로 보관하기 (Step 5, **미완료**)
|
|
|
|
**문제:** SK 가 다시 계산될 때마다 `roof.innerLines` 를 통째로 덮어씁니다. 사용자가 손으로 추가한 용마루/추녀 같은 라인은 SK 결과에 안 들어가니까 **사라질 위험**이 있습니다.
|
|
|
|
**제안:**
|
|
- `roof.userAddedLines` 라는 별도 보관함을 만들고
|
|
- 라인 추가 UI 에서 거기에 저장한 다음
|
|
- SK 결과 + 사용자 추가분을 합쳐서 `innerLines` 에 넣기
|
|
|
|
```js
|
|
roof.innerLines = [
|
|
...createInnerLinesFromSkeleton(...), // 자동 계산
|
|
...(roof.userAddedLines ?? []), // 사용자 추가
|
|
]
|
|
```
|
|
|
|
### 5.2 용마루 이동(`moveFlowLine`) 경로 판정 (**미완료**)
|
|
|
|
지금 `verifyMoveBoundary` 는 외벽 이동만 판정합니다. 용마루를 직접 잡고 끄는 경우는 항상 `unknown` 이 나옵니다. 이 경로에서도 경계 판정이 필요해지면 `buildRawMovedPoints` 를 확장해야 합니다.
|
|
|
|
### 5.3 빨강 처마 보조선 잔존 (**추적 중, 휴면**)
|
|
|
|
오버 이동을 클램프하면 빨강 색의 처마 보조선(`eaveHelpLine`) 이 화면에 남는 별건 이슈가 있습니다. 2026-04-27 이후 재개 예정 (메모리: `project_over_move_red_helper_deferred.md`).
|
|
|
|
### 5.4 v2.0 설계 (**휴면**)
|
|
|
|
v2.0 으로 가는 새 설계 방향이 있었지만 **2026-05-06 자로 휴면**입니다. 사용자 명령 전까지는 v2 관련 메모리(`project_v2_*`) 를 참조하지 마세요.
|
|
|
|
---
|
|
|
|
## 6. 꼭 지켜야 할 9가지 원칙
|
|
|
|
이 프로젝트에서 이 원칙들을 어기면 거의 반드시 새로운 버그가 생깁니다.
|
|
|
|
| # | 원칙 | 한 줄 설명 |
|
|
|---|------|-----------|
|
|
| 1 | **잘 되는 코드는 안 건드리기** | 버그 수정은 **최소 지점만**. 기존 함수 수정 금지. 필요하면 새 함수를 만드세요. |
|
|
| 2 | **고치기 전에 흐름부터 확인하기** | 한 파일만 보고 추측해서 고치지 마세요. 호출 체인 전체를 따라가서 어디를 고칠지 정한 다음 작업하세요. |
|
|
| 3 | **수정한 곳에는 태그를 남기기** | 수정한 줄 위에 `// [태그명 YYYY-MM-DD]` 주석을 달고, 작업 끝나면 git commit 메시지를 사용자에게 제시하세요 (커밋은 사용자가 직접 함). |
|
|
| 4 | **오버 이동은 평행 위치까지만 (clamp)** | 사용자가 너무 많이 끌면 평행한 위치까지만 잘라줍니다. 정책은 `OVER_MOVE_POLICY` 상수로 바꿀 수 있어요. |
|
|
| 5 | **화면 표시값과 내부 좌표는 따로** | 라벨이 흔들려 보이면 표시 단계에서 평균을 내는 식으로 보정하세요. 내부 좌표를 함부로 round 하면 곳곳에서 깨집니다. |
|
|
| 6 | **좌표 비교 허용오차는 0.5** | UI/Big.js 계산 오차 때문에 0.1 로 비교하면 자주 매칭 실패합니다. |
|
|
| 7 | **디버그 라벨은 별도 파일 만들지 말기** | `QPolygon.js` 안에 인라인으로 두고, `NEXT_PUBLIC_RUN_MODE === 'local'` 일 때만 표시되도록 가드. |
|
|
| 8 | **V1 한 트랙만** | v2.0 은 휴면 중. 사용자가 직접 명령하기 전까지 v2 관련 코드/메모리 언급 금지. |
|
|
| 9 | **moveLine + offset 이중 적용 방지** | offset 변경 후 baseLines 복원 → `drawHelpLine` 호출 시 `roof.moveFlowLine` / `roof.moveUpDown` 을 미리 설정하면 SK 빌더가 또 한 번 이동시켜 **이중 이동**이 발생합니다. 순서: `baseLines 복원 → drawHelpLine(moveLine 없이) → moveLine 속성 복원`. (메모리: `feedback_moveline_offset.md`) |
|
|
|
|
---
|
|
|
|
## 7. 전체 흐름 (사용자가 외벽선을 끌었을 때)
|
|
|
|
```
|
|
사용자가 마우스로 외벽선을 끌어다 놓음
|
|
↓
|
|
useMovementSetting.js __onMouseDown 핸들러
|
|
↓
|
|
handleSave() (line 514)
|
|
├─ roof.moveUpDown / moveSelectLine / moveDirect / movePosition 세팅
|
|
│ = "어느 라인을 어느 방향으로 얼마나 옮기는가" 정보를 저장
|
|
│
|
|
├─ ★ 안전장치: OVER_GUARD (line 895~940)
|
|
│ 너무 많이 끌었으면 평행 직전까지 잘라줌 (clamp 정책)
|
|
│ - 'clamp' (기본): 평행까지만 자르고 진행
|
|
│ - 'reject' : 아무것도 안 함
|
|
│ - 'passthrough' : 옛 동작 (위험)
|
|
│
|
|
├─ wall.baseLines 좌표를 실제로 변경
|
|
│
|
|
└─ roof.drawHelpLine() 호출
|
|
↓
|
|
QPolygon.js drawHelpLine (line 467)
|
|
├─ ★ verifyMoveBoundary 로 판정 → __verdict 변수에 보관
|
|
├─ 캔버스에서 내부 그림 다 지움 + innerLines = []
|
|
├─ 지붕 종류별로 분기:
|
|
│ - 용마루형(Eaves) + crossed → drawSkeletonRidgeRoofFromBaseLines (오버 전용)
|
|
│ - 용마루형 그 외 → drawSkeletonRidgeRoof (일반)
|
|
│ - 박공형(Gable) / 외쪽(Shed) / 패턴 → 각각 전용 함수
|
|
↓
|
|
skeletonBuilder (skeleton-utils.js)
|
|
├─ ★ 안전장치: verifyMoveBoundary (line 1071)
|
|
│ crossed 여도 멈추지 않고 경고만 찍음
|
|
├─ safeMovedPointsWithFallback → movingLineFromSkeleton
|
|
├─ wall.baseLines 직접 사용 (V1 정책)
|
|
├─ SkeletonBuilder.BuildFromGeoJSON ← 진짜 SK 계산
|
|
├─ createInnerLinesFromSkeleton (line 937 부근)
|
|
│ 끝에 가짜 ridge 제거(cull) 2단계 추가됨
|
|
│ ⚠ 사용자 추가 라인 보존(Step 5) 미완료
|
|
└─ canvas.skeleton.lastPoints 저장 (line 1315, 풀 길이 보장)
|
|
```
|
|
|
|
---
|
|
|
|
## 8. SK 라인 — 그동안 누적된 작업
|
|
|
|
> 모든 항목은 §6 의 원칙(특히 #1, #4)을 만족합니다. **기존 함수를 고치지 않고**, 진입부에 안전장치를 추가하거나 임계값을 조정하거나 끝부분에서 정리하는 식으로 처리했습니다.
|
|
|
|
### 8.1 오버 이동 정책 — 평행 직전까지 자동 클램프
|
|
- **장소**: `useMovementSetting.handleSave` 의 `OVER_GUARD` 블록 (line 895~940)
|
|
- **상수**: 파일 맨 위 `OVER_MOVE_POLICY = 'clamp' | 'reject' | 'passthrough'` (기본 `clamp`)
|
|
- **메모리**: `feedback_over_move_silent_skip.md`
|
|
|
|
### 8.2 평행 이동 시 SK 가 무너지는 문제
|
|
- **증상**: 외벽선을 평행 이동했을 때 점 개수 매칭이 깨졌습니다.
|
|
- **수정**: 좌표 변경 경로 2곳을 **인덱스 직접 매핑** 방식으로 통일.
|
|
- **메모리**: `project_parallel_collapse_fix.md`
|
|
|
|
### 8.3 정렬 기준을 `wallLines` 에서 `roofLines` 로 변경
|
|
- **이유**: wallLines 기준으로 정렬하니까 후처리에서 자꾸 어긋났습니다.
|
|
- **수정**: 정렬 master 를 `roofLines` 로. **2026-05-06**: `ensureCounterClockwiseLines` 의 그래프 키도 `0.1 round` 로 통일 (커밋 `6f41e127`).
|
|
- **메모리**: `project_index_master_roofLines.md`
|
|
|
|
### 8.4 SHOULDER_ABSORBED 임계값 통일
|
|
- 4개 경로에서 같은 임계값(< 10) 을 쓰도록 정리.
|
|
- **메모리**: `project_shoulder_absorbed_thresholds.md`
|
|
|
|
### 8.5 가짜 ridge 제거 (Phantom ridge cull)
|
|
- **증상**: SK 계산 오차로 길이 0인 가짜 ridge 가 생겼습니다.
|
|
- **수정**: `createInnerLinesFromSkeleton` 끝부분에서 **2단계로 청소** (zero-length → dead-end).
|
|
- **메모리**: `project_phantom_ridge_cull.md`
|
|
|
|
### 8.6 HIP 와 RIDGE 구분 임계값
|
|
- **증상**: 외벽 이동 오차로 ridge 가 hip 으로 잘못 분류됐습니다.
|
|
- **수정**: `addRawLine` 의 대각 판정 임계 `0.1 → 5.0mm`.
|
|
- **메모리**: `project_hip_ridge_threshold.md`
|
|
|
|
### 8.7 Hip pitch 기본값
|
|
- **증상**: hip 의 평면도/실측 토글이 동작 안 했습니다.
|
|
- **수정**: `processEavesEdge:2915` 에서 `outerLine.pitch ?? roof.pitch ?? 0` 폴백.
|
|
- **메모리**: `project_hip_pitch_fallback.md`
|
|
|
|
### 8.8 보조선(auxiliaryLine) 통합 단계 제외
|
|
- **이유**: 보조선이 통합에 끼면 코너 끝점이 사라져서 split 이 실패.
|
|
- **수정**: `integrateExtensionLines` 에서 보조선 제외.
|
|
- **메모리**: `project_aux_line_integrate_exclusion.md`
|
|
|
|
### 8.9 마루(ridge) 이동 보강
|
|
- **2차 이동에서 ridge 클릭 안 됨** → `useMovementSetting` 의 `[type]` effect 에 `ridge.selectable = true` fallback.
|
|
- **반대측 wall 오선택** → 거리 필터 전에 x/y 겹침 사전필터 추가.
|
|
- **메모리**: `project_ridge_2nd_move_fix.md`, `project_marudo_xoverlap_prefilter.md`
|
|
|
|
### 8.10 회귀 검증 5 시나리오
|
|
- 어떤 SK 수정이든 끝나면 5개 시나리오로 동작 확인 필수.
|
|
- **메모리**: `project_skeleton_regression_checklist.md`
|
|
|
|
---
|
|
|
|
## 9. 메모리 인덱스 (SK 관련, 전부 `~/.claude/projects/-Users-cha-Works-Projects-qcast3-qcast-front/memory/`)
|
|
|
|
### 원칙(`feedback_*`)
|
|
- `feedback_no_touch_working_code.md` — 잘 되는 코드 안 건드리기
|
|
- `feedback_full_analysis_before_action.md` — 흐름 먼저 확인
|
|
- `feedback_inline_change_tag.md` — 수정 태그 + 커밋 메시지
|
|
- `feedback_over_move_silent_skip.md` — 오버 이동 클램프 정책
|
|
- `feedback_moveline_offset.md` — moveLine + offset 이중 적용 방지 ★
|
|
- `feedback_display_vs_internal_values.md` — 표시값 vs 내부값 분리
|
|
- `feedback_same_point_tolerance.md` — 좌표 tolerance 0.5
|
|
- `feedback_debug_labels_local_only.md` — 디버그 라벨 인라인
|
|
- `feedback_v1_v2_dual_track.md` — V1 단일 트랙
|
|
|
|
### SK / 이동 (`project_*`)
|
|
- `project_parallel_collapse_fix.md`
|
|
- `project_index_master_roofLines.md`
|
|
- `project_shoulder_absorbed_thresholds.md`
|
|
- `project_phantom_ridge_cull.md`
|
|
- `project_hip_ridge_threshold.md`
|
|
- `project_hip_pitch_fallback.md`
|
|
- `project_aux_line_integrate_exclusion.md`
|
|
- `project_ridge_2nd_move_fix.md`
|
|
- `project_marudo_xoverlap_prefilter.md`
|
|
- `project_skeleton_regression_checklist.md`
|
|
- `project_over_move_red_helper_deferred.md` (빨강 보조선 잔존, 추적 중)
|
|
|
|
### 휴면 (참조 금지)
|
|
- `project_v2_design_direction.md`
|
|
- `project_v2_invariant_no_reset.md`
|
|
|
|
---
|
|
|
|
## 10. 인수받은 직후 추천 순서
|
|
|
|
1. **이 문서 §1~§3** 읽기 — 도메인 감 잡기.
|
|
2. **§6 의 원칙 9개** 머리에 박기.
|
|
3. **메모리 `MEMORY.md` 인덱스** 통독.
|
|
4. **§8.10 의 회귀 체크리스트 5 시나리오** 직접 실행해서 현재 동작 확인.
|
|
5. §5 의 미완료 항목 중 사용자 우선순위에 따라 착수.
|
|
|
|
---
|
|
|
|
## 마지막 한마디
|
|
|
|
이 프로젝트는 **수학적으로 예민한 SK 계산** 위에 사용자가 **수많은 수동 작업을 쌓아 올린** 구조입니다. 그래서 새 기능보다는 **회귀 방지가 항상 우선**입니다.
|
|
|
|
"잘 되는 코드는 건들지 말고", "오버 이동은 평행까지만 잘라주고", "사용자가 추가한 라인은 절대 사라지지 않게" — 이 세 줄만 머리에 두고 가시면 됩니다.
|
|
|
|
끝 (2026-05-06 갱신, SK 전용으로 분리).
|