Issue Title
AI가 짠 코드를 사람 눈이 아니라 자동 검사로 걸러내는 체계를 만든다. AI를 더 잘 시키는 것보다, AI가 틀렸을 때 CI가 바로 빨간불이 뜨게 하는 게 목표다.
📌 이 이슈는 실제로 작업하는 이슈가 아니라 전체 계획을 모아둔 목차 이슈다.
실제 작업은 아래 항목마다 이슈를 따로 만들어서 하고, 그 번호를 여기에 링크로 채운다.
Issue Content
왜 하는가
이 레포는 문서·스킬·리뷰 에이전트로 규칙을 잘 세워놨다. 그런데 그 규칙을 마지막에 확인하는 게 거의 다
"읽는 사람"이거나 "자기가 한 일을 자기가 보고하는 AI" 다. AI가 "빌드 됩니다 / 테스트 통과했습니다"라고 하면,
그게 진짜인지 확인해주는 장치가 없다.
이게 괜한 걱정이 아니라는 증거가 이미 레포에 있다 (docs/BUILD_AND_TEST.md 함정 10):
build_sim이 내가 지정한 스킴을 무시하고 엉뚱한 스킴을 빌드해놓고 "성공"이라고 끝낸다.
그래서 내가 바꾼 코드는 컴파일조차 안 됐는데 통과한 줄 알고 넘어간다(#181에서 실제로 겪음).
그래서 하려는 것: AI 말만 믿어야 하는 지점을 하나씩 자동 검사로 바꾼다.
그러면 덤으로 리뷰 에이전트(wss-pr-reviewer·wss-feature-reviewer)가 자동으로 걸리는 건 안 봐도 되니까,
사람 판단이 필요한 것만 보게 된다. 리뷰가 더 빠르고 정확해진다.
지금 상태
이미 잘 되어 있는 것
- Domain 테스트 문화가 자리잡음 — Swift Testing, 명세형 테스트 이름, Given-When-Then, Mock 호출 추적 (
docs/TESTING.md)
ModuleType.swift가 모듈 목록을 한곳에서 관리함
- XcodeBuildMCP가
.mcp.json으로 팀에 공유돼 있고, Demo 실행·snapshot_ui·tap 흐름이 함정까지 문서화돼 있음
NovelReviewViewModelTests가 좋은 예시임 — 비동기 순서 꼬임, 늦게 도착한 응답, 닫기 처리 같은
"AI가 놓치기 쉬운 상태 변화"를 실제로 테스트하고 있음
docs/TODO.md 4번에 앱 교체(cutover) 체크리스트가 이미 자세히 있음 (Bundle ID·서명 팀·Kakao 키해시 등)
비어 있는 것
| 항목 |
상태 |
| Swift 6 |
앱·모듈 어디에도 SWIFT_VERSION/SWIFT_STRICT_CONCURRENCY 설정이 하나도 없음. (Tuist/Package.swift가 swift-tools-version: 6.0이긴 한데, 그건 Tuist 설정용 패키지 얘기지 실제 앱 타깃과는 상관없음) |
| SwiftLint / SwiftFormat |
없음 |
| 아키텍처 검사기 |
없음. 의존성 규칙을 어겨도 자동으로 막아주는 게 하나도 없음 |
| CI |
.github/workflows/test.yml 하나뿐. PR에 /domain-test 댓글 달아야만 도는 수동 방식, Domain만 검사 |
| CI가 안 하는 것 |
PR 자동 실행 ❌ · 전체 빌드 ❌ · lint ❌ · Data/Feature/UI 테스트 ❌ |
| Feature 테스트 |
Tests 폴더 5개 중 4개가 빈 껍데기(3개는 15줄 placeholder, NovelDetail은 #expect(Bool(true)) 컴파일 확인용). 실물은 NovelReviewViewModelTests 하나 |
| Mock |
Domain에 Mock~Repository는 16개 있는데 Mock~UseCase는 0개 |
tuist build (전체 빌드) |
지금 항상 실패함 — Core/Logger/Demo/Demo.swift가 주석만 있는 빈 파일이라 LoggerDemo 앱에 @main이 없어서 빌드가 깨짐 |
진행 순서
막힌 것 먼저 뚫기 → PR 자동 CI → 아키텍처 검사기 → 개발 방식 바꾸기(계약 먼저) → SwiftLint → Swift 6 → QA 자동화 → V1 비교
축 A — 자동으로 막는 장치들
A0. 먼저 뚫어야 할 것 (이게 안 되면 아래가 시작이 안 됨)
A1. PR 올리면 자동으로 도는 CI — 빠른 검사 / 전체 검사 두 단계
A2. 아키텍처 검사기 — 구조 규칙을 어기면 CI에서 실패시키기
SwiftLint랑 따로 만든다. SwiftLint는 코딩 스타일 잡는 도구라서, 여기 규칙들(어떤 걸 import하면 안 된다 같은
구조 판단)을 SwiftLint의 정규식으로 억지로 만들면 엉뚱한 걸 잡거나 진짜 위반을 놓친다.
대신 Tools/ArchitectureCheck에 SwiftSyntax 기반 작은 실행 프로그램으로 만들면,
앱이나 테스트에는 아무것도 안 붙어서 "외부 라이브러리 안 쓴다" 원칙과도 안 부딪힌다.
(다른 방법으로 Harmonize라는 게 있는데, 이건 규칙을 테스트처럼 짜는 방식 — 대신 테스트에 외부 라이브러리가 붙음. 뒤에서 다시 논의)
import 방향 검사
ViewModel 규칙 검사 (지금은 Projects/Feature/CLAUDE.md에 글로만 적혀 있음)
동시성 예외 문법 통제
"통과 계층에 로직 금지" 검토 (계층 책임을 자동 검사로 — 다만 한계가 분명함)
모듈 설정 자체를 검사 (ModuleType.swift 기준으로 — 폴더 훑는 방식 ❌)
A3. SwiftLint / SwiftFormat — 스타일만
도입 판단: 이건 앱에 들어가는 라이브러리가 아니라 개발 도구라서 "외부 라이브러리 안 쓴다" 원칙과 무관하다고 봄.
.mise.toml로 버전을 고정해서 팀·CI가 같은 버전을 쓰게 함.
A4. Swift 6 — 경고부터 모으고 천천히
애플 공식 가이드도 모듈 하나씩 천천히 켜기를 전제로 함. 한 번에 전부 켜면 위험함.
축 B — 개발 방식을 "계약 먼저"로 바꾸기
B1. new-feature 스킬을 "계약 먼저"로 다시 짜기
지금 new-feature(148줄짜리 하나)는 View/ViewModel 뼈대를 먼저 만들고 나서 Figma·동작 확인으로 감.
이 순서를 바꿔서 뭘 만들지 먼저 정하고 코드는 나중에 쓰게 한다. 스킬도 단계별로 쪼개서 각 단계를 따로 실행할 수 있게 함.
0. V1 조사 (축 C — V1의 같은 화면이 어떻게 동작했는지 뽑고, 유지/개선/삭제 분류)
1. 화면 계약 작성 (화면 목적 · View가 넣는 입력 · 상태 · 액션 · 의존성 · 주요 시나리오 표)
2. 실패하는 테스트 먼저 작성 (상태 변화 · 비동기 순서 · 에러 처리 · 닫기/화면이동)
3. 구현 (테스트를 통과시키는 최소한만)
4. Demo 시나리오 (정상 / 빈 화면 / 에러 / 권한 / 페이지네이션)
5. XcodeBuildMCP로 QA (축 B3)
6. 리뷰어 통과
B2. Feature ViewModel TDD
이 레포에서 특히 잘 맞는 이유: Feature ViewModel이 이미 State / Action / handle(.액션) 구조라,
"액션 넣으면 상태가 이렇게 바뀐다"를 거의 함수처럼 테스트할 수 있음. 궁합이 이미 맞음.
B3. XcodeBuildMCP QA 자동화
핵심은 도구가 아니라 "시나리오 파일"이다. 지금 docs/BUILD_AND_TEST.md는 도구 쓰는 법은 잘 적혀 있는데,
QA가 뭘 기준으로 통과/실패를 판단할지가 없음.
B4. 리뷰어 결과 형식 통일
축 C — V1을 검증 재료로 쓰기
V2는 지금 운영 중인 V1(Team-WSS/WSS-iOS, UIKit·RxSwift)을 완전히 대체할 코드베이스임.
V1은 실제 운영으로 검증된 동작 기준이지 구조의 정답은 아님.
⚠️ 이 구분은 이미 실제로 겪었음 — Projects/Domain/NovelDomain/CLAUDE.md:41:
"옛날 앱은 연재상태를 여러 개 고를 수 있게 해놨는데, 서버 쿼리는 isCompleted: Bool? 하나뿐이라
둘 다 고르면 필터가 통째로 무시됐다. V1이 다중선택이라는 이유로 따라 하지 말 것."
| V1에서 가져올 것 |
V1에서 따라 하면 안 되는 것 |
| 실제 사용자 흐름 |
RxSwift 기반 상태 구조 |
| 서버 요청 형식·파라미터 |
비대해진 ViewModel |
| 기존에 저장하던 데이터(키·구조) |
양방향 의존성 |
| 로그인·푸시·딥링크 연속성 |
UI 구현 방식 |
| 놓치기 쉬운 예외 상황 |
버그였던 동작 |
| 운영하면서 이미 겪은 버그 |
서버 제약을 UI가 잘못 표현한 부분 |
V1 확보·참조 방식 (협업 제약) — V1은 레포 의존성이 아니라 추출 재료다. V1을 읽어 뽑은
결과물(동작 계약·parity 분류)은 V2 레포에 커밋하므로 협업자는 V1을 안 받아도 그 문서를 소비할 수
있다(V1 클론이 필요한 건 추출/갱신하는 사람뿐). V1 위치는 형제 폴더 클론 관례(기본 ../WSS-iOS)로
두고, 문서엔 **머신마다 다른 절대경로 대신 repo@commit + 레포 내부 경로**로 참조한다
(예: Team-WSS/WSS-iOS@<sha> WSSiOS/Source/Presentation/Library/…) — 내부 경로는 누구 클론이든 동일해 이식성이 있다.
C1. V1 동작 뽑아내기
C2. V1과 V2 결과 비교 테스트
C3. 앱 교체(cutover) 연속성 검증
⚠️ 체크리스트는 이미 docs/TODO.md 4번에 자세히 있음. 새로 만드는 게 아니라 자동 검증만 붙이면 됨.
이건 "기능 QA"가 아니라 "기존 사용자가 안 끊기게 하는 QA" 라서 따로 다룸.
아직 정하지 못한 것 (시작 전에 논의)
- ViewModel 내부 작업을 함수 대신 enum으로 바꾸기 (제안자 표현:
Input / Action 분리)
⚠️ 일단 보류를 권함. View 입력과 내부 작업은 이미 나뉘어 있음 — View가 부르는 건 Action enum,
내부 작업은 private func(load()·requestClose()·close()).
제안의 진짜 내용은 그 private 함수들을 enum 케이스로 바꾸자는 것임(TCA 리듀서 스타일).
그러면 상태 바꾸는 부분이 비동기 없는 순수 함수가 돼서 테스트하기 편해짐.
① 근데 케이스가 2배로 늘고, close()처럼 4줄에 모여 있던 게 enum + switch로 흩어짐.
② 제일 어려운 부분(비동기 순서·늦게 온 응답)은 이미 지금 구조로 NovelReviewViewModelTests가 잡고 있음.
바꿔서 뭘 더 잡을 수 있는지가 불분명함. 핵심은 "계약 먼저"(B1)이지 ViewModel 구조 쪼개기가 아님.
- 스냅샷 테스트 넣을지 —
swift-snapshot-testing은 앱엔 안 붙지만 테스트에는 외부 라이브러리가 붙음이라 애매함.
대안(스크린샷 + AI 비교)은 매번 결과가 달라서 회귀 테스트로는 약함
- 아키텍처 검사기를 Harmonize로 할지, 직접 만들지 — A2는 직접 만드는 걸로 적었는데, Harmonize처럼
"규칙을 테스트처럼 짜는" 방식이 더 편할 수도 있음. 외부 라이브러리 붙는 것과 저울질해서 결정
- 테스트 플랜(
.xctestplan) 넣을지 — 레이어별로 테스트 묶음(개발 중엔 단위 테스트만 / 제출 전엔 전부)을 만들면
CI랑 로컬이 명령어 하나로 통일되고 병렬 실행·순서 랜덤을 한곳에서 켤 수 있음. Tuist 스킴 생성이랑 잘 맞는지 확인 필요
- SwiftFormat 넣을지 — 포맷 자동화 vs diff 지저분해짐
이 목차 이슈가 끝나는 기준
Issue Title
AI가 짠 코드를 사람 눈이 아니라 자동 검사로 걸러내는 체계를 만든다. AI를 더 잘 시키는 것보다, AI가 틀렸을 때 CI가 바로 빨간불이 뜨게 하는 게 목표다.
Issue Content
왜 하는가
이 레포는 문서·스킬·리뷰 에이전트로 규칙을 잘 세워놨다. 그런데 그 규칙을 마지막에 확인하는 게 거의 다
"읽는 사람"이거나 "자기가 한 일을 자기가 보고하는 AI" 다. AI가 "빌드 됩니다 / 테스트 통과했습니다"라고 하면,
그게 진짜인지 확인해주는 장치가 없다.
이게 괜한 걱정이 아니라는 증거가 이미 레포에 있다 (
docs/BUILD_AND_TEST.md함정 10):그래서 하려는 것: AI 말만 믿어야 하는 지점을 하나씩 자동 검사로 바꾼다.
그러면 덤으로 리뷰 에이전트(
wss-pr-reviewer·wss-feature-reviewer)가 자동으로 걸리는 건 안 봐도 되니까,사람 판단이 필요한 것만 보게 된다. 리뷰가 더 빠르고 정확해진다.
지금 상태
이미 잘 되어 있는 것
docs/TESTING.md)ModuleType.swift가 모듈 목록을 한곳에서 관리함.mcp.json으로 팀에 공유돼 있고, Demo 실행·snapshot_ui·tap 흐름이 함정까지 문서화돼 있음NovelReviewViewModelTests가 좋은 예시임 — 비동기 순서 꼬임, 늦게 도착한 응답, 닫기 처리 같은"AI가 놓치기 쉬운 상태 변화"를 실제로 테스트하고 있음
docs/TODO.md4번에 앱 교체(cutover) 체크리스트가 이미 자세히 있음 (Bundle ID·서명 팀·Kakao 키해시 등)비어 있는 것
SWIFT_VERSION/SWIFT_STRICT_CONCURRENCY설정이 하나도 없음. (Tuist/Package.swift가swift-tools-version: 6.0이긴 한데, 그건 Tuist 설정용 패키지 얘기지 실제 앱 타깃과는 상관없음).github/workflows/test.yml하나뿐. PR에/domain-test댓글 달아야만 도는 수동 방식, Domain만 검사Tests폴더 5개 중 4개가 빈 껍데기(3개는 15줄 placeholder, NovelDetail은#expect(Bool(true))컴파일 확인용). 실물은NovelReviewViewModelTests하나Mock~Repository는 16개 있는데Mock~UseCase는 0개tuist build(전체 빌드)Core/Logger/Demo/Demo.swift가 주석만 있는 빈 파일이라LoggerDemo앱에@main이 없어서 빌드가 깨짐진행 순서
축 A — 자동으로 막는 장치들
A0. 먼저 뚫어야 할 것 (이게 안 되면 아래가 시작이 안 됨)
Core/Logger의 Demo 빈 파일 채우기 —Demo/Demo.swift가 주석만 있어서@main이 없고 빌드가 깨진다.그래서
tuist build(전체 빌드)가 여기서 멈추고, 그 뒤 모듈들은 검사도 못 한 채 넘어간다(BUILD_AND_TEST.md함정 11).→ 전체 빌드 CI(A1)를 하려면 이거부터 고쳐야 함.
testDependencies넣기Tuist/ProjectDescriptionHelpers/Project+Templates.swift에서testDependencies: []로 박혀 있어서,Feature 테스트가 Domain의
~DomainTesting(Mock)을 못 가져다 쓴다.그래서 지금
NovelReviewViewModelTests가SuspendedLoadNovelReviewDraftUseCase같은 가짜를 테스트 파일 안에서 직접 만들어 쓰고 있다.(
createDataModule·createCoreModule·createUIModule도 똑같이 막혀 있음)Mock~UseCase만들기 — 지금은Mock~Repository만 있음.Feature의 ViewModel은 UseCase를 주입받는데, 그걸 흉내 낼 Mock이 없다. Feature TDD(B2)를 하려면 필요함.
Mock 만드는 방식은
docs/TESTING.md에 있는 그대로.env.baseSetting이 비어 있어서 전 모듈이 Xcode 기본값을 씀.Swift 6를 모듈 하나씩 천천히 켜려면(A4) 모듈별로 설정을 넣을 자리가 있어야 하는데 지금은 없다.
A1. PR 올리면 자동으로 도는 CI — 빠른 검사 / 전체 검사 두 단계
→ 아키텍처 검사 → lint → 이번에 바뀐 모듈만 빌드·테스트
fail-fast: false)— "상관없는 모듈 하나 때문에 전체가 멈추는" 함정을 이미 겪었다(a80bdb2). 어느 모듈이 깨졌는지 한 번에 다 보여야 함.
Domain 바뀜 → 그 Domain 테스트 / Data 매퍼 바뀜 → Data 테스트 / Feature ViewModel 바뀜 → ViewModel 테스트 /
UI 바뀜 → Demo QA /
Project.swift·Plugins/바뀜 → 아키텍처 검사xcodebuild -run-tests-until-failure -test-iterations N으로같은 테스트를 여러 번 돌려본다. 비동기·페이지네이션·낙관적 업데이트처럼 순서 꼬임이 있을 수 있는 테스트에 유용함.
/domain-test댓글 방식을 계속 둘지 없앨지 결정 (자동 CI랑 겹침)A2. 아키텍처 검사기 — 구조 규칙을 어기면 CI에서 실패시키기
import 방향 검사
Sources에서import ~Data금지 ← Tuist 설정으로는 못 막는 위반 (Demo는 Data를 써도 되니까 경로로 구분해야 함)Sources에서import SwiftUI·Combine·~Data·~Feature금지ViewModel 규칙 검사 (지금은
Projects/Feature/CLAUDE.md에 글로만 적혀 있음)ObservableObject·@Published·@StateObject쓰면 실패@MainActor @Observable final classprivate(set) var state하나로만 — 밖에서 못 바꾸게handle(_:)하나로만동시성 예외 문법 통제
@unchecked Sendable·@preconcurrency·Task.detached·nonisolated는 허용 목록에 없으면 실패(Swift 6 작업할 때 "일단 이거 붙여서 경고 끄자"가 퍼지는 걸 막으려는 것)
"통과 계층에 로직 금지" 검토 (계층 책임을 자동 검사로 — 다만 한계가 분명함)
if/switch/?:)가 있으면 실패하는 규칙 검토- 실제 표본:
DefaultFeedService.getMyFeeds가visibilityType == "PUBLIC" ? true : nil로 요청 파라미터 매핑을Service에서 하고 있다(원래 Repository/
FeedMapper소관). enum을 문자열로 낮춰 넘겨서 이중 변환이 되고,switch누락 검사도 컴파일러가 못 한다. Service 단위 테스트가 없어 어느 테스트도 이 변환을 안 지나감 →docs/TODO.md8번.-
(Service가
String대신 완성된 Query를 받으면 변환할 거리가 없어짐 = leak이 문법적으로 불가능). "분기 금지"는그 아래 깔아두는 싼 그물이고, 남는 판단은 리뷰어(B4) 몫이다.
모듈 설정 자체를 검사 (
ModuleType.swift기준으로 — 폴더 훑는 방식 ❌)ModuleType.swift에 등록된 모듈은 실제 폴더가 있어야 함등록 안 된 정식 모듈 폴더가 있으면 실패
← 지금
test.yml은 폴더를 훑는 방식이라, 쓰다 남은 빈 폴더(Projects/Domain/Novel·Comment·Feed같은접미사 없는 잔재)까지 테스트 대상에 넣어버린다.
docs/TESTING.md에 경고만 있고 막는 건 없음.Domain 모듈은
.sources+.testing+.tests타깃이 다 있어야 함Domain 모듈은
Tests폴더에@Test가 최소 1개는 있어야 함Feature 모듈은
.demo가 있어야 함ViewModel이 있는 Feature인데 테스트가 빈 껍데기뿐이면 실패 (새 모듈부터 적용, 기존은 봐줌)
Project.swift의 의존성이 레이어 방향을 어기면 실패외부 라이브러리를 추가할 때 허용 목록을 안 고치면 실패 (CLAUDE.md 5번 "외부 라이브러리 안 쓴다" 원칙을 자동 검사로)
의존성 그래프를 파일로 저장해두고 CI에서 변화를 비교 (지금
graph.dot/graph.png는 손으로 뽑은 거라 아무도 안 봄)검사기 자체도 테스트를 붙인다 — 규칙이 코드가 되면 그 규칙도 테스트할 수 있음
A3. SwiftLint / SwiftFormat — 스타일만
.swiftlint.yml+ CI 잡!) · 강제 try · 함수 복잡도 · 파일 길이 · 안 쓰는 importColor(red:) ·.font(.system·print(·import XCTest(구조 판단이 필요한 규칙은 전부 A2로)
A4. Swift 6 — 경고부터 모으고 천천히
SWIFT_STRICT_CONCURRENCY = complete잡 추가 — Swift 5 상태 그대로, 경고만 모음@MainActor정리Sendable중 뭘 쓸지 정하기SWIFT_VERSION = 6으로 올리기SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor를 UI/Feature에 적용할지 검토 —ViewModel에 이미
@MainActor가 붙어 있어서 이걸 켜면 마이그레이션이 훨씬 수월함 (Xcode/Swift 버전별 설정 이름은 실제로 확인 필요)Sendable로 맞추기 — 강제되면 오히려 좋음. 컴파일러가 값 전달을 검증해줌축 B — 개발 방식을 "계약 먼저"로 바꾸기
B1.
new-feature스킬을 "계약 먼저"로 다시 짜기지금
new-feature(148줄짜리 하나)는 View/ViewModel 뼈대를 먼저 만들고 나서 Figma·동작 확인으로 감.이 순서를 바꿔서 뭘 만들지 먼저 정하고 코드는 나중에 쓰게 한다. 스킬도 단계별로 쪼개서 각 단계를 따로 실행할 수 있게 함.
CLAUDE.md의## 화면 동작 계약이랑 겹치지 않게 관계 정리B2. Feature ViewModel TDD
docs/TESTING.md에 "Feature ViewModel 테스트" 부분 추가 (지금은 Domain만 다룸)NovelReviewViewModelTests를 기준 예시로 삼기 — 특히 비동기 순서 꼬임 테스트가 좋은 본보기임AI가 "테스트 통과"라는 말을 못 믿겠으면, 실패하는 것도 실제로 봐야 함.
처음부터 통과하는 테스트는 검증이 아니라 그냥 자기 말 반복임. 실패 로그를 증거로 남긴다.
LibraryFeatureTests·HomeFeatureTests·NotificationFeatureTests·NovelDetailFeatureTests커버리지 목표는 의미 없는 테스트만 잔뜩 만드는 부작용이 있음.
UseCase / Entity / ViewModel마다 핵심 동작 테스트가 있는지가 더 유용함
빈 값/필드 누락/서버 에러 케이스
@Observable상태 변화를 중간중간 관찰하는 헬퍼(withObservationTracking기반)가 필요한지 판단 — 지금은 최종 상태만 확인 가능B3. XcodeBuildMCP QA 자동화
Projects/Feature/<Module>/QA/<Screen>QA.md- 실행 스킴 · Demo 앱 ID · Mock 시나리오 목록
- 처음 화면에 뭐가 보여야 하는지 · 사용자 동작 · 그 다음 기대 상태
- 스크린샷 확인 포인트 · 접근성 식별자 목록
-
BUILD_AND_TEST.md함정 9(가장자리 스와이프 뒤로가기는자동화로 확인 불가)처럼, 도구 한계인 걸 코드 문제로 착각하지 않게 미리 적어둠
accessibilityIdentifier) 규칙 만들기 — DesignSystem / WSSComponent 컴포넌트에.BUILD_AND_TEST.md함정 4: 별점 같은 커스텀 그림은 자동 탭 대상으로 안 잡혀서 좌표로 탭해야 하는데, 그러면 쉽게 깨짐.(겸사겸사 VoiceOver도 좋아짐)
session_set_defaults(스킴·시뮬레이터·앱ID 고정) →build_sim→ 설치/실행 →snapshot_ui로 요소 확인 → tap/type → 주요 상태 스크린샷 →실패하면
.xcresult+ 스크린샷 + 콘솔 로그 + 재현 순서 리포트-
session_set_defaults가 꼭 필요한 이유:BUILD_AND_TEST.md함정 10 — 인자로 준 스킴이 무시되고엉뚱한 스킴이 "성공"으로 끝남. 이걸 모르면 QA 결과 전체가 거짓말이 됨
snapshot_ui(텍스트 트리)를 1순위로,screenshot은 보조로 —텍스트라서 비교(diff)가 되니까 회귀 테스트가 됨. 스크린샷 눈으로 비교하는 건 매번 결과가 달라서 주된 방법이 못 됨
B4. 리뷰어 결과 형식 통일
wss-pr-reviewer·wss-feature-reviewer결과 형식 정하기 —Blocker / Warning / Nit ·
파일:줄· 재현 조건 · 고칠 방향 · 확인 명령어→ 리뷰어끼리 결과를 비교할 수 있게 되고,
make-PR의 "Blocker·Warning 0까지 고치기"를 자동으로 판정 가능docs/DEFINITION_OF_DONE.md중에서 자동으로 확인 가능한 항목을make-PR사전 점검 스크립트로 빼기축 C — V1을 검증 재료로 쓰기
C1. V1 동작 뽑아내기
추천 대상 LibraryFeature — V1 소스 존재(
WSSiOS/Source/Presentation/Library/),의도적 변경점이 풍부해 오탐 방지까지 확인 가능, 필터→서버쿼리·커서 페이지네이션이라 파라미터 비교가 구체적.
문서에 없던 parity 발견이 1건이라도 나오면 → 에이전트화 착수, 전부 이미 문서화된 것뿐이면 접근법 재조정.
진입 조건 · 사용자 동작 · API 호출 순서 · 요청 파라미터 · 성공/실패/빈 화면 처리 ·
로딩 처리 · 토스트/알럿 문구 · Keychain/UserDefaults 사용 · 딥링크/푸시/로그인 특수 처리 · 기존 버그
## 화면 동작 계약절(이미 규약으로 존재 →Projects/Feature/CLAUDE.md:22) — 새 문서 포맷을 만들지 않는다.new-feature의 0단계로 연결 (B1)C2. V1과 V2 결과 비교 테스트
화면 픽셀 비교보다 안정적임
API 요청 파라미터 생성 · 에러 변환 · UserDefaults/Keychain 저장 키 · 푸시 토큰/기기 ID 처리
페이지네이션 방식 · 에러 응답 처리
→ V2의 Data/Repository 테스트에 바로 넣음
docs/PARITY.md로 정착 예정):Keep(유지) = V1과 같아야 함 → 다르면 회귀 후보 / Improve(개선) = V1 버그·모호성을 의도적으로 고침(근거 문서화) /
Break(회귀) = V1 정상 동작을 실수로 깨뜨림 / Unknown(보류) = 사용자 확인.
🔴 Blocker 2개: ① Keep인데 V2가 다름(누락된 유지), ② V1 버그를 그대로 답습(Improve했어야 하는데 안 함).
Projects/Data/NovelData/CLAUDE.md:19의"옛날 경로(
/users/{id}/novels)는 첫 페이지 고정·필터 무시라서 이제 아무 데서도 안 씀"NovelDomain/CLAUDE.md:41(연재상태 다중→단일) ·NovelData/CLAUDE.md:19+LibraryFeature/CLAUDE.md:32(구 서재 경로 폐기) ·RecommendationDomain/CLAUDE.md:18(관심글 제거) ·NovelDetailFeature/CLAUDE.md:72,81(장르 뱃지·오버레이 no-op) ·Feature/CLAUDE.md(탭 복귀 재조회=V1 viewWillAppear 계승) ·WSSComponent/CLAUDE.md:56,65(엣지 스와이프백) ·README.md:24-42/ARCHITECTURE.md:3(As-Is/To-Be 대비표)C3. 앱 교체(cutover) 연속성 검증
운영 Bundle ID · 애플 개발자 팀 ID(
DEVELOPMENT_TEAM은 레포 어디에도 설정된 적이 없음) ·Kakao 앱 키/Bundle ID/키해시 · Keychain access group · UserDefaults 키 ·
푸시 토큰 등록 방식 · App Store Connect 같은 앱 레코드
아직 정하지 못한 것 (시작 전에 논의)
Input/Action분리)Actionenum,내부 작업은
private func(load()·requestClose()·close()).제안의 진짜 내용은 그 private 함수들을 enum 케이스로 바꾸자는 것임(TCA 리듀서 스타일).
그러면 상태 바꾸는 부분이 비동기 없는 순수 함수가 돼서 테스트하기 편해짐.
① 근데 케이스가 2배로 늘고,
close()처럼 4줄에 모여 있던 게 enum + switch로 흩어짐.② 제일 어려운 부분(비동기 순서·늦게 온 응답)은 이미 지금 구조로
NovelReviewViewModelTests가 잡고 있음.바꿔서 뭘 더 잡을 수 있는지가 불분명함. 핵심은 "계약 먼저"(B1)이지 ViewModel 구조 쪼개기가 아님.
swift-snapshot-testing은 앱엔 안 붙지만 테스트에는 외부 라이브러리가 붙음이라 애매함.대안(스크린샷 + AI 비교)은 매번 결과가 달라서 회귀 테스트로는 약함
"규칙을 테스트처럼 짜는" 방식이 더 편할 수도 있음. 외부 라이브러리 붙는 것과 저울질해서 결정
.xctestplan) 넣을지 — 레이어별로 테스트 묶음(개발 중엔 단위 테스트만 / 제출 전엔 전부)을 만들면CI랑 로컬이 명령어 하나로 통일되고 병렬 실행·순서 랜덤을 한곳에서 켤 수 있음. Tuist 스킴 생성이랑 잘 맞는지 확인 필요
이 목차 이슈가 끝나는 기준
docs/TODO.md로 옮겨짐