Skip to content

레이어 이름 붙이기 · 테스트 이음매 · 일탈 기준 - #23

Merged
dongglehada merged 4 commits into
mainfrom
feat/layers-and-seams
Aug 23, 2026
Merged

dongglehada merged 4 commits into
mainfrom
feat/layers-and-seams

Conversation

@dongglehada

Copy link
Copy Markdown
Contributor

변경 사항

A(레이어에 이름 붙이기) + 테스트를 막던 싱글턴 교체 + 일탈 기준입니다.

1. 먼저 바로잡은 것 — 싱글턴을 고친다고 다 테스트가 되지는 않습니다

싱글턴 다섯 중 테스트가 실제로 막혀 있던 것은 Shared/에 있는 것들뿐입니다. 튜토리얼 쪽 둘(TutorialCoordinator, TutorialOverlayWindow)은 App/Common/에 있어서 싱글턴을 고쳐도 테스트 타깃이 못 봅니다 — 싱글턴이 아니라 폴더가 막고 있습니다. 그래서 손대지 않았습니다.

2. 이음매 둘 (5d3fe46)

싱글턴을 없앤 게 아니라 갈아끼울 자리를 만들었습니다. 앱은 그대로 shared를 쓰고 테스트만 자기 것을 넣습니다.

  • ThemeStore(defaults:notifyWidgets:) — private init()이 App Group을 직접 열어서 한 줄도 테스트할 수 없었습니다
  • SharedModelContainer.inMemory() — 모델 인스턴스가 필요한 로직을 테스트할 수 있게 합니다. CloudKit을 반드시 꺼야 합니다 (기본값이 .automatic이라 테스트 프로세스가 iCloud를 붙이려다 죽습니다 — 실제로 겪었습니다)

3. 그 이음매가 값을 하는지 증명 (ba0bf65) — 112 → 126건

  • ThemeStoreTests 4건
  • TodoItemRecurrenceTests 10건 — 모델 쪽 반복 판정. RecurrenceTests가 덮던 건 TodoSnapshot(값 사본)이고, 앱 화면·알림·위젯이 실제로 부르는 건 이쪽인데 한 줄도 안 덮여 있었습니다. 리팩터링 1단계 항목 2번이 이걸로 끝났습니다

4. 레이어 이름 + 기계 검사 (2981d85, a1b2c3d)

도메인(Shared) ← 어댑터(App/Common) ← 프레젠테이션(App/Features)

새 구조가 아니라 이미 그렇게 돌던 것에 이름을 준 것입니다. 이름의 값은 기계 검사입니다.

python3 tools/quality_baseline.py        # 레이어 의존 방향
python3 tools/quality_baseline.py --since main   # 관례에서 벗어난 것

지금 네 건이 걸립니다. 전부 진짜고 CONVENTIONS.md 0절에 푸는 방법과 함께 적었습니다.

위반 어떻게 풀 것인가
Settings → Calendar (CategoryEditorSheet) 시트를 Settings/로 옮긴다
Calendar → Settings (SettingsScreen) 화면 이동은 예외로 둔다
TodayTodo → Calendar (DayTimelineView) 둘 다 쓰는 것은 위로 올린다
Common → Features (CategoryClassifying) 프로토콜을 Shared/로 내린다

Shared/ → App/ 방향은 검사하지 않습니다 — 위젯과 테스트 빌드가 이미 막습니다.

5. 일탈 기준 — 확인 규칙의 세 번째 축

말씀하신 "유사도로 확인 규칙을 만들 수 있지 않나"를 이렇게 구현했습니다. --since 모드가 새로 들인 것만 찾습니다 — 새 싱글턴, 새 플랫폼 분기, 새 프로토콜, 토큰 우회, try!/fatalError, // TEMP: 잔존, 새 파일.

세 축을 한 줄로: 기계가 못 잡고 · 되돌리기 어렵고 · 관례 밖이면 🔴.

거꾸로 읽으면 기존 관례 안에서 쓴 코드는 안 봐도 됩니다. 그게 규범을 문서로 두는 값이기도 합니다 — 규칙이 없으면 무엇이 "관례"인지 정의가 안 되고, 그러면 전부 봐야 합니다.

확인 등급

이 PR에 --since main을 돌린 결과입니다.

새 파일: ThemeStoreTests.swift, TodoItemRecurrenceTests.swift
✅ 새로 들인 개념 없음 — 기존 관례 안에서 쓴 변경이다

🟡 훑어보세요

  • Mosco/Shared/ThemeStore.swift — 생성자가 하나 늘었습니다. 앱 경로는 shared로 그대로
  • Mosco/Shared/SharedModelContainer.swift — 테스트 전용 함수 추가. 앱 경로 안 지나갑니다

🟢 안 보셔도 됩니다

  • 테스트 둘, 문서 둘, 도구 하나

🔴 없음. 새 개념을 안 들였고, 앱이 실제로 지나가는 경로는 안 바뀌었습니다.

체크리스트

  • 빌드 통과 — App / MoscoWidget / Mac Catalyst
  • 관련 테스트 통과 — 126건 (112 → 126)
  • 화면 확인 필요 없음 — 앱 동작이 안 바뀝니다
  • 불필요한 로그/주석 제거 — // TEMP: 0건

확인한 것

  • 이음매가 실제로 테스트를 열어주는지를 테스트로 증명했습니다. 이음매만 만들고 테스트가 없으면 코드만 늘린 것입니다.
  • 두 검사 모두 주석을 걷고 봅니다. 처음엔 안 걷어서 주석 속 언급이 위반으로 잡혔습니다 — 이 저장소는 "왜 그랬는지"를 주석에 적는 습관이 있어서, 그대로 두면 검사를 아무도 안 믿게 됩니다.
  • 레이어 검사가 찾은 넷이 전수 조사 결과와 일치하는지 대조했습니다. 일치합니다.

쓰면서 걸린 것 둘 (다음 사람을 위해)

  • 컨테이너를 붙들지 않고 mainContext만 받아두면 프로세스가 죽습니다. 컨테이너가 해제되면서입니다. 테스트 주석에 적었습니다
  • 모델 쪽 반복 판정이 Calendar.current를 하드코딩해서 테스트가 기기 시간대를 탑니다. 지금은 테스트도 Calendar.current로 날짜를 만들어 맞췄지만, 이게 나중에 뽑아낼 이유이기도 합니다

위임했으면 쌌을 곳

없었습니다. 전부 파일 두세 개짜리 작업이었습니다.

관련 이슈

없음 (DECISIONS.md D2의 A안 · D5)

dongglehada and others added 4 commits August 22, 2026 23:42
싱글턴을 없앤 게 아니라 갈아끼울 자리를 만들었다. 앱은 그대로 shared를 쓰고
테스트만 자기 것을 넣는다.

ThemeStore — private init()이 App Group을 직접 열어서 한 줄도 테스트할 수
없었다. 저장소와 위젯 알림을 받도록 바꿨다.

SharedModelContainer.inMemory() — 모델 인스턴스가 필요한 로직을 테스트할 수
있게 한다. CloudKit을 반드시 꺼야 한다(기본값이 .automatic이라 그냥 두면
테스트 프로세스가 iCloud를 붙이려다 죽는다).

튜토리얼 쪽 싱글턴 둘은 손대지 않았다. 그것들은 App/Common/에 있어서 싱글턴을
고쳐도 테스트 타깃이 못 본다 — 싱글턴이 아니라 폴더가 막고 있다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ThemeStoreTests 4건 — 기본색, 되읽기, 되돌리기, 그리고 색이 바뀔 때만 위젯을
깨우는지. 마지막 것은 규범 문서에 적힌 실제 사고다.

TodoItemRecurrenceTests 10건 — 모델 쪽 반복 판정. RecurrenceTests가 덮던 것은
TodoSnapshot(값 사본)이고 앱 화면·알림·위젯이 실제로 부르는 것은 이쪽인데 한
줄도 안 덮여 있었다. 하루 완료가 다른 날로 번지지 않는지가 핵심이다.

쓰면서 걸린 것 둘을 주석에 남겼다 — 컨테이너를 붙들지 않고 mainContext만
받아두면 프로세스가 죽는다. 그리고 모델 쪽이 Calendar.current를 하드코딩해서
테스트가 기기 시간대를 탄다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
새 구조를 도입한 게 아니라 이미 그렇게 돌던 것에 이름을 줬다 —
도메인(Shared) ← 어댑터(App/Common) ← 프레젠테이션(App/Features).

이름의 값은 기계 검사다. quality_baseline.py가 화면끼리의 참조와 어댑터가
화면을 참조하는 것을 잡는다. Shared → App 방향은 위젯·테스트 빌드가 이미
막으므로 안 본다. 주석은 걷어내고 본다 — 이 저장소는 왜 그랬는지를 주석에
적는 습관이 있어서, 안 걷으면 위반 아닌 것이 잔뜩 잡히고 그러면 아무도
이 검사를 안 믿는다.

지금 네 건이 걸린다. 전부 진짜고 CONVENTIONS 0절에 푸는 방법과 함께 적었다.

그리고 --since 모드를 넣었다. "관례에서 벗어난 것"만 찾는다 — 새 싱글턴,
새 플랫폼 분기, 새 프로토콜, 토큰 우회, try!/fatalError, TEMP 잔존.
이것이 review-criteria의 세 번째 축이다: 기계가 못 잡고 · 되돌리기 어렵고 ·
관례 밖이면 사람이 본다. 거꾸로 말하면 관례 안에서 쓴 코드는 안 봐도 된다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
같은 실수를 두 번 했다. 레이어 검사에서 주석을 안 걷어 위반 아닌 것이
잡혔고, 일탈 검사에도 같은 구멍이 있었다 — 테스트 주석의
"UserDefaults.standard를 쓰면"이 전역 접근으로 잡혔다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dongglehada
dongglehada merged commit 52561e5 into main Aug 23, 2026
1 check passed
@dongglehada
dongglehada deleted the feat/layers-and-seams branch August 23, 2026 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant