배치와 설계
2. 배치 — 독립한 프로그램
저장소를 같이 쓸 뿐, tabbit과 아무것도 공유하지 않습니다. 코드도, 솔루션도, 테스트도,
패키지도 각각입니다.
tools/mabbit/
Mabbit.slnx 자체 솔루션. Tabbit.slnx에 등재하지 않습니다
src/ 프로그램. 패키지 1개(Sylvan.Data.Excel)
test/ 자체 테스트와 자체 워크북 픽스처
폴더 이름은 소문자입니다. 실행 파일 이름과 같은 표기이므로 어느 플랫폼의 셸에서도 대문자를
기억할 필요가 없습니다. 이름은 merge + tabbit이고, 계열이 유지되므로 완성된 브랜드
자산에서 아이콘과 로고를 파생만 하면 됩니다.
2.1 공유하지 않는 근거
처음에는 tabbit의 격자 리더를 참조하였습니다. 「같은 코드로 읽어야 변환과 답이 갈리지
않는다」는 것이 근거였는데, 그 근거가 성립하지 않습니다 — 병합은 이 프로그램이 한 실행에서
읽은 파일 3개를 서로 비교할 뿐이고, 그 결과를 변환이 읽은 것과 대조하는 자리가 없습니다.
§4.7의 검증조차 별도 프로세스가 파일을 직접 다시 읽습니다.
그리고 그 참조의 값이 실측으로 드러났습니다.
| 항목 | tabbit 참조 | 독립 |
|---|---|---|
| DLL | 29개 | 2개 |
| 배포 크기 | 39 MB | 369 KB |
| 시작 시간 | 69 ms | 32 ms |
병합 드라이버는 충돌한 파일마다 1회, 사람이 프롬프트에서 기다리는 동안 실행됩니다. 구글 API 클라이언트·데이터베이스 드라이버 4종·C# 컴파일러를 그 자리에 끌고 갈 이유가 없습니다 — 셀 격자를 읽는 데 하나도 쓰지 않습니다.
인자 파싱 라이 브러리도 쓰지 않습니다. 옵션이 고정된 몇 개이고 전부 경로이며, 한 번 설정 파일에 적히고 나면 다시 타이핑되지 않습니다.
2.2 지키는 것
- 크로스플랫폼을 유지합니다. 병합 드라이버는 CI에서 리눅스로 실행되어야 합니다.
- 엑셀 설치를 요구하지 않습니다. 참고 구현이 그것을 요구하여 CI에서 사용할 수 없게 된 자리입니다.
TreatWarningsAsErrors와 nullable을 켭니다. 새로 작성하는 코드이므로 지금은 비용이 0이고, 나중에 켜는 비용이 662개였던 전례가 있습니다.- CI가
dotnet test Tabbit.slnx로 도는 범위 밖입니다. 자체 솔루션이므로 게이트를 따로 걸어야 합니다 — §8.
3. 참고한 구현 — XlsxMerge
넥슨코리아가 공개한 XlsxMerge(MIT)를 참고 대상으로 확인하였습니다. 엑셀 문서를 행 단위로 비교하고 3-way 병합하는 도구이며, SCM 연동 가이드를 함께 제공합니다.
3.1 가져오는 것
| 무엇 | 근거 |
|---|---|
| 행 단위를 병합의 단위로 삼는 판단 | 표 형태의 문서에서 변경은 대부분 행 방향으로 발생합니다. 셀 단위 정합만으로는 행 삽입 하나가 그 아래 전부를 변경으로 보고합니다 |
| 값과 수식으로 범위를 한정하는 판단 | 차트·도형·매크로·주석을 병합 대상에서 제외하고 그 사실을 먼저 밝힙니다. 경계를 명시한 것이 그 도구가 신뢰를 얻는 방식입니다 |
| SCM 연동을 별도 문서로 제공하는 구성 | 병합 도구는 SCM에 등록되지 않으면 사용되지 않습니다. 등록 절차가 기능의 일부입니다 |
| 병합 결과를 커밋 전에 확인하도록 안내하는 것 | 자동 병합을 무조건 신뢰하게 만들지 않습니다 |
3.2 가져오지 않는 것
| 무엇 | 근거 |
|---|---|
diff.exe·diff3.exe 외부 프로세스 의존 | 행을 텍스트 줄로 직렬화해 범용 diff3에 넘기는 방식입니다. 정합이 위치 기반이 되므로 행 순서가 변경된 워크북에서 정확도가 낮아지고, 실행 파일 2개를 배포본에 동봉해야 합니다. 키로 정합하면 그 단계가 필요하지 않습니다 |
| 엑셀 설치와 Windows 요구 | §2.2 |
4. 설계
4.1 병합이 요구하는 범위 — 레이아웃까지
필요한 것은 레이아웃까지이고 쿠킹 전체가 아닙니다. 이 판단이 나머지 설계를 결정합니다.
워크북을 Model까지 쿠킹하여 ModelFingerprint·SnapshotDiff를 재사용하는 구성은 성립하지
않습니다 — enum 선언과 참조는 워크북을 가로질러 해석되므로, 병합 대상 파일 하나만 쿠킹하면
다른 워크북에 선언된 enum에서 실패합니다. 전체 소스를 쿠킹하면 성립하나, 병합 1회마다 워크북
전부를 읽는 비용이 됩니다.
병합에 필요한 것은 셋뿐입니다.
| 필요한 것 | 어디서 |
|---|---|
| 표 사각형 | src/Cooking/Layouts/ — 레이아웃 파서가 격자 위에서 산출합니다 |
| 키 컬럼과 컬럼 이름 | 같은 자리 |
| 셀의 값 | src/Importers/ — 격자 그대로입니다 |
값 비교에 정규화가 필요하지 않습니다. CanonicalValue가 존재하는 이유는 서로 다른 시점의
두 모델을 대조하기 위해서인데, 병합은 base·mine·theirs