# 4차 — 로컬 머지툴 사용성 (2026-09-10 ~)
지시서: [작업지시/4차.md](../작업지시/4차.md)
---
## D-1. `doc/history/` 를 만들고 진행상황을 나눴다 — **완료** (2026-09-10)
**왜** - `진행상황.md` 가 721줄이었고 한 파일에 다섯 가지가 섞여 있었다.
주차 목표 · 차수별 상태 표 · 항목별 상세 · 미룬 것 · 완료 기록.
"3차 D-3 에서 무슨 판단을 했더라" 를 찾으려면 스크롤로 뒤져야 했다.
이렇게 나눴다.
```
doc/
진행상황.md 73줄. 지금 대기·진행 중인 것만
작업지시/
3차.md · 4차.md · 머지툴보강.md · 사용성보강.md
history/
changelog.md 한 줄 요약 + 날짜별 문서 목록
2026-09-08-2주차.md 90줄
2026-09-09-3차.md 565줄
2026-09-10-4차.md 이 파일
```
**옮기기만 하고 내용은 고치지 않았다.** 지시서가 그렇게 시켰다 - 다시 쓰면 그때 무슨 판단을
했는지가 흐려진다. 옮긴 자리에 날짜와 제목만 얹었다.
**지시서를 `doc/작업지시/` 로 옮긴 것은 `git mv` 로 했다.** 새로 쓰고 지우면 이력이 끊긴다.
`git log --follow` 로 옛 이력을 그대로 따라갈 수 있다.
**링크는 열다섯 개가 전부 살아 있다.** 옮기면서 다섯 군데를 고쳤다.
`doc/작업지시-3차.md` → `작업지시/3차.md`, 지시서 안의 `doc/진행상황.md` → `../진행상황.md`,
`작업지시-머지툴보강.md` → `머지툴보강.md`. 문서 전체를 훑어 남은 옛 경로가 없는 것을 확인했다.
**곁들여 하나** - 미룬 것 목록에 `최근 파일 목록` 이 남아 있었다. 4차 B-1 로 올라온 항목이라
두 자리에 다르게 적혀 있었다. 미룬 것 쪽에서 뺐다.
**판정 기준 확인**
| 기준 | 결과 |
|---|---|
| 진행상황.md 200줄 아래 | 721 → **73줄** |
| 대기·진행 중인 것이 한 화면에 | 4차 열세 항목이 한 화면에 들어온다 |
| 끝난 차수 상세를 링크 한 번으로 | changelog 한 줄 → 날짜별 파일 |
| 링크가 깨지지 않음 | 15개 전부 정상 (스크립트로 확인) |
---
## D-2. 앞으로의 기록 규칙을 정했다 — **완료** (2026-09-10)
**왜** - D-1 로 자리를 나눠 놓기만 하면 다시 한 파일로 쌓인다. 어디에 적을지를 규칙으로 못박아야 한다.
지시서 [작업지시/4차.md](../작업지시/4차.md) 의 공통 규칙을 고쳤다. 항목 하나가 끝나면 세 군데에 남긴다.
1. **상세**는 `doc/history/YYYY-MM-DD-주제.md` — 원인 · 판단 · 확인한 결과
2. `진행상황.md` 은 **상태만** — 여기에 상세를 적지 않는다
3. `history/changelog.md` 에 한 줄 — 날짜 · 무엇을 왜 · 커밋
**곁들여 둘**
- 진행 순서를 `A → B → C` 에서 **`D → A → B → C`** 로 고쳤다. D 를 먼저 하라는 말이 D 묶음 안에만
있어서, 공통 규칙만 읽으면 A 부터 시작하게 되어 있었다. 규칙과 본문이 어긋나 있었다.
- 플랫폼 changelog 경로를 `UbisamPlatform/doc/history/changelog.md` 로 풀어 적었다.
MergeHub 에도 같은 이름의 파일이 생겨서 그냥 `doc/history/changelog.md` 라고 쓰면 어느 쪽인지 알 수 없다.
**두 파일은 기준이 다르다** - 플랫폼은 "다른 소비 프로그램에 미치는 영향", MergeHub 는 "무엇을 왜".
**판정 기준**은 4차 A~C 를 진행하는 동안 이 규칙대로 쌓이는지로 본다. 지금부터 적용한다.
---
## A-1. 저장하지 않은 편집이 말없이 사라지던 것 — **완료** (2026-09-10)
### 먼저 조사한 것 — 어느 길로 들어오면 사라졌나
`HomeViewModel` 에서 본문을 다시 읽는 자리를 전부 훑었다. 열한 군데였고 성격이 셋으로 갈렸다.
| 길 | 어떻게 들어오나 | 판정 |
|---|---|---|
| `OnPlainCompareRequested` | 폴더 비교 더블클릭 · 폴더 검색 결과 · 끌어다 놓기 · 탐색기 우클릭 · `git mergetool` | **막음** |
| `PickLeftFile` / `PickRightFile` | 기준 파일 · 내 파일 단추 | **막음** |
| `DropFile` | 경로 칸에 파일을 놓기 | **막음** |
| `OnSelectedFileChanged` | Git 탭 파일 목록에서 다른 파일 고르기 | **막음** (취소하면 목록 선택도 되돌린다) |
| `OnSelectedNodeChanged` | 이력에서 다른 커밋 고르기 | **막음** (취소하면 커밋 선택도 되돌린다) |
| `OnRemoteBranchChanged` | 3-Way 켜고 끄기 | **막음** (취소하면 브랜치도 되돌린다) |
| `SwitchDemoAsync` | 데모 폴더 전환 | **막음** |
| `RevertAll` | 전체 되돌리기 단추 | **막음** (되살릴 수 없어 한 번 묻는다) |
| `ReloadLinesFrom(keepAsEdited: true)` | 비교 규칙 변경 · 무시 규칙 변경 | **안 막음** — 편집을 지키도록 이미 만들어져 있다 |
| `LoadDefaultPlainFiles` | 켤 때 기본 파일 열기 | **안 막음** — 아직 고친 것이 있을 수 없다 |
| `RefreshCommits` → `LoadCommitAsync` | 커밋·체크아웃 뒤 목록 다시 세우기 | **일부러 안 막음** (아래) |
**비교 규칙을 바꾸는 길에 손대지 않은 것이 중요하다.** 지시서가 그렇게 못박았고, 실제로 그 길은
`keepAsEdited: true` 로 편집을 지킨다. 여기까지 확인 창을 붙였으면 규칙 하나 바꿀 때마다 창이 떠서 못 쓴다.
**`RefreshCommits` 는 일부러 두었다.** 사용자가 고른 것이 아니라 프로그램이 목록을 다시 세우는 자리다.
그 한가운데 창을 띄우면 무엇에 답하는 창인지 알 수 없고, 취소해도 목록은 이미 새로 세워진 뒤라
되돌릴 자리가 없다. `_suspendGuard` 로 그 구간만 묻지 않게 했다. **남은 구멍이라는 것은 인정한다** -
커밋·체크아웃을 한 뒤에는 고치던 것이 다시 읽힌다. 다만 그 조작들은 대개 저장한 뒤에 하는 일이다.
### 어떻게 막았나
`ConfirmDiscard(무엇을 하려는지)` 하나로 모았다. 고친 것이 없으면 **묻지 않는다** -
매번 뜨는 창은 읽지 않고 누르게 되고, 정작 위험할 때도 그냥 눌러 버린다.
세 갈래로 묻는다. **예/아니오로는 "버리고 가기"와 "그만두기"가 구분되지 않는다** -
아니오를 눌렀는데 그대로 열려 버리면 그것이 곧 잃는 것이다.
플랫폼에 `MessageUtil.ShowChoice` 를 새로 넣었다(플랫폼 `ccfed6f`).
`저장하고 계속` 은 **커밋하지 않고 파일에만 쓴다**(`SaveWorkingCopy`). Git 모드의 저장은 커밋 메시지가
있어야 하는데 확인 창에서 메시지를 받을 수는 없다. 커밋은 사용자가 따로 할 일이다.
작업 게이트를 타지 않는다 - 확인 창이 UI 스레드에서 답을 기다리는 중이라 게이트를 기다리면 서로 막힌다.
**저장이 실패하면 넘어가지 않는다.** 여기서 넘어가면 저장한 줄 알고 잃는다.
### 곁들여 잡은 것 — 고친 것이 없는데도 묻던 문제
만들어 놓고 화면에서 확인하다 걸렸다. 파일을 새로 열어도 `HasChanges` 가 꺼지지 않아
**앞 파일의 상태가 그대로 끌려왔다.** `LoadPlainFilesAsync` 에 그 줄이 빠져 있었다.
지금까지는 저장 단추가 켜진 채 남는 정도라 눈에 안 띄었는데, A-1 이 그 값을 읽으면서 드러났다.
### 화면에서 확인한 것
| 한 일 | 결과 |
|---|---|
| 고친 뒤 다른 파일 열기 | `Other.cpp 에 저장하지 않은 편집이 있습니다 / 다른 파일을 엽니다: WeldController.cpp` |
| [취소] | 편집기 내용 그대로, 파일도 그대로, 열린 파일도 그대로 |
| [저장하고 계속] | `Other.cpp` 에 고친 내용이 쓰이고 `WeldController.cpp` 가 열렸다 |
| [저장하지 않고 계속] | 파일은 손대지 않은 채 다음 파일이 열렸다 |
| 고친 것 없이 두 번 연속 열기 | 창이 뜨지 않았다 |
검사 514건 통과 · 빌드 경고 0 · 오류 0.
---
## A-2. 내보낸 보고서에 만든 사람이 없던 것 — **완료** (2026-09-10)
**왜** - `결과 내보내기` 로 뽑은 글에 `만든 때` 는 있는데 **누가 뽑았는지가 없었다.**
남에게 보내라고 만든 기능인데 작성자가 없으면 받는 쪽이 되묻게 된다.
**이름 판단을 한 함수로 모았다.** `MergeNote.AuthorOf` 를 새로 빼고 `MergeNote.Build` 도 그것을 쓴다.
설정에 적힌 이름이 없으면 윈도우 계정으로 물러선다. **같은 사람이 같은 화면에서 한 일인데
주석과 보고서에 다른 이름이 남으면 그 자체가 버그다** - 판단이 두 군데로 갈리면 언젠가 어긋난다.
**이름이 비어 있으면 줄 자체를 넣지 않는다.** `만든 사람: ` 만 덩그러니 있는 것보다 없는 편이 낫다.
파일 비교 · 폴더 비교 두 보고서에 모두 들어간다. 자리는 `만든 때` 바로 아래다.
**확인한 것**
실제로 뽑아 본 파일 머리글이다.
```
MergeHub 파일 비교 결과
만든 때: 2026-09-10 20:33:17
만든 사람: 김승준
왼쪽 : ...\left\Other.cpp
오른쪽: ...\right\Other.cpp
```
화면의 머지 주석 미리보기가 `// [김승준] 2026-09-10` 이었으니 **두 이름이 같다.**
검사 8건 추가(`ReportTest` 23 → 31). 전체 **522건 통과** · 빌드 경고 0 · 오류 0.
---
## B-1. 최근 비교 목록 — **완료** (2026-09-10)
**왜** - 같은 짝을 반복해서 여는데 그때마다 경로를 다시 골라야 했다. 매일 쓰는 사람에게 가장 잦은 마찰이다.
`Services/RecentPairs.cs` 신설. 화면 없이 검사할 수 있게 **기억하는 일만** 여기서 한다.
**파일 짝과 폴더 짝을 따로 둔다.** 한 목록에 섞으면 파일 비교 화면에서 폴더가 나온다.
상한은 15개 - 목록이 길면 고르는 것이 더 느려진다.
**저장 형식은 탭으로 나눈 글이다.** JSON 을 안 쓴 까닭 - 줄 몇 개가 전부라 라이브러리를 하나 더 매달 값이 없고,
글이면 사람이 열어 보고 손으로 고칠 수도 있다. 윈도우 경로에는 탭이 못 들어가서 나누는 데 문제가 없다.
자리는 `D:\UbisamConfig\Settings\MergeHub.Recent.txt` 다. **설정 화면에는 안 내놓는다** -
사람이 손으로 고칠 값이 아니고 설정 목록만 어지럽힌다.
**켤 때 한 번 읽고 바뀔 때마다 바로 쓴다.** 프로그램이 갑자기 꺼져도 목록이 남게 하려는 것이다.
**없는 경로를 자동으로 지우지 않는다.** 지시서가 그렇게 못박았다 - 네트워크 드라이브나 USB 는
잠깐 안 보일 뿐인데 지워 버리면 다시 찾아 들어가야 한다. 흐리게 보여 주고 열려고 할 때 알린다.
**비교가 끝난 짝만 담는다.** 읽다 실패했거나 취소한 짝을 남기면 다음에 골랐을 때 또 실패한다.
**목록 이름은 양쪽 끝 이름만** 쓴다. 전체 경로는 길어서 목록에서 읽히지 않는다.
두 이름이 같으면 하나만 적고(`Other.cpp`), 다르면 둘 다 적는다(`기준.cs ↔ 내작업.cs`). 전체 경로는 툴팁에 있다.
여는 길은 다른 데와 같은 `RequestPlainCompare` 다. 그래서 **저장하지 않은 편집도 A-1 에서 함께 걸린다.**
**화면에서 확인한 것**
| 한 일 | 결과 |
|---|---|
| 파일 비교 · 폴더 비교를 한 번씩 | `MergeHub.Recent.txt` 에 `F` 줄과 `D` 줄이 하나씩 |
| 프로그램을 껐다 켜기 | 두 목록이 그대로 남았다 |
| 폴더 최근에서 고르기 | 두 칸이 채워지고 비교가 바로 돌았다 (`전체 2개 · 다름 2`) |
| 파일 최근에서 고르기 | `BASE · Weld.cpp` / `LOCAL · Weld.cpp` 로 열렸다 |
| 짝의 두 파일을 지운 뒤 | 목록에 그대로 남고 **흐리게** 보였다 |
| 그 죽은 짝을 고르기 | `두 파일 모두 찾을 수 없습니다` 토스트, 화면은 그대로 |
검사 33건 추가(`RecentTest`). 전체 **555건 통과** · 빌드 경고 0 · 오류 0.
---
## B-3. 폴더 비교에서 다음 다른 파일로 — **완료** (2026-09-10)
**왜** - 파일 하나를 보고 **폴더 탭으로 돌아갔다가** 다음 파일을 다시 열어야 했다.
다른 파일이 스무 개면 이 왕복을 스무 번 한다.
`GitWorkspace` 에 훑는 중인 목록(`Queue`)과 자리(`QueueIndex`)를 뒀다.
폴더 비교가 채우고 파일 비교가 딛는다 - 두 화면이 서로를 모르는 채로 같은 목록을 봐야 해서
이미 통로 노릇을 하는 이 객체에 뒀다.
**원본 결과가 아니라 지금 트리를 훑어 목록을 만든다.** 필터로 "코드가 다른 것만" 을 걸어 뒀으면
그 안에서만 움직여야 한다 - 목록에 없는 파일로 넘어가면 왜 거기로 갔는지 알 수 없다.
**찾은 줄 목록도 훑을 목록이 된다.** 같은 파일이 여러 줄 걸렸으면 한 번만 담는다 -
다음 파일을 눌렀는데 같은 파일이 다시 열리면 멈춘 것처럼 보인다.
**최근 목록으로 연 파일은 목록을 비운다.** 남아 있던 목록을 그대로 두면 다음 파일이 엉뚱한 데로 간다.
**먼저 묻고 그 다음에 자리를 옮긴다.** 확인 창에서 취소하면 자리도 그대로여야 하는데,
자리를 먼저 옮기면 취소했을 때 목록 위치만 앞서 나간다. 물은 뒤에는 두 번 묻지 않도록 막는다.
단축키는 `Ctrl+PageUp` · `Ctrl+PageDown` 이다. `Alt+PageDown`(북마크)은 그대로 두었고
검사로 빼앗지 않았음을 확인한다.
**화면에서 확인한 것** (파일 네 개 · 하나는 하위 폴더)
| 한 일 | 자리 | 열린 파일 | 이전/다음 |
|---|---|---|---|
| 검색 결과로 열기 | 1 / 4 | A.cpp | 끔 / 켬 |
| 다음 파일 ×3 | 2 → 3 → 4 | B.cpp → C.cpp → D.cpp | 마지막에서 다음이 꺼짐 |
| 이전 파일 ×2 | 3 → 2 | C.cpp → B.cpp | 둘 다 켬 |
| 고친 뒤 다음 파일 → 취소 | **2 / 4 그대로** | B.cpp 그대로 | 편집 내용도 그대로 |
| 다시 눌러 버리고 계속 | 3 / 4 | C.cpp | B.cpp 파일은 손대지 않음 |
검사 5건 추가(`ShortcutTest` 63 → 68). 전체 **560건 통과** · 빌드 경고 0 · 오류 0.
---
## C-1. 같은 경로가 세 군데 나오던 것 — **완료** (2026-09-10)
**무엇이 문제였나** - 같은 경로가 셋에 있었다.
① 위 툴바의 잘린 경로 ② 패널 머리글 `BASE · A.cpp` ③ 그 아래 `경로` 상자의 전체 경로.
①은 260px 안에서 잘려 **읽을 수 없었고**, ③은 패널마다 세로 40px 남짓을 먹었다.
**둘을 없앴다.** 전체 경로는 머리글 툴팁으로 옮겼다.
**복사하는 방법은 지켰다.** ③이 읽기전용 `TextBox` 였던 까닭이 드래그 복사다.
줄을 없애면서 복사까지 없어지면 안 되므로 **머리글 우클릭 → 전체 경로 복사** 로 옮겼다.
클립보드가 다른 프로그램에 잡혀 있으면 실패하므로 그때는 알리고 넘어간다.
**놓는 자리는 단추로 옮겼다.** ①이 3차 D-7 의 슬롯 드롭 자리였다. 없애면 그 기능이 사라지므로
`기준 파일...` · `내 파일...` 단추가 받게 했다. **잘린 글자보다 단추가 겨냥하기 쉽다.**
단추 툴팁에 전체 경로를 걸어 두어 지금 무엇이 물려 있는지도 그대로 보인다.
**행 잠금은 그대로다.** 양쪽 패널에서 같은 줄을 없앴고 머리 높이는 `SharedSizeGroup` 이 묶고 있다.
3-Way 에서는 ③이 이미 접혀 있었으므로 달라지는 것이 없다.
**화면에서 확인한 것**
| 확인 | 결과 |
|---|---|
| 코드가 시작하는 자리 | 머리글 바로 아래로 올라왔다 (2560×1600 에서 약 58px, 코드 두 줄) |
| 두 패널 01행 높이 | 같다 |
| 머리글 툴팁 | `BASE · A.cpp` → `…\left\A.cpp` / `LOCAL · A.cpp` → `…\right\A.cpp` |
| 단추 툴팁 | 각각 지금 물린 전체 경로 |
| 머리글 우클릭 → 전체 경로 복사 | 클립보드에 전체 경로가 들어왔다 |
| 탐색기에서 `기준 파일...` 단추에 놓기 | `BASE · C.cpp` 로 바뀌고 LOCAL 은 그대로 |
검사 560건 통과 · 빌드 경고 0 · 오류 0.
### C-1 다시 — 파일 이름만 남긴 것이 잘못이었다 (2026-09-10)
**사용자 지적** - "파일 비교는 서로 다른 경로에 있는 파일을 보는 것 아니냐.
경로가 같으면 같은 파일일 텐데 왜 경로를 지웠느냐."
맞는 지적이다. 처음 고칠 때 머리글을 `BASE · {파일 이름}` 으로 줄였는데,
**파일 비교에서 이름이 같은 경우가 오히려 흔하다**(같은 파일의 두 판을 견주는 것이 주된 쓰임이다).
그러면 양쪽 머리글이 `BASE · Weld.cpp` / `LOCAL · Weld.cpp` 로 똑같아져 어느 쪽이 어느 쪽인지 알 수 없다.
실제로 B-1 확인 때 그 화면이 나왔는데 그때는 못 알아봤다.
툴팁에 전체 경로가 있긴 하지만 양쪽에 하나씩 올려 보고 머릿속에서 견줘야 한다.
**전체 경로도 아니고 파일 이름도 아닌, 다른 부분만 적는다.**
`PathUtil.DistinctTail` 을 새로 두어 같은 앞부분만 떼고 나머지를 보여 준다.
```
D:\work\trunk\src\a.cpp → trunk\src\a.cpp
D:\work\branch\src\a.cpp → branch\src\a.cpp
```
파일 이름 칸은 언제나 남긴다. 드라이브가 다르거나 겹치는 앞부분이 없으면 전체 경로를 그대로 준다.
대소문자와 슬래시 방향은 같게 본다.
**확인** - 이름이 같은 두 파일을 열었더니 `BASE · left\A.cpp` / `LOCAL · right\A.cpp` 로 갈렸다.
검사 11건 추가(`PathTest` 43 → 54). 전체 **571건 통과**.
**남길 것** - 화면을 줄이는 일은 무엇이 남아야 하는지부터 정하고 줄여야 한다.
여기서는 "경로가 길다"만 보고 줄였는데, 정작 그 경로가 두 칸을 가르는 유일한 정보였다.
### C-1 또 한 번 — 경로를 읽으라고 적는다 (2026-09-10)
**사용자 지적** - "경로를 좀 잘 보이게 해볼까."
머리글의 위아래가 뒤집혀 있었다. **늘 똑같은 `BASE` 는 크고 파란데,
정작 이 칸이 무엇인지 말하는 경로는 회색 작은 글씨였다.** 정보를 담은 쪽이 뒤로 물러나 있었다.
세 가지를 바꿨다.
- 경로 줄에서 `BASE ·` `LOCAL ·` 접두사를 뺐다. **위 제목이 이미 그 말을 하고 있다.**
- 회색(Muted) 을 걷고 본문 색으로, 11 → 12px, 글꼴은 코드와 같은 Consolas 로.
경로는 기호가 많아 고정폭이 읽기 쉽다.
- **갈리는 첫 칸만 굵은 강조색**으로 칠한다(`left\` · `right\`). 나머지는 양쪽이 같은 글자라
읽을 값이 없다. 어디가 다른지 눈이 바로 간다.
```
BASE LOCAL
left\WeldController.cpp right\WeldController.cpp
^^^^ ^^^^^ (굵은 강조색)
```
Git 모드도 같이 정리됐다 - `BASE / 9c9e0bb · StartWeld.cs`, `LOCAL / master`.
**걸린 것 하나** - `SplitLead` 의 튜플 요소 이름을 `Rest` 로 뒀다가 CS8126 이 났다.
`Rest` 는 `ValueTuple` 이 제 필드 이름으로 쓰는 예약어다. `Tail` 로 바꿨다.
검사 571건 통과 · 빌드 경고 0 · 오류 0.
---
## C-2. 아래쪽 정보가 세 군데로 흩어져 있던 것 — **완료** (2026-09-10)
**무엇이 문제였나** - 같은 성격의 정보가 세 자리에 나뉘어 있었다.
```
BASE 바닥 Diff 요약 / 총 43 라인 · 차이 10 라인
MERGED 바닥① 1행 1열 | 차이 - / 5 | UTF-8 · CRLF · C#
MERGED 바닥② 편집 0건 · 되돌리기 0건 · 남은 차이 10 라인 | 코드 검사 이상 없음
```
같은 뜻의 숫자가 세 벌이었다 - `차이 10 라인` · `남은 차이 10 라인` · `차이 - / 5`.
어느 것을 봐야 할지 헷갈리고, 패널마다 54px 를 먹었다.
**패널 안이 아니라 창 아래 한 줄로 옮겼다.** 자리 규칙을 정했다.
| 자리 | 무엇 |
|---|---|
| 왼쪽 | 지금 어디에 있는지 — `1행 1열` · `차이 3 / 5` |
| 가운데 | 얼마나 남았는지 — `총 34줄 · 남은 차이 5줄` (고친 것이 있으면 편집·되돌리기가 붙는다) |
| 오른쪽 | 이 파일이 무엇인지 — `UTF-8 · CRLF · C#` · 비교 규칙 · 코드 검사 |
**곁들여 얻은 것** - 상태바가 패널 밖으로 나오면서 두 패널의 코드 영역 높이가 저절로 같아진다.
전에는 BASE 와 MERGED 의 바닥 높이를 손으로 맞춰 두어야 했다(`PaneFooterHeight` 상수가 그 흔적이다).
3-Way 에서도 네 칸이 같은 자리에 선다.
**숫자를 하나씩만 남겼다.** `총 N줄` 은 파일 크기, `남은 차이 N줄` 은 남은 일이다.
편집·되돌리기 건수는 **0건일 때는 아예 적지 않는다** - 늘 `0건 0건` 이 붙어 있으면 눈이 그냥 지나친다.
**화면에서 확인한 것**
| 확인 | 결과 |
|---|---|
| 2-Way | `1행 1열 │ 차이 -/2 │ 총 8줄 · 남은 차이 2줄 … UTF-8 · CRLF · C/C++ │ 코드 검사 이상 없음` |
| 3-Way | 같은 한 줄, `총 34줄 · 남은 차이 5줄` · `코드 검사 확인 필요 1건` |
| 두 패널 코드 높이 | 같다. 3-Way 네 칸도 같은 자리에 선다 |
| 같은 숫자 두 번 | 없다 |
검사 571건 통과 · 빌드 경고 0 · 오류 0.
---
## 곁들여. 엔터를 누르면 한참 뒤에 반응하던 것 — **완료** (2026-09-10)
**사용자 지적** - "줄바꿈 엔터를 누르면 딜레이가 있어, 꽤 길게 체감된다."
**원인** - 화면에 보이는 글자는 편집기가 아니라 아래 표시층이 그린다(편집기 글자는 투명이다).
글자를 칠 때는 `ShowTypedLineNow` 가 그 줄만 즉시 표시층에 옮겨 준다. 그런데 **줄 수가 달라지면**
(엔터 · 줄 합치기 · 여러 줄 붙여넣기) 아래 줄이 통째로 밀린 것이라 한 줄만 고쳐서는 맞지 않아
그 함수가 손을 뗀다. 그러면 남는 것은 **0.6초짜리 재정렬 타이머**뿐이고,
그 동안 화면에는 아무것도 나오지 않는다. 글자는 즉시 나오는데 엔터만 느렸던 까닭이다.
**고친 방법** - `ShowTypedLineNow` 가 맞췄는지 여부를 돌려주게 하고,
못 맞춘 경우에는 타이머를 기다리지 않고 **그 자리에서 재정렬**한다.
빠른 길이 통하는 보통 타이핑은 지금까지처럼 타이머로 미룬다.
**재 본 값** (SendKeys 왕복 시간. 이 도구 자체가 한 번에 31ms 쯤 쓴다)
| 상황 | 한 번당 |
|---|---|
| 200줄 파일 · 엔터 | 31 ms |
| 2000줄 파일 · 엔터 | 32 ms |
| 글자 치기(견줄 값) | 31 ms |
**엔터가 글자 한 자와 구별되지 않는 수준이 됐다.** 예전에는 0.6초 동안 아무것도 안 나왔다.
2000줄에서도 200줄과 같은 값이 나오는 것을 보면 재정렬 자체는 이 크기에서 문제가 아니다.
화면에서도 확인했다 - 엔터로 넣은 빈 줄이 양쪽 칸에 곧바로 나타나고 정렬이 어긋나지 않는다.
### 엔터 고침이 만든 되돌이 문제 — **고침** (2026-09-10)
**사용자 지적** - "엔터 줄바꿈하고 글자가 이상하게 찍히고 커서 포인트 줄도 안 맞아."
**내가 만든 문제였다.** 바로 위에서 "그 자리에서 재정렬" 하도록 고쳤는데, 그 자리가
편집기의 `TextChanged` 이벤트 안이었다. 재정렬은 편집기 글을 통째로 갈아 끼우고
캐럿 자리도 다시 잡는다(`OnEditorTextReplaced`). **자기 이벤트 안에서 자기 글을 바꾸는 셈**이라
캐럿이 엉키고 뒤따라오던 글자가 엉뚱한 자리에 찍혔다.
**고친 방법** - 부르는 자리를 한 차례 뒤로 물렸다. 타이머 간격을 상황에 따라 나눈다.
| 상황 | 간격 | 왜 |
|---|---|---|
| 보통 타이핑 | 600ms | 글자마다 다시 정렬하면 화면이 요동친다 |
| 줄 수가 바뀜 | **1ms** | 늦추려는 것이 아니라 이 입력이 끝난 다음 차례에 돌게 하려는 것 |
`0` 이 아니라 `1ms` 인 까닭이 그것이다. 값이 아니라 차례가 목적이다.
**다시 확인한 것**
| 확인 | 결과 |
|---|---|
| 2행 끝에서 엔터 → `added` 입력 | 3행에 정확히 `added`, 캐럿 `3행 6열` |
| 양쪽 칸 정렬 | BASE 01·02 · 빈 줄 · 03·04 / LOCAL 01·02·03·04·05 로 줄이 맞는다 |
| 엔터 20번 | 622ms (한 번당 31ms) — 글자 치기와 같은 값 유지 |
**남길 것** - 화면 반응을 앞당길 때 어느 이벤트 안에서 부르는지를 먼저 봐야 한다.
"바로 부른다"가 "지금 이 자리에서 부른다"가 되면 되돌이가 생긴다.
---
## C-3. Git 탭에서 지금 못 쓰는 것이 위쪽 4분의 1을 차지하던 것 — **완료** (2026-09-10)
**무엇이 문제였나** - `저장소 연결` 칸에 원격 URL · 이름 · 클론 · 원격 추가 · 원격 삭제 ·
Gitea 계정 · 토큰 · Fetch · Pull · Push 가 세 줄로 있었다. **원격 저장소가 없어 전부 비활성**인데
화면에서는 원격이 제일 넓었다. 이 도구는 로컬에서 쓰는 머지툴이 우선이고 푸시도 하지 않는다.
**지우지 않고 접었다.** 사내 Gitea 가 생기면 다시 필요하다.
제목 옆에 `▸ 원격 · 계정` 단추를 두고 두 줄을 함께 접는다. **단추 글자가 지금 상태를 그대로 말한다.**
**늘 보이는 것은 로컬 경로 한 줄이다** - 경로 · `폴더...` · `열기` · `새로고침`.
**새로고침을 옮겼다.** 원격 줄에 Fetch/Pull/Push 와 함께 있었는데, 이건 **로컬 작업**이다.
접으면 함께 사라지므로 로컬 경로 줄로 옮겼다.
**펼침 상태를 기억한다.** `MergeHubSettings.GitRemoteExpanded` 에 남기되
`[Browsable(false)]` 로 **설정 화면에는 내놓지 않는다** - 화면에서 접고 펴는 것이지
사람이 설정 목록에서 찾아 고칠 값이 아니다(B-1 의 최근 목록과 같은 판단).
플랫폼 설정 그리드가 `Browsable` 을 이미 보고 있어 별도 처리가 필요 없었다.
**원격을 이미 쓰고 있으면 펼친 채로 시작한다.** `RemoteUrl` 이 적혀 있으면 접지 않는다 -
쓰던 사람 화면에서 갑자기 사라지면 없어진 줄 안다.
**화면에서 확인한 것**
| 확인 | 결과 |
|---|---|
| 접었을 때 | `클론` · `원격 추가/삭제` · `Fetch/Pull/Push` 가 사라진다 |
| 서브탭 줄 위치 | 접으면 위로 올라온다 (2560×1600 에서 약 128px) |
| 펼치면 | 단추가 `▾` 로 바뀌고 예전과 똑같이 나온다 |
| 설정 파일 | `"GitRemoteExpanded": true` / `false` 로 따라 바뀐다 |
| 껐다 켜기 | 펼쳐 둔 채로 껐다 켜면 펼쳐진 채로 뜬다 |
검사 571건 통과 · 빌드 경고 0 · 오류 0.
### C-3 다시 — 접힌 것이 있다는 사실이 안 보였다 (2026-09-10)
**사용자 지적** - "어딜 접은 거야, 안 보이는데."
단추는 있었지만 **11px 회색 글씨에 `▸ 원격 · 계정` 이라고만** 적혀 있었다.
화살표 하나로는 "여기 접힌 것이 있다" 가 전해지지 않는다. 접는 기능은
접혀 있다는 사실 자체가 보여야 쓸모가 있다. 안 그러면 기능이 사라진 것과 같다.
두 가지를 고쳤다.
- **글자가 무슨 일이 나는지 말한다** - `▸ 원격 저장소 · 계정 펴기` / `▾ … 접기`.
무엇이 숨어 있고 누르면 어떻게 되는지가 한 줄에 다 있다.
- **누르는 것으로 읽히게** - 12px 굵게, 강조색, 손 모양 커서, 폭을 190 으로.
---
## C-4. 색 구분 범례를 읽는 것에서 쓰는 것으로 — **완료** (2026-09-10)
**무엇이 문제였나** - 범례 여덟 칸이 늘 한 줄을 차지하는데 읽기만 하는 설명이었다.
0건인 갈래도 있는 갈래와 똑같은 무게로 자리를 잡고 있었다.
**두 가지를 했다.**
**① 0건은 흐리게.** `ZeroToDimConverter` 로 투명도를 0.35 로 낮춘다.
숨기지 않는 까닭 - 항목이 나타났다 사라지면 자리가 매번 움직여 같은 것을 다시 찾아야 한다.
자리는 그대로 두고 무게만 뺀다. 있는 것과 없는 것이 한눈에 갈린다.
**② 개수를 누르면 그 갈래로 간다.** `추가 1` 을 누르면 추가된 줄로 가고, 다시 누르면 다음 추가 줄로 간다.
"추가된 줄만 훑고 싶다"는 일이 잦은데 차이 이동(`Alt+↑↓`)은 갈래를 가리지 않는다.
이제 색 이름을 외울 필요도 없다 - 색을 보고 그 자리를 누르면 된다.
지금 자리 다음부터 찾고 끝에 닿으면 처음으로 돌아온다. **차이 이동과 같은 방식이라 새로 외울 규칙이 없다.**
한 줄도 없으면 조용히 넘어가지 않고 `삭제 줄이 없습니다` 로 알린다 -
아무 일도 안 일어나면 기능이 없는 줄 안다.
**단추처럼 보이지 않게 했다.** 테두리와 배경을 지우고 손 모양 커서와 마우스 올렸을 때의 옅은 배경만 남겼다.
범례 줄이 단추 여덟 개로 보이면 시끄럽다.
**화면에서 확인한 것** (차이 2줄 · 추가 1줄인 파일)
| 누른 것 | 캐럿 |
|---|---|
| 시작 | 1행 |
| `추가 1` | **7행** (추가된 줄) |
| `BASE와 다름 2` | **12행** |
| 다시 | **5행** |
| 또 다시 | **12행** (한 바퀴 돌아옴) |
| `삭제 0` | 그대로 · "삭제 줄이 없습니다" |
0건 갈래(`머지 반영함` · `직접 수정` · `삭제` · `무시` · `자리 옮김`)가 눈에 띄게 흐리다.
검사 571건 통과 · 빌드 경고 0 · 오류 0.