blake@xcode:~/Projects$ cat ios-agent-development.md

AI 에이전트로 iOS 앱 만들기: 실무자 가이드

# AI 에이전트로 iOS 앱 만들기

words: 8968 read_time: 45m updated: 2026-04-14 23:56
$ less ios-agent-development.md

요약: 현재 세 가지 에이전트 런타임이 iOS용 코드를 작성할 수 있습니다: Claude Code CLI(+ MCP), Codex CLI(+ MCP), 그리고 Xcode 26.3의 네이티브 Intelligence 에이전트입니다. 두 개의 MCP 서버(59개 도구를 제공하는 XcodeBuildMCP와 20개 도구를 제공하는 Apple의 xcrun mcpbridge)가 에이전트에게 빌드, 테스트, 시뮬레이터, 디버깅에 대한 구조화된 접근을 제공합니다. 이 가이드는 실제 CLAUDE.md 패턴, 훅 설정, 그리고 무엇이 작동하고 무엇이 실패하는지에 대한 솔직한 평가를 다루며, 총 293개의 Swift 파일로 구성된 8개의 프로덕션 iOS 앱에서 얻은 경험을 바탕으로 합니다. 에이전트는 SwiftUI 뷰, SwiftData 모델, 리팩토링, 빌드 오류 진단에 뛰어납니다. 반면 .pbxproj 수정, 코드 서명, 시각적 디버깅에서는 실패합니다. “에이전트가 Swift를 작성한다”와 “에이전트가 iOS 앱을 출시한다” 사이의 격차는 프롬프팅이 아니라 설정으로 해결됩니다.

저는 AI 코딩 에이전트로 8개의 iOS 앱을 개발하고 있습니다. 프로토타입이 아닙니다 — App Store에 등록된 앱으로, HealthKit 통합, Metal 셰이더, SpriteKit 물리 엔진, iCloud 동기화, Live Activities, Game Center 리더보드, 그리고 iOS, watchOS, tvOS에 걸친 멀티 플랫폼 타겟을 포함합니다. 이 앱들의 모든 Swift 코드는 에이전트가 작성하고 제가 리뷰했거나, 제가 작성하고 에이전트가 리팩토링한 것입니다. 제 추정으로는 에이전트가 약 85%, 제가 15% 정도의 비율입니다.

이 가이드는 제가 처음 시작할 때 있었으면 좋았을 레퍼런스입니다. 전체 스택을 다룹니다: 어떤 에이전트 런타임을 사용할지, 구조화된 빌드 접근을 위해 MCP 서버를 어떻게 설정할지, CLAUDE.md에 무엇을 작성할지, 에이전트가 Xcode 프로젝트를 망가뜨리지 않도록 어떤 훅을 설정할지, 그리고 가장 중요하게는 — 에이전트가 실패하는 지점과 직접 개입해야 하는 상황을 다룹니다.

핵심 요약

AI 에이전트가 처음인 iOS 개발자:

  • Claude Code CLI + XcodeBuildMCP로 시작하세요. 가장 성숙한 런타임이며 MCP 도구 지원이 가장 깊습니다. 두 개의 명령어를 설치하고 프로젝트에 CLAUDE.md를 추가하면, 에이전트가 에러 메시지를 복사할 필요 없이 빌드, 테스트, 디버깅을 수행할 수 있습니다.
  • 에이전트가 .pbxproj를 수정하게 하지 마세요. 이것이 가장 중요한 규칙입니다. .pbxproj.xcodeproj/에 대한 쓰기를 차단하는 PreToolUse 훅 하나가 수 시간의 복구 작업을 절약해 줍니다.
  • CLAUDE.md는 에이전트의 온보딩 문서입니다. CLAUDE.md에 투자하는 1시간이 에이전트 실수를 수정하는 10시간을 절약합니다.

기존 에이전트 사용자가 iOS를 워크플로에 추가하는 경우:

  • MCP가 iOS 빌드 루프를 혁신합니다. MCP 이전에는 에이전트가 Swift를 작성할 수 있었지만 컴파일 여부를 확인할 수 없었습니다. XcodeBuildMCP를 사용하면 에이전트가 코드를 작성하고, 빌드하고, 구조화된 에러를 읽고, 수정하고, 테스트를 실행하는 것까지 자율적으로 수행합니다.
  • 세 가지 런타임은 각각 다른 용도에 적합합니다. Claude Code CLI는 깊이 있는 에이전트 세션에, Codex CLI는 헤드리스 배치 작업에, Xcode 26.3 네이티브 에이전트는 IDE를 떠나지 않고 빠르게 인라인 수정할 때 사용합니다.
  • 훅 인프라는 그대로 활용할 수 있습니다. 기존 PostToolUse 포매터, PreToolUse 차단기, 테스트 러너 훅은 경로만 약간 조정하면 iOS 프로젝트에서도 동일하게 작동합니다.

AI 지원 iOS 개발을 평가하는 팀 리드:

  • 에이전트의 효과는 프로젝트 규모가 아니라 프로젝트 문서화 수준에 비례합니다. 상세한 CLAUDE.md가 있는 63개 파일 앱이 문서가 없는 14개 파일 앱보다 더 나은 에이전트 결과물을 생성합니다.
  • .pbxproj 경계는 타협할 수 없습니다. 에이전트는 Xcode 프로젝트 파일을 안정적으로 편집할 수 없습니다. 워크플로에서 Xcode 타겟에 파일을 수동으로 추가하는 과정을 반드시 포함해야 합니다.
  • 솔직한 ROI: 에이전트는 구현 작업의 70-80%를 처리합니다. 나머지 20-30% — 시각적 완성도, 서명, 성능 튜닝, App Store 제출 — 에는 사람의 판단이 필요합니다.

경로 선택

필요한 내용 바로 가기
MCP를 처음 설정하는 경우 MCP 설정: 전체 구성 가이드 — 두 서버 설치, 검증, 에이전트 설정
iOS 프로젝트용 CLAUDE.md 작성 iOS 프로젝트를 위한 CLAUDE.md 패턴 — 8개 앱의 실제 예제
세 가지 에이전트 런타임 비교 iOS를 위한 세 가지 에이전트 런타임 — Claude Code vs. Codex vs. Xcode 네이티브
에이전트가 할 수 있는 것과 없는 것 이해 에이전트가 잘하는 것에이전트가 못하는 것
iOS 개발용 훅 설정 iOS 개발용 훅 — 저장 시 포맷, .pbxproj 보호, 테스트 러너
심화 레퍼런스 (이 페이지) 계속 읽기 — 설정부터 고급 패턴까지 전부 다룹니다

이 가이드 사용법

이 가이드는 3,000줄 이상의 레퍼런스입니다. 자신의 경험 수준에 맞는 곳에서 시작하세요:

경험 수준 시작 지점 추가 탐색
iOS + 에이전트 모두 처음 사전 준비MCP 설정첫 에이전트 세션 CLAUDE.md 패턴, 잘 되는 것/안 되는 것
iOS 개발자, 에이전트는 처음 세 가지 런타임MCP 설정CLAUDE.md , 아키텍처 패턴
에이전트 사용자, iOS는 처음 아키텍처 패턴에이전트가 못하는 것CLAUDE.md 프레임워크별 컨텍스트, 고급 워크플로
둘 다 경험 있음 고급 워크플로멀티 플랫폼 패턴 런타임 비교 매트릭스, 포트폴리오

목차

  1. 포트폴리오: 8개 앱, 293개 파일
  2. 사전 준비
  3. iOS를 위한 세 가지 에이전트 런타임
  4. MCP 설정: 전체 구성 가이드
  5. iOS 프로젝트를 위한 CLAUDE.md 패턴
  6. 첫 에이전트 세션
  7. iOS에서 에이전트가 잘하는 것
  8. iOS에서 에이전트가 못하는 것
  9. iOS 개발용 훅
  10. 에이전트와 잘 맞는 아키텍처 패턴
  11. 프레임워크별 컨텍스트
  12. 멀티 플랫폼 패턴
  13. 고급 워크플로
  14. 실전 사례 연구
  15. 에이전트와 함께하는 프로젝트 생애주기
  16. 에이전트 정의 설정
  17. 에이전트 지원 iOS 테스트 패턴
  18. iOS 프로젝트의 컨텍스트 윈도우 관리
  19. 문제 해결
  20. iOS에서 흔한 에이전트 실수와 예방법
  21. 솔직한 평가
  22. FAQ
  23. 빠른 참조 카드
  24. 참고 자료

관련 리소스

주제 리소스
Xcode용 MCP 설정 (짧은 블로그 글) Two MCP Servers Made Claude Code an iOS Build System
Claude Code CLI 전체 레퍼런스 Claude Code CLI: The Complete Guide
Codex CLI 레퍼런스 Codex CLI: The Complete Guide
훅 시스템 심층 분석 Anatomy of a Claw: 84 Hooks as an Orchestration Layer
에이전트 아키텍처 패턴 Agent Architecture Guide

포트폴리오: 8개 앱, 293개 파일

설정에 들어가기 전에, 이 가이드의 기반이 된 프로젝트들을 소개합니다. 이들은 토이 프로젝트가 아닙니다 — 5개의 Apple 프레임워크, 3개의 플랫폼을 아우르며, 14개 파일의 운동 트래커부터 63개 파일의 멀티 플랫폼 명상 타이머까지 iOS 복잡성의 전 범위를 포괄합니다.

스택 파일 수 복잡도
Banana List SwiftUI + SwiftData + iCloud Drive 동기화 + MCP 서버(Claude Desktop용) 53 전체 CRUD, iCloud 동기화, 앱 데이터를 Claude Desktop에 노출하는 커스텀 MCP 서버
Ace Citizenship SwiftUI 학습 앱 + FastAPI 백엔드 26 클라이언트-서버, REST API 통합, 퀴즈 엔진
TappyColor SpriteKit 색상 매칭 게임 30 게임 루프, 물리 엔진, 터치 처리, 파티클 효과
Return 젠 명상 타이머 — iOS 26+, watchOS, tvOS 63 HealthKit, Live Activities, Watch 확장 런타임, TV 포커스 내비게이션, iCloud 세션 동기화
amp97 Metal 셰이더 + 오디오 시각화 41 커스텀 Metal 렌더 파이프라인, 오디오 분석, 실시간 GPU 컴퓨트
Reps SwiftUI + SwiftData 운동 트래킹 14 최소 기능 앱, 깔끔한 SwiftData 패턴
Water SwiftUI + SwiftData + Metal + HealthKit 수분 섭취 트래킹 34 Metal 유체 시뮬레이션, HealthKit 수분 섭취 기록, 위젯
Starfield Destroyer SpriteKit + Metal 우주 슈팅 게임 32 99개 레벨, 8대 우주선, Game Center 리더보드, Metal 후처리

파일 수가 중요한 이유: 에이전트의 효과는 프로젝트 크기가 아닌 프로젝트의 가독성과 비례합니다. Return(63개 파일)은 amp97(41개 파일)보다 더 나은 에이전트 출력을 생성하는데, Return에는 파일 주석, 아키텍처 다이어그램, 명시적 패턴이 포함된 상세한 CLAUDE.md가 있기 때문입니다. amp97의 Metal 셰이더는 문서화 수준과 관계없이 에이전트가 추론하기 본질적으로 더 어렵습니다.


사전 요구 사항

iOS 개발용 에이전트 런타임을 설정하기 전에 다음을 준비하세요:

필수: - macOS 15+(Sequoia) 또는 macOS Tahoe - Xcode 26.3+ 설치 및 설정 완료(라이선스 동의, 플랫폼 다운로드) - iOS Simulator 런타임 최소 1개 설치 - Anthropic API 계정(Claude Code용) 또는 OpenAI 계정(Codex용)

권장: - SwiftFormat 설치(brew install swiftformat) — 저장 시 자동 포맷 훅에 사용 - SwiftLint 설치(brew install swiftlint) — 선택 사항이지만 스타일 규칙 적용에 유용 - 터미널 사용 경험 — 세 가지 런타임 모두 커맨드라인에서 작동하거나 커맨드라인과 통합됨

Xcode 설치 확인:

# Check Xcode version
xcodebuild -version
# Expected: Xcode 26.3 or later

# Check available simulators
xcrun simctl list devices available
# Expected: at least one iPhone simulator

# Verify xcrun mcpbridge is available
xcrun mcpbridge --help
# Expected: usage information (not "command not found")

xcrun mcpbridge에서 “command not found”가 반환되면 Xcode 26.3 이상이 필요합니다. App Store 또는 developer.apple.com에서 Xcode를 설치하거나 업데이트하세요. 참고: xcode-select --install은 Command Line Tools만 설치하며, mcpbridge는 포함되지 않습니다 — 전체 Xcode.app이 필요합니다.


iOS를 위한 세 가지 에이전트 런타임

세 가지 서로 다른 런타임이 iOS 코드를 작성, 빌드, 테스트할 수 있습니다. 이들은 상호 대체가 불가능합니다 — 각각 다른 강점, 다른 MCP 통합 패턴, 다른 이상적인 사용 사례를 가지고 있습니다.

1. Claude Code CLI

개요: Anthropic의 터미널 기반 에이전틱 코딩 어시스턴트입니다. 코드베이스를 읽고, 명령을 실행하고, 파일을 수정하며, MCP를 통해 외부 도구와 연결됩니다.

MCP 통합: XcodeBuildMCP와 Apple의 Xcode MCP를 모두 완벽하게 지원합니다. 에이전트가 MCP 프로토콜을 통해 도구를 탐색하고 구조화된 매개변수로 호출합니다. 두 서버에 걸쳐 총 59 + 20개의 도구를 사용할 수 있습니다.

설정:

# Install Claude Code (if not already installed)
claude --version  # verify installation

# Add XcodeBuildMCP (59 tools — builds, tests, simulators, debugging)
claude mcp add XcodeBuildMCP \
  -s user \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- npx -y xcodebuildmcp@latest mcp

# Add Apple Xcode MCP (20 tools — file ops, diagnostics, Swift REPL, previews)
claude mcp add --transport stdio xcode \
  -s user -- xcrun mcpbridge

적합한 용도: 심층 구현 세션 — 새 기능 구축, 여러 파일에 걸친 리팩토링, 복잡한 이슈 디버깅, 빌드-테스트-수정 루프의 자율 실행. Claude Code의 1M 컨텍스트 윈도우(Opus 4.6)를 활용하면 대부분의 중소 규모 iOS 프로젝트를 작업 메모리에 유지할 수 있습니다 — 경험상 파일 크기에 따라 대략 50개 파일까지 가능합니다.

일반적인 세션:

You: Add a new SettingsView with toggles for notifications and haptic feedback.
     Use the existing Settings.swift pattern from TimerManager.
     Build for simulator after implementation and fix any errors.

Claude: [reads Settings.swift, TimerManager.swift, existing views]
        [creates SettingsView.swift]
        [calls build_sim via MCP]
        [reads structured error: missing import HealthKit]
        [fixes import]
        [calls build_sim again  success, 0 errors, 1 warning]
        [calls test_sim  all tests pass]

MCP 이전 워크플로우와의 핵심 차이점은 에이전트가 수동 빌드를 요청하거나 에러 출력을 붙여넣으라고 하지 않는다는 것입니다. 빌드-에러-수정 루프가 자율적으로 작동합니다.

2. Codex CLI

개요: OpenAI의 터미널 기반 코딩 에이전트입니다. 개념적으로 Claude Code와 유사하지만 OpenAI 모델(GPT-4o, o3)을 사용하며 권한 모델이 다릅니다.

MCP 통합: Codex는 codex mcp add 명령을 통해 MCP를 지원합니다. Apple의 Xcode MCP는 바로 연결됩니다:

# Add Apple Xcode MCP to Codex
codex mcp add xcode -- xcrun mcpbridge

XcodeBuildMCP도 동일한 npx 명령으로 Codex에서 사용할 수 있습니다:

# Add XcodeBuildMCP to Codex
codex mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp

적합한 용도: 헤드리스 배치 작업, CI/CD 통합, 그리고 다른 모델 계열의 세컨드 오피니언이 필요한 작업. Codex의 샌드박스 모드는 격리된 환경에서 코드를 실행하므로, 상태를 변경하는 테스트 스위트 실행과 같은 파괴적 작업에 유용합니다.

Claude Code와의 주요 차이점: - Claude 모델 대신 OpenAI 모델 사용 - 컨텍스트 윈도우 크기와 토큰 비용 구조가 다름 - 샌드박스 우선 권한 모델(기본적으로 더 제한적) - MCP 생태계 규모가 작음(테스트된 커뮤니티 서버 수가 적음) - 훅 시스템 사용 가능(v0.119.0+)하지만 Claude Code보다 성숙도가 낮음 — 이벤트 타입이 적고 조건부 if 필드가 없음

iOS에서 Claude Code 대신 Codex를 사용해야 할 때:

모델 다양성이 필요할 때 Codex를 사용하세요 — 두 번째 에이전트가 첫 번째 에이전트가 작성한 코드를 리뷰하면 서로 다른 유형의 오류를 발견할 수 있습니다. collab 워크플로우(Claude가 빌드하고 Codex가 리뷰)는 iOS에 효과적인데, 한 모델 계열에는 올바르게 보이는 SwiftUI 패턴이 다른 모델에서는 미묘한 문제를 가질 수 있기 때문입니다. Metal 셰이더와 동시성 패턴은 특히 듀얼 모델 리뷰의 혜택을 받습니다.

3. Xcode 26.3 네이티브 에이전트

개요: Apple이 AI 코딩 에이전트를 Xcode의 Intelligence 패널에 직접 통합했습니다. Xcode 26.3부터 Xcode 설정 > Intelligence에서 Claude Agent와 Codex를 인텔리전스 제공자로 설정할 수 있습니다.

설정:

  1. Xcode 26.3+ 열기
  2. Settings > Intelligence로 이동
  3. 새 제공자 추가:
  4. Claude의 경우: “Claude Agent”를 선택하고 Anthropic API 키 입력
  5. Codex의 경우: “Codex”를 선택하고 OpenAI API 키 입력
  6. Intelligence 사이드바에 에이전트가 나타나며 인라인으로 호출 가능

적합한 용도: 빠른 인라인 편집, 에이전트 수준의 추론을 활용한 코드 완성, 그리고 Xcode를 벗어나지 않으려는 개발자에게 적합합니다. 네이티브 통합이므로 에이전트가 Xcode의 프로젝트 컨텍스트 — 열린 파일, 빌드 타겟, 스킴 설정 — 에 MCP 브릿지 없이 직접 접근할 수 있습니다.

CLI 에이전트 대비 제한 사항: - 훅 시스템 없음 — 저장 시 자동 포맷이나 .pbxproj 쓰기 차단을 적용할 수 없음 - CLAUDE.md 로딩 없음 — 프로젝트 수준 설정 파일을 읽지 않음 - 제한된 자율성 — 에이전트가 전체 프로젝트가 아닌 현재 파일이나 선택 영역에서 작동 - 서브에이전트 위임 없음 — 복잡한 다단계 작업을 병렬화할 수 없음 - MCP 서버 설정 없음 — 에이전트가 Xcode 내장 도구만 사용

Xcode 네이티브 에이전트를 사용해야 할 때:

터미널로 전환하는 것이 오버헤드인 빠르고 범위가 한정된 편집에 적합합니다. “이 모델에 계산 프로퍼티를 추가해 줘.” “이 함수의 유닛 테스트를 작성해 줘.” “이 뷰를 @Observable을 사용하도록 리팩토링해 줘.” 한두 개 파일만 수정하고 빌드-테스트 사이클이 필요 없는 작업에 사용하세요.

빌드, 테스트, 다중 파일 리팩토링, 또는 자율적 에러 수정이 필요한 작업에는 MCP가 연결된 CLI 에이전트를 사용하세요.

런타임 비교 매트릭스

기능 Claude Code CLI Codex CLI Xcode 26.3 네이티브
MCP 지원 전체(79개 도구) 전체(79개 도구) Xcode 내장 도구만
훅 시스템 있음(성숙) 있음(기본, v0.119.0+) 없음
CLAUDE.md / 프로젝트 설정 있음 codex.md 동등 기능 없음
자율 빌드-테스트-수정 있음(MCP 경유) 있음(MCP 경유) 부분적(인라인만)
서브에이전트 위임 있음(최대 10개 병렬) 없음 없음
컨텍스트 윈도우 1M 토큰(Opus 4.6) 모델에 따라 다름 제공자에 따라 다름
다중 파일 작업 전체 코드베이스 접근 전체 코드베이스 접근 현재 파일 / 선택 영역
.pbxproj 보호 훅 경유 수동 해당 없음(Xcode 네이티브 사용)
저장 시 포맷 PostToolUse 훅 경유 외부 도구 Xcode 설정
오프라인 사용 불가 불가 불가
비용 모델 Anthropic API 사용량 OpenAI API 사용량 제공자 API 사용량

권장 사항: Claude Code CLI를 주 런타임으로 사용하세요. 빠른 인라인 편집에는 Xcode 26.3 네이티브 에이전트를, 리뷰 패스와 배치 작업에는 Codex CLI를 사용하세요. 세 가지는 경쟁이 아닌 상호 보완 관계입니다.


MCP 설정: 완전한 구성 가이드

MCP(Model Context Protocol)는 에이전트를 “Swift를 작성하고 빌드되기를 바라는 도구”에서 “Swift를 작성하고, 빌드하고, 구조화된 오류를 읽고, 수정하는 도구”로 변화시키는 핵심 요소예요. 이 섹션은 블로그 포스트보다 더 깊이 들어가서 — 두 서버 모두, 모든 설치 방법, 검증, 그리고 도구가 실제로 사용되도록 보장하는 에이전트 설정을 다룹니다.

XcodeBuildMCP: 헤드리스 iOS 개발을 위한 59개 도구

XcodeBuildMCPxcodebuild, xcrun simctl, LLDB를 59개의 구조화된 MCP 도구로 래핑합니다. Xcode를 실행하지 않아도 동작하며 — 전체 빌드-테스트-디버그 사이클이 Apple의 커맨드라인 도구를 통해 헤드리스로 작동해요.

설치 옵션:

# Option 1: Via npx (recommended — always uses latest version)
claude mcp add XcodeBuildMCP \
  -s user \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- npx -y xcodebuildmcp@latest mcp

# Option 2: Via Homebrew (pinned version, manual updates)
brew install xcodebuildmcp
claude mcp add XcodeBuildMCP \
  -s user \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- xcodebuildmcp mcp

# Option 3: Project-scoped (omit -s user)
claude mcp add XcodeBuildMCP \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- npx -y xcodebuildmcp@latest mcp

-s user 플래그를 사용하면 서버를 모든 프로젝트에서 전역으로 사용할 수 있어요. 프로젝트 범위 설치를 원한다면 이 플래그를 생략하세요(iOS 프로젝트에서만 MCP를 사용하고 웹 프로젝트에서는 사용하지 않으려는 경우에 유용합니다).

-e XCODEBUILDMCP_SENTRY_DISABLED=true 환경 변수는 크래시 리포트 텔레메트리를 비활성화합니다. XcodeBuildMCP는 기본적으로 Sentry를 포함하고 있어 파일 경로를 포함한 오류 데이터를 전송해요. 프로젝트에 진단 데이터를 기여하려는 게 아니라면 비활성화하세요.1

전체 도구 목록 (8개 카테고리에 걸친 59개 도구):

카테고리 도구 기능
프로젝트 탐색 discover_projs, list_schemes, list_targets .xcodeproj/.xcworkspace 파일 검색, 사용 가능한 스킴 및 타겟 목록 표시
빌드 build_sim, build_device, build_mac 파일 및 라인별로 구조화된 Xcode 오류/경고 출력과 함께 빌드
테스트 test_sim, test_device 메서드별 통과/실패 결과와 함께 테스트 실행
시뮬레이터 라이프사이클 list_sims, boot_sim, shutdown_sim, open_sim, session_set_defaults 시뮬레이터 생성, 부팅, 관리 및 설정
기기 관리 list_devices, install_device, launch_device 실제 기기 배포 및 관리
디버깅 debug_attach_sim, debug_attach_device, debug_breakpoint, debug_stack, debug_variables, debug_eval, debug_continue, debug_step, debug_detach 브레이크포인트 및 변수 검사를 포함한 완전한 LLDB 통합
UI 자동화 snapshot_ui, screenshot, tap, swipe, type_text 자동화된 인터랙션 및 시각적 캡처
프로젝트 스캐폴딩 create_project, add_file, add_package 프로젝트 생성 및 의존성 추가

일상 작업에서 가장 중요한 도구들:

  1. build_sim — 수백 번 호출하게 될 도구예요. 파일, 라인, 심각도별로 분류된 Xcode 오류를 반환합니다. 에이전트가 오류를 읽고, 해당 파일로 이동하여 여러분이 아무것도 건드리지 않아도 수정해요.

  2. test_sim — 테스트 메서드별 결과를 반환합니다. 에이전트는 단순히 “테스트 실패”가 아니라 정확히 어떤 테스트가 왜 실패했는지 알 수 있어요.

  3. list_sims + boot_simxcrun simctl 플래그를 외울 필요 없는 시뮬레이터 관리. 에이전트가 사용 가능한 런타임을 탐색하고 적절한 기기를 선택합니다.

  4. discover_projs + list_schemes — 프로젝트 인트로스펙션. 에이전트가 스킴 이름이나 워크스페이스 구조를 추측할 필요가 없어요.

  5. debug_attach_sim + debug_stack + debug_variables — 원격 LLDB 디버깅. 에이전트가 디버거를 열지 않고도 브레이크포인트를 설정하고, 변수를 검사하고, 코드를 단계별로 실행할 수 있습니다.

Apple Xcode MCP: Xcode와 연결하는 20개 도구

Apple의 MCP 서버는 Xcode 26.3에서 xcrun mcpbridge를 통해 제공됩니다. 실행 중인 Xcode 프로세스와 XPC(Apple의 프로세스 간 통신 프레임워크)를 통해 통신하며, CLI 도구로는 접근할 수 없는 내부 상태를 노출해요.

설치:

# Standard installation (global)
claude mcp add --transport stdio xcode \
  -s user -- xcrun mcpbridge

# For Codex CLI
codex mcp add xcode -- xcrun mcpbridge

Xcode 26.3 이상과 실행 중인 Xcode 프로세스가 필요합니다. Xcode가 열려 있지 않으면 이 서버를 통한 모든 MCP 호출이 실패하거나 멈춰요. XcodeBuildMCP에는 이런 제약이 없습니다.

도구 목록 (5개 카테고리에 걸친 20개 도구):

카테고리 도구 기능
파일 작업 XcodeRead, XcodeWrite, XcodeUpdate, XcodeGlob, XcodeGrep Xcode 프로젝트 컨텍스트 내에서 파일 읽기/쓰기
빌드 & 테스트 BuildProject, GetBuildLog, RunAllTests, RunSomeTests Xcode의 내부 빌드 시스템으로 빌드 및 테스트
진단 XcodeListNavigatorIssues, XcodeRefreshCodeIssuesInFile 실시간 코드 진단 (빌드 오류만이 아닌)
코드 & 문서 ExecuteSnippet, DocumentationSearch Swift REPL 실행 및 Apple 문서 검색
프리뷰 RenderPreview 헤드리스 SwiftUI 프리뷰 렌더링

Apple MCP에서만 사용 가능한 도구들 (XcodeBuildMCP에서는 제공되지 않음):

  1. DocumentationSearch — WWDC 세션을 포함한 Apple 개발자 문서를 검색합니다. Apple API 관련 질문에 대해 웹 검색보다 빠르고 신뢰할 수 있어요. “HKQuantityType(.dietaryWater)가 유효한가요?”라고 물으면 원본 소스에서 확정적인 답변을 얻을 수 있습니다.

  2. ExecuteSnippet — 프로젝트 컨텍스트 내에서 Swift REPL을 실행합니다. 에이전트가 전체 앱을 빌드하지 않고도 API 동작을 확인하고, 타입 변환을 테스트하고, 표현식을 검증할 수 있어요.

  3. RenderPreview — SwiftUI 프리뷰를 헤드리스로 렌더링합니다. 에이전트가 뷰가 오류 없이 렌더링되는지 확인할 수 있지만, 시각적 정확성은 평가할 수 없어요(렌더 결과가 시각적으로 검사되지 않고 데이터로 반환되기 때문).

  4. XcodeListNavigatorIssues — 빌드 오류뿐만 아니라 Xcode 분석기의 실시간 진단을 반환합니다. 사용하지 않는 변수, 잠재적 순환 참조, 빌드 시스템이 표면화하지 않는 지원 중단 경고 같은 문제를 감지해요.

두 서버를 모두 사용하는 이유

빌드와 테스트에서는 겹치지만 근본적으로 다릅니다:

┌─────────────────────────────────────────────────────────────────┐
                     MCP TOOL COVERAGE                           
├─────────────────────────────────────────────────────────────────┤
                                                                 
  XcodeBuildMCP (59 tools)        Apple Xcode MCP (20 tools)    
  ┌─────────────────────┐         ┌─────────────────────┐       
   Standalone                     Requires Xcode             
   (no Xcode process)            (XPC bridge)               
                                                            
    Simulators          BOTH     Documentation            
    Real devices       ┌─────┐   Swift REPL               
    LLDB debugging     Build   SwiftUI previews         
    UI automation      Test    Live diagnostics         
    Project scaffold   └─────┘   Analyzer issues          
    Screenshot                                             
  └─────────────────────┘         └─────────────────────┘       
                                                                 
└─────────────────────────────────────────────────────────────────┘

XcodeBuildMCP 사용 시점: 빌드-테스트-디버그 사이클. Xcode를 열지 않아도 동작하고, 시스템 메모리를 적게 소비하며, 더 풍부한 시뮬레이터 및 기기 관리를 제공합니다. 기본 빌드 도구로 사용하세요.

Apple Xcode MCP 사용 시점: 문서 조회, Swift REPL 검증, SwiftUI 프리뷰 렌더링, 실시간 진단. 이러한 기능이 필요한 세션에서는 Xcode를 열어 두세요.

실제 사용 경험: 저는 MCP 호출의 약 90%에 XcodeBuildMCP를, 문서 조회와 REPL 검증에 Apple Xcode MCP를 사용합니다. 에이전트는 빌드와 테스트에 기본적으로 XcodeBuildMCP를 사용하는데, Xcode 프로세스 오버헤드가 없어 더 빠르고 XPC 의존성이 없어 더 안정적이기 때문이에요.

검증

두 서버를 모두 설치한 후 연결을 확인하세요:

# List all configured MCP servers
claude mcp list

# Expected output includes:
# XcodeBuildMCP: npx -y xcodebuildmcp@latest mcp - Connected
# xcode: xcrun mcpbridge - Connected

서버가 “Disconnected”로 표시되거나 나타나지 않는 경우:

  1. XcodeBuildMCP가 연결되지 않을 때: Node.js가 설치되어 있는지 확인하세요(node --version). npx 명령어에는 Node.js 18 이상이 필요합니다.
  2. Apple Xcode MCP가 연결되지 않을 때: Xcode 26.3 이상이 설치되어 있고 터미널에서 xcrun mcpbridge 명령어가 동작하는지 확인하세요. 라이선스 동의를 위해 Xcode를 최소 한 번은 열어야 합니다.
  3. 둘 다 나타나지 않을 때: Claude Code를 재시작하세요(새 터미널에서 claude 실행). 세션 중간에 등록된 MCP 서버는 재시작할 때까지 나타나지 않을 수 있어요.

에이전트가 MCP를 사용하도록 가르치기

MCP 서버를 설치하는 것만으로는 충분하지 않아요. 명시적인 안내 없이는 에이전트가 Bash를 통해 xcodebuild를 직접 실행하거나(비구조화된 출력, 컨텍스트 토큰 낭비) Apple 문서를 웹 검색으로 찾으려 할 수 있습니다(더 느리고, 덜 신뢰할 수 있음).

CLAUDE.md나 에이전트 정의에 다음을 추가하세요:

## 빌드 및 테스트 — 항상 MCP 사용

모든 빌드 작업에서 쉘 명령어 대신 MCP 도구를 우선적으로 사용하세요:

- **빌드**: `build_sim` / `build_device` (Bash로 `xcodebuild` 사용 금지)
- **테스트**: `test_sim` / `test_device` (Bash로 `xcodebuild test` 사용 금지)
- **시뮬레이터**: `list_sims`, `boot_sim`, `open_sim` (Bash로 `xcrun simctl` 사용 금지)
- **디버그**: `debug_attach_sim`, `debug_stack`, `debug_variables`
- **Apple 문서**: `DocumentationSearch` (Apple API 검색 시 WebSearch 사용 금지)
- **Swift 검증**: `ExecuteSnippet` (Bash로 `swift` 사용 금지)
- **미리보기**: `RenderPreview`로 헤드리스 SwiftUI 검증

MCP는 구조화된 JSON를 반환합니다. Bash는 비구조화된 텍스트를 반환합니다.
구조화된 데이터는 토큰 소비가 적고 오류 진단이 더 정확합니다.

이 지침은 에이전트가 MCP 도구를 먼저 사용하도록 유도합니다. 이 지침이 없으면 에이전트가 Bash로 긴 xcodebuild 명령어를 구성하고, 출력을 파싱하느라 수천 개의 컨텍스트 토큰을 소비하며, 실제 오류를 잘못 식별하는 경우가 발생합니다.


iOS 프로젝트를 위한 CLAUDE.md 패턴

CLAUDE.md는 에이전트 기반 개발에서 프로젝트 내 가장 중요한 파일입니다. 에이전트의 온보딩 문서로, 아키텍처 문서를 읽은 신입과 추측으로 일하는 신입의 차이를 만듭니다.

제가 관리하는 모든 iOS 프로젝트에는 CLAUDE.md가 있습니다. 8개 앱 전체에서 효과가 입증된 패턴을 소개합니다.

필수 섹션

모든 iOS CLAUDE.md에는 다음 6개 섹션이 필요합니다. 나머지는 선택 사항입니다.

1. 프로젝트 정보

# Return - Zen Focus Timer

**Bundle ID:** `com.941apps.Return`
**Target:** iOS 26+ / macOS Tahoe / watchOS 26+ / tvOS 26+
**Architecture:** SwiftUI with @Observable pattern, companion Watch and TV apps
**Swift version:** 6.2
**Minimum deployment:** iOS 26.0

이 정보가 중요한 이유: 에이전트는 코드를 작성하기 전에 배포 대상을 알아야 합니다. iOS 17을 대상으로 하는 에이전트는 NavigationView@ObservedObject를 사용하지만, iOS 26을 대상으로 하는 에이전트는 NavigationStack@Observable을 사용합니다. Bundle ID는 자격(entitlements)과 HealthKit 설정에 중요합니다. Swift 버전에 따라 동시성 모델(async/await vs. 완료 핸들러, 엄격한 동시성 vs. 유연한 동시성)이 결정됩니다.

2. 목적 주석이 포함된 파일 구조

## File Structure

Return/ ├── ReturnApp.swift # App entry, dark mode enforcement ├── ContentView.swift # Main timer view with theme backgrounds ├── TimerManager.swift # Timer state, logic, and repeat handling ├── AudioManager.swift # Sound playback with AVAudioPlayer ├── Settings.swift # Centralized settings with validation ├── SettingsSheet.swift # Settings UI ├── HealthKitManager.swift # Mindful session logging + cross-device sync ├── LiveActivityManager.swift # Lock Screen/Dynamic Island ├── Theme.swift # Theme definitions ├── ThemeManager.swift # Theme state management ├── VideoBackgroundView.swift # AVPlayer video backgrounds ├── GlassTextShape.swift # Core Text glyph paths for glass effect ├── GlassTimerText.swift # Timer text with glass material └── Constants.swift # App constants


각 파일명 뒤의 인라인 주석은 장식이 아닙니다. 작성할 수 있는 가장 효과적인 문서입니다. 에이전트가 새 기능을 추가할 위치를 결정할 때, 이 주석이 모든 파일을 읽지 않고도 첫 시도에 올바른 파일로 안내합니다.

안티패턴: 주석 없이 파일을 나열하는 것. TimerManager.swift만으로는 상태를 관리하는지, UI를 담당하는지, 둘 다인지 알 수 없습니다. TimerManager.swift # Timer state, logic, and repeat handling은 무엇이 해당 파일에 포함되어야 하고 무엇이 포함되지 말아야 하는지를 정확히 알려줍니다.

3. 빌드 및 테스트 명령어

## Build & Test

Build for iOS simulator:
```bash
xcodebuild -scheme Return -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build

Run tests:

xcodebuild -scheme Return -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test

Run tvOS tests:

xcodebuild -scheme ReturnTV -destination 'platform=tvOS Simulator,name=Apple TV' test

Prefer MCP tools (build_sim, test_sim) over these raw commands. MCP returns structured JSON with categorized errors.

에이전트가 MCP를 우선 사용해야 하지만 원시 명령어도 포함하세요. 원시 명령어는 대체 문서 역할을 하며 스킴 이름과 대상을 명시적으로 보여줍니다.

#### 4. 핵심 패턴과 규칙

```markdown

## Key Patterns

### Observable Architecture
- ALL view models use `@Observable` (NEVER `ObservableObject`)
- ALL navigation uses `NavigationStack` (NEVER `NavigationView`)
- State management via `@Observable` classes with `@MainActor` isolation

### Settings Pattern
- Centralized `Settings.shared` singleton
- All settings bounded to valid ranges with validation
- Sound names validated against whitelist
- Thread-safe access via @MainActor

### Audio System
- `AVAudioPlayer` with `.playback` category (plays in silent mode)
- Silent audio loop for background execution
- Bell playback with completion callbacks and token-based staleness

이러한 패턴은 에이전트가 비일관성을 도입하는 것을 방지합니다. 명시적인 패턴 문서가 없으면 에이전트가 한 파일에서는 ObservableObject를 사용하고 다른 파일에서는 @Observable을 사용하거나, 기존 Settings.shared 싱글톤 대신 새로운 설정 메커니즘을 만들 수 있습니다.

5. 에이전트가 절대 해서는 안 되는 것

## Rules

- **NEVER modify .pbxproj files** — create Swift files, then I will add them to Xcode manually
- **NEVER modify .xcodeproj/ contents directly**
- **NEVER add new package dependencies** without asking first
- **NEVER change the deployment target**
- **NEVER modify entitlements files** unless explicitly asked
- **NEVER use NavigationView** — always NavigationStack
- **NEVER use ObservableObject** — always @Observable
- **NEVER use @StateObject** — always @State with @Observable

명시적 금지 사항은 암묵적 기대보다 효과적입니다. 에이전트는 긍정적 제안보다 부정적 제약 조건을 더 안정적으로 따르는데, 이는 이진적(하라/하지 마라)이기 때문이며 휴리스틱(이것을 선호하라/때때로 저것을 사용하라)이 아니기 때문입니다.

6. 프레임워크별 컨텍스트

이 섹션은 앱마다 다릅니다. 설정이 직관적이지 않은 프레임워크에 포함하세요:

HealthKit 앱의 경우:

## HealthKit Configuration

- Entitlement: `com.apple.developer.healthkit`
- Info.plist keys:
  - `NSHealthShareUsageDescription`: "Return reads your mindful minutes..."
  - `NSHealthUpdateUsageDescription`: "Return logs meditation sessions..."
- Category types: `HKCategoryType(.mindfulSession)`
- Authorization checked on every write (user can revoke at any time)
- HealthKit is unavailable on tvOS — guard with `#if canImport(HealthKit)`

SwiftData 앱의 경우:

## SwiftData Models

### Model Relationships
- `GroceryList` has many `GroceryItem` (cascade delete)
- `GroceryItem` belongs to one `GroceryList`
- `GroceryItem` has optional `Category`

### Model Container Setup
- Configured in App struct with `modelContainer(for:)`
- Schema versioning: currently V2
- Migration plan: `GroceryMigrationPlan` handles V1 → V2

### Queries
- `@Query(sort: \GroceryItem.name)` for sorted fetches
- `@Query(filter: #Predicate { !$0.isCompleted })` for active items
- Always use `@Query` in views, `modelContext.fetch()` in managers

SpriteKit 앱의 경우:

## SpriteKit Scene Hierarchy

GameScene (SKScene) ├── backgroundLayer (SKNode, zPosition: -100) │ └── StarfieldNode (custom, parallax scrolling) ├── gameLayer (SKNode, zPosition: 0) │ ├── playerShip (PlayerNode, zPosition: 10) │ ├── enemyContainer (SKNode, zPosition: 5) │ └── bulletPool (SKNode, zPosition: 8) ├── effectsLayer (SKNode, zPosition: 50) │ └── ParticleManager (manages explosion/trail emitters) └── hudLayer (SKNode, zPosition: 100) ├── scoreLabel (SKLabelNode) └── healthBar (HealthBarNode)

- Physics categories defined in `PhysicsCategory.swift` as bitmasks
- Contact detection via `didBegin(_ contact:)` on GameScene
- Bullet pooling: pre-allocate 50, recycle via `removeFromParent()` + re-add

Metal 앱의 경우:

## Metal Pipeline

- Render pipeline: `MetalView``Renderer``ShaderLibrary`
- Compute pipeline: `AudioAnalyzer` → compute shader → texture output
- Shared uniforms struct: `Uniforms` in `ShaderTypes.h` (bridged to Swift)
- Frame timing: `CADisplayLink` drives render loop
- Buffer triple-buffering: 3 in-flight frames with semaphore

### Shader Files
- `Shaders.metal` — Main render shaders (vertex + fragment)
- `Compute.metal` — Audio analysis compute kernel
- `PostProcess.metal` — Bloom and color grading

### DO NOT modify Metal shaders without testing on device.
Simulator Metal is not representative of device GPU behavior.

실제 CLAUDE.md 예시: Banana List (SwiftUI + SwiftData + iCloud + MCP 서버)

6개 섹션이 중간 복잡도의 앱에서 어떻게 함께 작동하는지 보여주는 주석이 달린 예시입니다. iCloud 동기화와 앱 데이터를 Claude Desktop에 노출하는 커스텀 MCP 서버를 갖춘 53개 파일의 식료품 목록 앱 Banana List에서 사용하는 CLAUDE.md 패턴입니다:

# Banana List - Grocery List App

**Bundle ID:** `com.941apps.BananaList`
**Target:** iOS 26+
**Architecture:** SwiftUI + SwiftData + iCloud Drive sync
**Swift version:** 6.2
**Minimum deployment:** iOS 26.0

## Core Features

- Grocery lists with items, categories, and quantities
- iCloud Drive sync via SwiftData CloudKit integration
- Custom MCP server exposing list data to Claude Desktop
- Liquid Glass design system
- Haptic feedback on interactions
- Share sheets for list sharing

## 파일 구조

BananaList/ ├── BananaListApp.swift # App entry, model container setup ├── Models/ │ ├── GroceryList.swift # @Model: list with name, items, color │ ├── GroceryItem.swift # @Model: item with name, quantity, category, isCompleted │ ├── Category.swift # @Model: user-defined categories │ └── SampleData.swift # Preview and test data ├── Views/ │ ├── ListsView.swift # Main list of grocery lists │ ├── ListDetailView.swift # Items within a list │ ├── ItemRow.swift # Single item row with swipe actions │ ├── AddItemSheet.swift # New item form │ ├── CategoryPicker.swift # Category selection with create-new │ └── SettingsView.swift # App settings ├── Managers/ │ ├── CloudSyncManager.swift # iCloud Drive sync status and conflict resolution │ └── HapticManager.swift # UIImpactFeedbackGenerator wrapper ├── MCP/ │ ├── MCPServer.swift # MCP server for Claude Desktop integration │ ├── ListTools.swift # MCP tools: list CRUD operations │ └── ItemTools.swift # MCP tools: item CRUD operations └── Extensions/ ├── Color+Extensions.swift # Custom color definitions └── View+Extensions.swift # Reusable view modifiers

## SwiftData 모델

### 관계

- `GroceryList` 여러 `GroceryItem` 가져요 (cascade delete)
- `GroceryItem` 하나의 `GroceryList` 속해요 (필수)
- `GroceryItem` 선택적 `Category` 가져요
- `Category` 여러 `GroceryItem` 가져요 (삭제  nullify)

### 컨테이너 설정

```swift
@main
struct BananaListApp: App {
    var body: some Scene {
        WindowGroup {
            ListsView()
        }
        .modelContainer(for: [GroceryList.self, GroceryItem.self, Category.self])
    }
}

쿼리 패턴

  • 목록: @Query(sort: \GroceryList.name) var lists: [GroceryList]
  • 활성 항목: @Query(filter: #Predicate { !$0.isCompleted })
  • 카테고리별: fetch 후 메모리에서 필터링 (SwiftData predicate 제한 사항)

빌드 및 테스트

xcodebuild -scheme BananaList -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme BananaList -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test

MCP 도구(build_sim, test_sim)를 직접 명령어보다 우선적으로 사용하세요.

핵심 패턴

Observable + SwiftData

  • SwiftData @Model 클래스는 자동으로 Observable이에요
  • @Model 클래스에 @Observable을 추가하지 마세요 (중복이며 경고 발생)
  • 폼에서 모델 속성에 양방향 바인딩할 때는 @Bindable을 사용하세요
  • 뷰에서는 @Query를, 뷰가 아닌 코드에서는 modelContext.fetch()를 사용하세요

iCloud 동기화

  • SwiftData CloudKit 통합을 통해 자동으로 동기화돼요
  • 충돌 해결: last-write-wins (CloudKit 기본값)
  • 동기화 상태는 CloudSyncManager.shared.syncState로 확인할 수 있어요
  • 동일한 iCloud 계정으로 두 시뮬레이터에서 실행하여 동기화를 테스트하세요

MCP 서버 아키텍처

  • 포트 8765에서 로컬 WebSocket 서버로 실행돼요
  • 6가지 도구 제공: listAll, getList, createList, addItem, completeItem, deleteItem
  • Claude Desktop은 ~/.config/claude-desktop/config.json의 MCP 설정을 통해 연결돼요

규칙

  • .pbxproj 또는 .xcodeproj 내용을 절대 수정하지 마세요
  • 모델 스키마를 변경할 때는 반드시 SampleData.swift도 업데이트하세요
  • ObservableObject를 절대 사용하지 마세요 — SwiftData 모델은 이미 Observable이에요
  • @StateObject를 절대 사용하지 마세요 — @Observable 클래스에는 @State를 사용하세요
  • NavigationView를 절대 사용하지 마세요 — 항상 NavigationStack을 사용하세요
  • @Model 클래스에 @Observable 매크로를 절대 추가하지 마세요
  • 폼에서 모델 속성에 바인딩할 때는 항상 @Bindable을 사용하세요
  • iCloud 동기화 변경 사항은 항상 두 시뮬레이터 인스턴스에서 테스트하세요
### 실제 CLAUDE.md: Reps (최소 SwiftData 앱 — 14개 파일)

소규모 프로젝트의 경우 CLAUDE.md를 간결하게 작성할 수 있어요. 다음은 14개 파일로 구성된 운동 추적 앱 Reps의 패턴이에요. 짧은 CLAUDE.md라도 6가지 필수 섹션을 모두 다루고 있다는 점에 주목하세요:

```markdown
# Reps - Workout Tracking

**Bundle ID:** `com.941apps.Reps`
**Target:** iOS 26+
**Architecture:** SwiftUI + SwiftData
**Swift version:** 6.2

## File Structure

Reps/ ├── RepsApp.swift # App entry, model container ├── Models/ │ ├── Workout.swift # @Model: workout with exercises, date, duration │ ├── Exercise.swift # @Model: exercise with sets, reps, weight │ └── ExerciseTemplate.swift # @Model: saved exercise definitions ├── Views/ │ ├── WorkoutListView.swift # Main list of workouts │ ├── WorkoutDetailView.swift # Exercises within a workout │ ├── ExerciseRow.swift # Single exercise with inline editing │ ├── AddExerciseSheet.swift # Exercise selection from templates │ ├── NewWorkoutView.swift # Start new workout flow │ └── StatsView.swift # Progress charts and summaries ├── Managers/ │ └── WorkoutTimer.swift # Active workout timer └── Extensions/ └── Date+Extensions.swift # Formatting helpers

## Build & Test

```bash
xcodebuild -scheme Reps -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme Reps -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test

SwiftData Relationships

  • Workout has many Exercise (cascade delete)
  • Exercise has optional ExerciseTemplate
  • ExerciseTemplate standalone (nullify on exercise delete)

Rules

  • NEVER modify .pbxproj
  • NEVER use ObservableObject — use @Observable
  • NEVER use NavigationView — use NavigationStack
  • @Model classes are already Observable — do not add @Observable macro
  • Use @Bindable for form bindings to model properties
40줄의 CLAUDE.md로 14 파일 프로젝트를 커버할  있어요. 작성하는  10분이면 충분하고, 에이전트의 혼란을 줄여  시간을 절약할  있어요.

### 실제 CLAUDE.md: Starfield Destroyer (SpriteKit + Metal  32 파일)

게임 프로젝트는 프레임워크별 컨텍스트가  필요해요. 에이전트가  그래프, 물리 카테고리, 게임 상태 머신을 이해해야 해요:

```markdown
# Starfield Destroyer - Space Shooter

**Bundle ID:** `com.941apps.StarfieldDestroyer`
**Target:** iOS 26+
**Architecture:** SpriteKit + Metal post-processing + Game Center
**Swift version:** 6.2

## Game Overview

99 levels across 3 galaxies. 8 unlockable ships with different stats.
Game Center leaderboards and achievements. Metal shader post-processing
for bloom and screen effects.

## File Structure

StarfieldDestroyer/ ├── StarfieldDestroyerApp.swift # App entry, Game Center auth ├── GameScene.swift # Main game scene, update loop ├── MenuScene.swift # Title screen, ship selection ├── Entities/ │ ├── PlayerShip.swift # Player node with physics, weapons, shields │ ├── EnemyShip.swift # Enemy base class with AI behaviors │ ├── Bullet.swift # Bullet pool node │ ├── PowerUp.swift # Collectible power-ups │ └── Boss.swift # Boss enemies (levels 33, 66, 99) ├── Systems/ │ ├── LevelManager.swift # Level progression, wave spawning │ ├── PhysicsCategory.swift # UInt32 bitmask categories │ ├── CollisionHandler.swift # Contact delegate methods │ ├── ScoreManager.swift # Score tracking, multipliers │ ├── ParticleManager.swift # Explosion, trail, shield emitters │ └── AudioManager.swift # Sound effects, background music ├── UI/ │ ├── HUDNode.swift # Score, health, level display │ ├── ShipSelectView.swift # SwiftUI ship selection (UIHostingController) │ ├── GameOverView.swift # Game over screen with score submission │ └── PauseMenu.swift # Pause overlay ├── Metal/ │ ├── MetalRenderer.swift # Post-processing render pipeline │ ├── BloomShader.metal # Bloom post-process effect │ └── ShaderTypes.h # Shared uniforms (bridging header) ├── Data/ │ ├── ShipData.swift # 8 ship definitions (speed, damage, shields) │ ├── LevelData.swift # 99 level configurations │ └── AchievementData.swift # Game Center achievement definitions └── GameCenterManager.swift # Leaderboard/achievement submission

## SpriteKit 씬 계층 구조

GameScene (SKScene) ├── backgroundLayer (zPosition: -100) │ └── StarfieldNode (parallax scrolling, 3 layers) ├── gameLayer (zPosition: 0) │ ├── playerShip (zPosition: 10) │ ├── enemyContainer (zPosition: 5) │ ├── bulletPool (zPosition: 8) — pre-allocated 50 bullets │ └── powerUpContainer (zPosition: 3) ├── effectsLayer (zPosition: 50) │ └── ParticleManager (explosion + trail emitters) └── hudLayer (zPosition: 100) ├── scoreLabel (SKLabelNode) ├── healthBar (custom SKShapeNode) └── levelLabel (SKLabelNode)

## 물리 카테고리

```swift
struct PhysicsCategory {
    static let none:      UInt32 = 0
    static let player:    UInt32 = 0b1        // 1
    static let enemy:     UInt32 = 0b10       // 2
    static let bullet:    UInt32 = 0b100      // 4
    static let powerUp:   UInt32 = 0b1000     // 8
    static let shield:    UInt32 = 0b10000    // 16
    static let bossBullet:UInt32 = 0b100000   // 32
}

// Contact pairs:
// player + enemy → damage
// player + powerUp → collect
// bullet + enemy → destroy
// player + bossBullet → damage

게임 상태 머신

.menu  .playing  .paused  .playing
                  .gameOver  .menu
                  .bossIntro  .playing
                  .levelComplete  .playing (next level)

Metal 후처리

  • 블룸 셰이더: BloomShader.metal — 다중 패스 가우시안 블러 + 가산 블렌딩
  • Uniforms: PostProcessUniforms { float intensity; float threshold; float2 resolution; }
  • SpriteKit이 각 프레임을 렌더링한 후 SKView.presentScene(:transition:)을 통해 적용돼요
  • 기기에서 테스트하지 않고 Metal 셰이더를 수정하지 마세요

빌드 및 테스트

xcodebuild -scheme StarfieldDestroyer -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme StarfieldDestroyer -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test

## 규칙

- .pbxproj를 절대 수정하지 마세요
- PhysicsCategory 비트마스크를 절대 수정하지 마세요 (모든 충돌 감지가 깨집니다)
- 렌더 순서를 이해하지 않고  계층 구조의 z 순서를 절대 변경하지 마세요
- Swift와 Metal 참조를 모두 업데이트하지 않고 ShaderTypes.h를 절대 수정하지 마세요
- 새로운 적을 추가할 때는 EnemyShip을 수정하지 말고 서브클래싱하세요
- 총알 풀링: removeFromParent() + 재추가로 재활용하고, 절대 새로 할당하지 마세요
- Game Center: 점수를 제출하기 전에 항상 isAuthenticated를 확인하세요

실제 CLAUDE.md: amp97 (Metal + 오디오 시각화 — 41개 파일)

Metal 프로젝트는 에이전트가 시각적 출력을 검증할 수 없기 때문에 가장 많은 프레임워크별 컨텍스트가 필요합니다:

# amp97 - Audio Visualizer

**Bundle ID:** `com.941apps.amp97`
**Target:** iOS 26+
**Architecture:** Metal render pipeline + AVAudioEngine analysis
**Swift version:** 6.2

## Architecture

Audio Input (microphone/file) → AVAudioEngine tap → FFT (vDSP) → Frequency/amplitude buffers → Metal compute shader (analysis) → Metal render pipeline (visualization) → CADisplayLink (60fps) → MTKView

## File Structure

amp97/ ├── amp97App.swift # App entry ├── Audio/ │ ├── AudioEngine.swift # AVAudioEngine setup, tap installation │ ├── FFTProcessor.swift # vDSP FFT, frequency bin extraction │ ├── AudioBuffer.swift # Ring buffer for audio data │ └── MicrophoneManager.swift # Microphone permission, session config ├── Rendering/ │ ├── MetalView.swift # MTKView wrapper for SwiftUI │ ├── Renderer.swift # Main render loop, pipeline state │ ├── ShaderLibrary.swift # Compiled shader management │ ├── BufferManager.swift # Triple-buffered uniform updates │ └── TextureManager.swift # Offscreen render targets ├── Shaders/ │ ├── Shaders.metal # Vertex + fragment shaders │ ├── AudioCompute.metal # Audio analysis compute kernel │ ├── PostProcess.metal # Bloom, color grading │ └── ShaderTypes.h # Shared uniforms (bridging header) ├── Visualizations/ │ ├── WaveformViz.swift # Oscilloscope-style waveform │ ├── SpectrumViz.swift # Frequency spectrum bars │ ├── CircularViz.swift # Radial visualization │ └── VizSelector.swift # Visualization switching ├── Views/ │ ├── MainView.swift # Full-screen viz with overlays │ ├── ControlsOverlay.swift # Play/pause, viz selection, gain │ └── SettingsView.swift # Audio source, sensitivity └── Extensions/ ├── SIMD+Extensions.swift # Vector math helpers └── Color+Metal.swift # UIColor → float4 conversion

## Metal Pipeline

### Uniforms (ShaderTypes.h)
```c
typedef struct {
    float time;
    float2 resolution;
    float audioLevel;       // 0.0-1.0 RMS amplitude
    float frequencyBins[64]; // FFT output, normalized
    float4x4 transform;
} Uniforms;

Render Pipeline

  1. Compute pass: AudioCompute.metal processes FFT data → texture
  2. Render pass: Shaders.metal reads texture + uniforms → visualization
  3. Post-process pass: PostProcess.metal applies bloom → final output

Buffer Management

  • Triple buffering with DispatchSemaphore(value: 3)
  • Uniforms updated per-frame on CPU, consumed by GPU 1-2 frames later
  • Audio data ring buffer: 4096 samples, lock-free single producer/consumer

Rules

  • NEVER modify ShaderTypes.h without updating BOTH Swift and Metal sides
  • NEVER exceed 64 frequency bins (fixed buffer size in shader)
  • NEVER test Metal visual output in simulator — device only
  • NEVER modify the audio engine tap format (48kHz, mono, float32)
  • Triple buffer discipline: always signal semaphore in completion handler
  • Audio session: .playAndRecord category with .defaultToSpeaker option
### 프로젝트 규모에 따른 CLAUDE.md 확장

적절한 세부 수준은 파일 수와 프레임워크 복잡도에 따라 달라집니다:

| 프로젝트 규모 | CLAUDE.md 깊이 | 예시 |
|-------------|----------------|---------|
| **소규모 (< 20 파일)** | 프로젝트 정보 + 파일 목록 + 규칙 | Reps (14 파일): 기본 SwiftData 패턴, 빌드 명령어, 금지 사항 |
| **중규모 (20-40 파일)** | + 프레임워크 컨텍스트 + 핵심 패턴 | TappyColor (30 파일): SpriteKit  계층 구조, 물리 카테고리, 게임 루프 |
| **대규모 (40 이상 파일)** | + 아키텍처 다이어그램 + 관계  + 멀티 타겟 정보 | Return (63 파일): 크로스 플랫폼 아키텍처, 세션 동기화 다이어그램, 플랫폼별 차이점 |
| **특수 (Metal/GPU)** | + 파이프라인 다이어그램 + 공유 타입 정의 + 버퍼 레이아웃 | amp97 (41 파일): 렌더 파이프라인 단계, uniform 구조체, 버퍼 관리 |

과도하게 문서화하는 비용은 거의 없습니다 (에이전트는 필요 없는 부분을 건너뜁니다). 반면 문서화가 부족하면 비용이 큽니다 (에이전트가 코드베이스와 충돌하는 패턴을 임의로 만들어냅니다).

### CLAUDE.md 체크리스트

iOS 프로젝트의 CLAUDE.md를 작성하거나 점검할   체크리스트를 활용하세요:

- [ ] Bundle ID와 배포 타겟이 명시되어 있는가
- [ ] Swift 버전과 아키텍처 패턴이 명시되어 있는가
- [ ] 인라인 용도 설명이 포함된 파일 구조가 있는가
- [ ] 올바른 scheme과 destination이 포함된 빌드 명령어가 있는가
- [ ] 올바른 scheme과 destination이 포함된 테스트 명령어가 있는가
- [ ] MCP 선호 사항이 명시되어 있는가 ("xcodebuild보다 build_sim 선호")
- [ ] @Observable 규칙이 있는가 (ObservableObject 사용 금지)
- [ ] NavigationStack 규칙이 있는가 (NavigationView 사용 금지)
- [ ] .pbxproj 수정 금지 규칙이 있는가
- [ ] 프레임워크별 컨텍스트가 있는가 (HealthKit 권한, SwiftData 관계, SpriteKit 계층 구조, Metal 파이프라인)
- [ ] 플랫폼 가용성 가드가 문서화되어 있는가 (#if canImport, #if os)
- [ ] 주요 싱글톤과 공유 패턴이 문서화되어 있는가
- [ ] 알려진 제한 사항이나 주의 사항이 명시되어 있는가

---

##  번째 에이전트 세션

MCP가 설정되고 프로젝트에 CLAUDE.md가 있다면, 효과적인  세션의 진행 과정을 살펴보겠습니다. 여기서는 Claude Code CLI를 사용하지만 워크플로우는 어떤 런타임에도 적용됩니다.

### 1단계: 에이전트가 프로젝트를 인식하는지 확인

You: Summarize this project. What scheme should I build with?

Claude: [reads CLAUDE.md] [calls discover_projs via MCP] [calls list_schemes via MCP]

    This is Return, a zen meditation timer for iOS 26+, watchOS, and tvOS.
    Available schemes: Return, ReturnWatch, ReturnTV, ReturnTests.
    Primary scheme for iOS: Return.
에이전트가 CLAUDE.md 내용을 참조하지 않는다면, 파일이 프로젝트 루트(`.xcodeproj` 또는 `Package.swift`와 같은 디렉토리)에 있는지 확인하세요.

### 2단계: 상태 점검 빌드 실행

You: Build the Return scheme for iOS simulator and report results.

Claude: [calls list_sims — finds iPhone 16 Pro on iOS 26.2] [calls boot_sim — boots the simulator] [calls build_sim — builds the project]

    Build succeeded. 63 source files compiled.
    2 warnings:
    - HomeView.swift:132 — UIScreen.main deprecated in iOS 26.0
    - LogWaterIntent.swift:61 — Result of try? is unused
    0 errors.
### 3단계: 테스트 실행

You: Run all tests and report pass/fail.

Claude: [calls test_sim]

    12 tests executed:
    ✓ TimerManagerTests (5/5 passed)
    ✓ SettingsTests (4/4 passed)
    ✓ ConstantsTests (3/3 passed)
    All tests passed.
### 4단계: 기능 구현

You: Add a new “Rain” theme to the theme system. Follow the existing pattern in Theme.swift and ThemeManager.swift. Include a rain ambient sound. Build and test after implementation.

Claude: [reads Theme.swift — understands Theme struct pattern] [reads ThemeManager.swift — understands theme registration] [reads existing theme definitions] [adds Rain theme to Theme.swift following existing pattern] [registers Rain theme in ThemeManager.swift] [calls build_sim — build succeeds] [calls test_sim — all tests pass]

    Added Rain theme:
    - Theme.swift: new `static let rain` with video, cover, ambient
    - ThemeManager.swift: added to `allThemes` array
    - Build: success (0 errors, 2 existing warnings)
    - Tests: 12/12 passed

    Note: You'll need to add rain_ambient.wav to the project
    and add the new Theme.swift changes to the Xcode target manually
    (I did not modify .pbxproj).
에이전트가 .pbxproj를 수정하지 않았다는 메모는 CLAUDE.md의 규칙이 반영된 결과입니다. 해당 규칙이 없었다면 에이전트는 프로젝트 파일을 수정하려 시도했을 것이고, 파일이 손상될 가능성이 높았을 것입니다.

---

## iOS에서 에이전트가 잘하는 작업

에이전트가 일관되게 정확하고 프로덕션에 바로 사용할  있는 결과물을 만들어내며, 사람의 리뷰가 거의 필요 없는 작업들입니다.

### SwiftUI 뷰와 수정자

에이전트는 SwiftUI의 선언적 구문에 대한 패턴 인식 능력이 뛰어납니다.  구성, 수정자 체이닝, 상태 바인딩, 레이아웃 등은 SwiftUI의 API 영역이  문서화되어 있고 패턴이 매우 일관적이기 때문에 에이전트의 학습 데이터와  맞습니다.

**에이전트가 특히 잘하는 부분:**
- 설명을 바탕으로 새로운  구축 ("X, Y, Z 토글이 있는 설정 시트 만들기")
- 수정자 체이닝 적용 (`.glassEffect()`, `.sensoryFeedback()`, `.navigationTitle()`)
- 레이아웃 패턴  변환 (VStack에서 LazyVGrid로, List에서 ScrollView로)
- SwiftData 모델에 `@Bindable`  바인딩 구현
- 샘플 데이터가 포함된 프리뷰 프로바이더 작성

**우수한 결과를 만들어내는 프롬프트 예시:**

Create a SettingsView that matches the existing pattern in SettingsSheet.swift. Include toggles for: - Enable haptic feedback (Settings.shared.hapticsEnabled) - Enable HealthKit logging (Settings.shared.healthKitEnabled) - Show session history (navigation link to SessionHistoryView)

Use Liquid Glass styling with .glassEffect() on section backgrounds. Follow the @Observable pattern, not ObservableObject.

구체적인 지시가 중요합니다. "설정 뷰를 만들어줘"라고 하면 범용적인 결과가 나옵니다. "SettingsSheet.swift의 기존 패턴에 맞는 SettingsView를 만들어줘"라고 하면 코드베이스와 일관된 결과가 나옵니다.

### SwiftData 모델과 쿼리

에이전트는 SwiftData의 `@Model` 매크로, 관계, `@Query` 패턴을 안정적으로 처리합니다.  프레임워크의 선언적 특성이 Django ORM이나 SQLAlchemy와 유사하여, 에이전트가 다양한 코드베이스에서 학습한 패턴과  맞습니다.

**에이전트가 특히 잘하는 부분:**
- 관계가 포함된 `@Model` 클래스 정의
- 정렬 디스크립터와 프레디케이트를 활용한 `@Query` 작성
- `modelContext` 통한 CRUD 작업 구현
- 스키마 버전  마이그레이션 계획
- 프리뷰 데이터  테스트 픽스처

**에이전트에게 가이드가 필요한 부분:**
- 복잡한 `#Predicate` 표현식 (SwiftData의 프레디케이트 DSL에는 에이전트가 항상 파악하지 못하는 제약사항이 있으므로, 알려진 제약사항을 CLAUDE.md에 문서화하세요)
- CloudKit 동기화 설정 (SwiftData를 통해 자동으로 처리되지만, 에이전트가 수동 동기화를 구현하려고   있습니다)

### 단위 테스트

에이전트가 작성하는 단위 테스트는 iOS 프로젝트에서 일관되게 높은 품질을 보여줍니다. 에이전트는 XCTest 패턴, 비동기 테스트 메서드, setup/teardown 라이프사이클을  이해하고 있습니다.

Write unit tests for TimerManager covering: 1. Initial state is .stopped 2. start() transitions to .running 3. pause() transitions to .paused 4. reset() returns to .stopped with original duration 5. Timer counts down correctly (test with 3-second duration)

에이전트는 `setUp()` `tearDown()` 포함된  구조화된 XCTest 케이스를 생성하며, 적절한 어서션과 타이머 기반 테스트를 위한 비동기 처리를 포함합니다.

### 리팩토링과 패턴 적용

에이전트는 기계적인 리팩토링에 탁월합니다. 뷰를 컴포넌트로 추출하거나, `ObservableObject` `@Observable` 변환하거나, `NavigationView`에서 `NavigationStack`으로 마이그레이션하거나, 여러 파일에 걸쳐 일관된 패턴을 적용하는 작업이 대표적입니다.

Refactor all views in the Views/ directory to use @Observable instead of ObservableObject. Update @StateObject to @State, @ObservedObject to direct property access, and @Published to plain properties.

에이전트는 각 파일을 체계적으로 처리하며, 변환을 정확하게 적용하고, 기존 기능을 유지합니다. 이는 활용도가 매우 높은 작업으로, 수동 편집으로 한 시간이 걸릴 리팩토링이 거의 완벽한 정확도로 몇 분 만에 완료됩니다.

### MCP를 통한 빌드 오류 진단

구조화된 MCP 출력이 있으면, 에이전트는 대부분의 개발자보다 빠르게 빌드 오류를 진단합니다. 에이전트는 오류 JSON를 읽고, 정확한 파일과 줄을 식별하고, 오류 메시지를 이해하고, 수정을 적용합니다 — 대부분 한 번의 턴으로 해결합니다.

**에이전트가 자율적으로 수정하는 오류:**
- 누락된 임포트
- 타입 불일치
- 프로토콜 준수 누락
- 사용 중단된 API 사용법 (대체 코드 포함)
- 필수 이니셜라이저 매개변수 누락
- 접근 제어 위반

**에이전트에게 도움이 필요한 오류:**
- 모호한 타입 해석 (여러 모듈이 동일한 타입을 정의하는 경우)
- 복잡한 제네릭 제약 조건 실패
- 매크로 확장 오류 (에이전트가 확장된 매크로 출력을 볼 수 없음)

### 시뮬레이터 관리

에이전트는 MCP를 통해 시뮬레이터 라이프사이클을 잘 처리합니다:

Boot an iPhone 16 Pro simulator on iOS 26, install the app, and take a screenshot.

에이전트는 `list_sims` 사용 가능한 런타임을 확인하고, `boot_sim`으로 시뮬레이터를 시작하고, `build_sim`으로 앱을 빌드  설치하고, `screenshot`으로 화면을 캡처합니다  모두 구조화된 MCP 호출을 통해 수행됩니다.

---

## iOS에서 에이전트가 잘 못하는 것들

에이전트가 실패하는 지점에 대한 솔직한 분석입니다. 이러한 한계를 알면 불필요한 좌절과 토큰 낭비를 방지할  있습니다.

### .pbxproj 파일 수정 — 절대 금지

이것은 iOS 에이전트 개발에서 가장 중요한 규칙입니다. `.pbxproj` 파일은 Xcode의 프로젝트 설정 파일로, UUID 참조, 빌드 단계 목록, 타겟 멤버십이 포함된 구조화된 텍스트 파일입니다. 명목상으로는 사람이 읽을  있지만, 실질적으로 AI 에이전트가 파싱하기는 불가능합니다.

**에이전트가 .pbxproj에서 실패하는 이유:**
-  파일은 위치 의존적인 커스텀 포맷을 사용합니다 (JSON, YAML, XML 어느 것도 아닙니다)
- 모든 항목이 UUID로 상호 참조되어 있어, 파일 하나를 추가하려면 3~5개의 서로 다른 섹션을 일관성 있게 업데이트해야 합니다
-  글자만 잘못 배치되어도 전체 프로젝트 파일이 손상됩니다
- Xcode의 .pbxproj 병합 충돌 해결은 이미 불안정한데, 에이전트의 편집은 상황을  악화시킵니다

**에이전트가 .pbxproj를 편집하면 발생하는 :**
1. 편집이 성공한 것처럼 보입니다 (에이전트가 "파일이 업데이트되었습니다"라고 보고합니다)
2. Xcode가 프로젝트를 열지 못합니다 ("프로젝트 파일이 손상되었습니다")
3. git 히스토리에서 복구하는  15~60분을 소비합니다
4. PreToolUse 훅을 추가하는 법을 배우게 됩니다 ([](#hooks-for-ios-development) 참조)

**올바른 워크플로:** 에이전트가 Swift 파일을 생성하면, 여러분이 직접 Xcode 프로젝트에 추가합니다 (Xcode로 드래그하거나, File > Add Files 사용). 파일당 5초면 되고,  시간에 걸친 복구 작업을 방지할  있습니다.

**Swift Package Manager 프로젝트의 경우:**  제한은  심각합니다. `Package.swift` 에이전트가 안정적으로 편집할  있는 표준 Swift 파일입니다. 프로젝트가 SPM만 사용한다면 (.xcodeproj 없이), 에이전트가 전체 프로젝트 구조를 관리할  있습니다.

### 복잡한 Interface Builder / Storyboard 편집

프로젝트가 Interface Builder (`.xib` 파일) 또는 Storyboard (`.storyboard` 파일) 사용하는 경우, 에이전트는 이를 의미 있게 편집할  없습니다.  파일들은 자동 생성된 UUID, 제약 조건 참조, 아울렛 연결이 포함된 XML 파일로, 텍스트 편집이 아닌 시각적 편집을 위해 설계되었습니다.

**대응 방법:** 새로운 뷰는 SwiftUI만 사용하세요. 프로젝트에 레거시 Interface Builder 파일이 있다면, 그대로 두고 새로운 UI는 SwiftUI로 구축하세요.

### 성능 최적화

에이전트는 올바른 코드를 작성하지만, 반드시 성능이 좋은 코드를 작성하는 것은 아닙니다. 앱을 프로파일링하거나, 병목 지점을 식별하거나, 프레임 레이트를 측정할  없습니다. 성능 최적화에는 다음이 필요합니다:

1. Instruments 프로파일링 (시각적 도구로, 에이전트가 접근할  없습니다)
2. 특정 기기의 GPU/CPU 특성에 대한 이해
3. 측정 기반의 반복적인 변경

**이런 상황에서 나타납니다:**
- Metal 셰이더 최적화 (에이전트가 유효한 Metal을 작성하지만 GPU 프레임 타임을 측정할  없습니다)
- SwiftUI  body 복잡도 (에이전트가 깊이 중첩된 뷰를 생성하여 리드로우 오버헤드를 유발합니다)
- Core Data / SwiftData 페치 최적화 (에이전트가 올바른 쿼리를 작성하지만 대규모 데이터셋에서는 느릴  있습니다)

**대응 방법:** 구현에는 에이전트를 활용하고, Instruments로 직접 프로파일링한 , 식별한 특정 최적화를 에이전트에게 적용하도록 요청하세요.

### 코드 서명 및 프로비저닝

에이전트는 오류 메시지를 읽는  이상으로 코드 서명 문제를 디버깅할  없습니다. 프로비저닝 프로파일 관리, 인증서 생성, 엔타이틀먼트 설정, App Store 제출은 근본적으로 Apple Developer 포털, 키체인 접근, Xcode의 서명 UI를 통해 사람이 직접 수행하는 워크플로입니다.

**에이전트가 보는 :** "Signing for 'Return' requires a development team."

**에이전트가   없는 :** 인증서가 만료되었는지, 프로비저닝 프로파일에 해당 기기가 포함되어 있는지, 번들 ID가 App ID와 일치하는지, 엔타이틀먼트 파일이 올바른지 여부.

**대응 방법:** 모든 서명 작업은 Xcode의 Signing & Capabilities 탭에서 처리하세요. 에이전트에게 서명 실패를 디버깅하도록 요청하지 마세요.

### 복잡한 Metal 셰이더 디버깅

에이전트는 문법적으로 올바른 Metal Shading Language (MSL) 작성하지만, 시각적 출력을 검증하거나 GPU  문제를 디버깅할  없습니다. Metal 셰이더는 GPU에서 실행되기 때문에, 에이전트에게는 셰이더가 올바른 시각적 결과를 생성하는지 확인할 피드백 메커니즘이 없습니다.

**에이전트가 Metal로   있는 :**
- 설명을 바탕으로 버텍스  프래그먼트 셰이더 작성
- Swift에서 Metal 렌더 파이프라인 설정
- 데이터 병렬 연산을 위한 컴퓨트 셰이더 생성
- `.metal` 파일의 컴파일 오류 수정

**에이전트가 Metal로   없는 :**
- 셰이더 출력의 시각적 정확성 검증
- GPU 성능 디버깅 (프레임 타임, 점유율, 메모리 대역폭)
- 시각적 아티팩트 진단 (밴딩, 정밀도 문제, 잘못된  공간)
- 서로 다른 GPU 아키텍처에서의 테스트 (A 시리즈 vs. M 시리즈 동작 차이)

**대응 방법:** Metal 셰이더는 실제 기기에서 테스트하세요. 시뮬레이터의 Metal 구현은 기기 GPU 동작을 대표하지 않습니다. 시각적 디버깅에는 Xcode의 GPU Frame Capture를 사용하세요.

### 시각적 레이아웃 검증

에이전트는 앱의 UI를   없습니다. SwiftUI 레이아웃 코드를 작성하고 컴파일이 되는지 확인할 수는 있지만, 결과 화면이 올바르게 보이는지는 판단할  없습니다. 10픽셀 중앙에서 벗어나거나, 잘못된 폰트 굵기를 사용하거나, 요소가 겹치는 뷰도 빌드 오류를 발생시키지 않고 모든 로직 테스트를 통과합니다.

**대응 방법:** UI 변경 사항은 시각적으로 검토하세요. Xcode의 SwiftUI Previews를 사용하거나 (헤드리스 렌더링을 위해 Apple MCP의 `RenderPreview` 사용), 자동화된 시각적 회귀 감지를 위해 swift-snapshot-testing과 같은 라이브러리로 스냅샷 테스트를 고려하세요.

---

## iOS 개발을 위한 Hooks

Hooks는 에이전트 워크플로우의 특정 시점에서 결정적으로 실행되는  명령어입니다. 이것은 강제 메커니즘으로, ".pbxproj를 수정하지 마세요"(에이전트가 무시할  있는 제안) ".pbxproj를 수정할 수 없습니다"(하드 블록) 사이의 차이를 만들어 줍니다.

Hook 시스템에 대한 배경 지식은 [Claude Code hooks 가이드](/blog/claude-code-hooks-tutorial) 참조하세요.  섹션에서는 iOS에 특화된 hook 패턴을 다룹니다.

### PreToolUse: .pbxproj 쓰기 차단

모든 iOS 프로젝트에서 가장 중요한 hook입니다. 에이전트가 `.pbxproj` 파일, `.xcodeproj/` 디렉토리  기타 Xcode 관리 파일에 쓰는 것을 차단합니다:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode project files. Create Swift files and add to Xcode manually.\" >&2; exit 2; fi'"
      }
    ]
  }
}

이 설정을 프로젝트 루트의 .claude/settings.json이나 전역 보호를 위해 ~/.claude/settings.json에 배치하세요.

동작 원리: 에이전트가 패턴과 일치하는 파일에 대해 Edit 또는 Write 도구를 사용하려고 하면, hook이 실행되어 파일 경로를 감지하고 stderr에 경고를 출력한 뒤 종료 코드 2로 종료합니다(도구 사용이 차단됨). 에이전트는 오류 메시지를 수신하고 접근 방식을 조정합니다.

차단 대상: - 직접적인 .pbxproj 수정 - .xcodeproj/ 또는 .xcworkspace/ 디렉토리 내부의 모든 파일 - Interface Builder 파일 (.xib, .storyboard)

PostToolUse: SwiftFormat을 활용한 저장 시 자동 포맷팅

에이전트가 Swift 파일을 작성하거나 수정할 때마다 자동으로 포맷팅합니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftformat \"$FP\" --quiet 2>/dev/null; fi'"
      }
    ]
  }
}

요구 사항: SwiftFormat이 설치되어 있어야 합니다 (brew install swiftformat).

이것이 중요한 이유: 에이전트는 구문적으로 올바른 Swift를 생성하지만, 포맷팅 규칙을 일관되게 따르지는 않습니다. SwiftFormat은 들여쓰기, 중괄호 배치, import 정렬을 정규화합니다. 저장 시 포맷팅 hook 덕분에 에이전트가 수정하는 모든 Swift 파일이 여러분에게 보이기 전에 자동으로 포맷팅됩니다.

선택 사항: .swiftformat 설정 파일을 프로젝트 루트에 추가하여 포맷팅 규칙을 커스터마이즈할 수 있습니다:

# .swiftformat
--indent 4
--allman false
--stripunusedargs closure-only
--importgrouping testable-bottom
--header strip

PostToolUse: SwiftLint 자동 실행

SwiftLint를 사용하는 경우, Swift 파일이 수정될 때마다 실행합니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftlint lint --path \"$FP\" --quiet 2>/dev/null || true; fi'"
      }
    ]
  }
}

|| true는 lint 경고가 에이전트를 차단하지 않도록 합니다. lint 위반이 차단되기를 원한다면 이를 제거하세요.

PostToolUse: 변경 후 자동 빌드

적극적인 피드백 루프를 위해 Swift 파일이 변경될 때마다 빌드를 트리거합니다:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then xcodebuild -scheme Return -destination \"platform=iOS Simulator,name=iPhone 16 Pro\" build 2>&1 | tail -5; fi'"
      }
    ]
  }
}

주의: 이 방식은 비용이 큽니다. 파일을 수정할 때마다 빌드가 트리거됩니다. 신중하게 사용하세요 — 즉각적인 빌드 피드백이 필요한 디버깅 세션에서 가장 유용합니다. 일반적인 개발 시에는 에이전트가 준비되었을 때 MCP을 통해 수동으로 빌드를 트리거하도록 하세요.

PreToolUse: 엔타이틀먼트 수정 차단

엔타이틀먼트 파일이 에이전트에 의해 실수로 수정되지 않도록 보호합니다:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.entitlements$\"; then echo \"BLOCKED: Do not modify entitlements files without explicit permission.\" >&2; exit 2; fi'"
      }
    ]
  }
}

통합 iOS Hook 설정

다음은 모든 iOS 프로젝트에서 사용하는 전체 .claude/settings.json 설정입니다:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard|entitlements)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode-managed files. Create Swift files and add manually.\" >&2; exit 2; fi'"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftformat \"$FP\" --quiet 2>/dev/null; fi'"
      }
    ]
  }
}

이 설정은 두 가지를 보장합니다: 1. 에이전트가 Xcode 프로젝트 파일을 손상시킬 수 없습니다 (PreToolUse 차단) 2. 에이전트가 수정하는 모든 Swift 파일이 자동으로 포맷팅됩니다 (PostToolUse 포맷팅)


에이전트와 잘 맞는 아키텍처 패턴

모든 Swift 아키텍처가 에이전트에 동일하게 적합한 것은 아닙니다. 다음 패턴들은 명시적이고, 일관성이 있으며, 훈련 데이터에 충분히 반영되어 있어 가장 좋은 결과를 보여줍니다.

@Observable (ObservableObject 사용 금지)

iOS 26+ 대상 프로젝트에서는 @Observable만 사용해야 합니다. 이는 최신 패턴이자 에이전트 친화적인 패턴입니다:

// CORRECT — @Observable
@Observable
@MainActor
final class TimerManager {
    var timeRemaining: TimeInterval = 0
    var state: TimerState = .stopped

    func start() {
        state = .running
        // ...
    }
}

// In a view:
struct TimerView: View {
    @State private var timer = TimerManager()

    var body: some View {
        Text(timer.timeRemaining, format: .number)
    }
}
// WRONG — ObservableObject (deprecated pattern)
class TimerManager: ObservableObject {
    @Published var timeRemaining: TimeInterval = 0
    @Published var state: TimerState = .stopped
}

// WRONG — @StateObject (deprecated pattern)
struct TimerView: View {
    @StateObject private var timer = TimerManager()
}

@Observable이 에이전트 친화적인 이유: 패턴이 더 단순하고(@Published 어노테이션이 필요 없음), 소유권 모델이 명확하며(@StateObject vs. @ObservedObject 대신 @State 사용), 움직이는 부분이 적어 에이전트가 버그를 덜 만들어냅니다.

CLAUDE.md에 반드시 명시하세요: iOS 26을 대상으로 하더라도 에이전트가 훈련 데이터의 ObservableObject 패턴으로 돌아가는 경우가 있습니다. 명시적으로 금지해야 이를 방지할 수 있습니다.

// CORRECT
NavigationStack {
    List(items) { item in
        NavigationLink(value: item) {
            ItemRow(item: item)
        }
    }
    .navigationDestination(for: Item.self) { item in
        ItemDetailView(item: item)
    }
}

// WRONG
NavigationView {
    List(items) { item in
        NavigationLink(destination: ItemDetailView(item: item)) {
            ItemRow(item: item)
        }
    }
}

NavigationStack은 iOS 16 이상에서 사용 가능하며, 새 코드에서 유일하게 사용해야 하는 내비게이션 패턴입니다. 타입 안전한 navigationDestination(for:) 패턴은 에이전트가 잘못된 내비게이션 링크를 만드는 것을 방지합니다.

SwiftData를 활용한 영속성

SwiftData 모델은 에이전트 기반 개발에서 가장 깔끔한 영속성 패턴입니다:

@Model
final class GroceryItem {
    var name: String
    var quantity: Int
    var isCompleted: Bool
    var category: Category?
    var list: GroceryList?

    init(name: String, quantity: Int = 1) {
        self.name = name
        self.quantity = quantity
        self.isCompleted = false
    }
}

에이전트가 SwiftData를 다룰 때 핵심 규칙: 1. @Model 클래스는 자동으로 Observable이므로 @Observable을 추가하면 안 됩니다 2. 폼 바인딩에는 @Bindable을 사용하세요: @Bindable var item: GroceryItem 3. 뷰에서 반응형 데이터를 위해 @Query를 사용하세요: @Query var items: [GroceryItem] 4. 뷰가 아닌 코드에서는 modelContext.fetch()를 사용하세요 5. 관계 삭제에는 명시적 규칙이 필요합니다: .cascade, .nullify, .deny

Swift 6.2 동시성

새 프로젝트에서는 Swift 6.2 엄격 동시성을 적용하세요:

// Actor isolation for shared mutable state
@MainActor
@Observable
final class DataManager {
    var items: [Item] = []

    func loadItems() async throws {
        let fetched = try await api.fetchItems()
        items = fetched  // Safe: @MainActor isolated
    }
}

// Sendable conformance for cross-actor transfers
struct Item: Sendable, Identifiable {
    let id: UUID
    let name: String
    let createdAt: Date
}

에이전트를 위한 동시성 가이드: - 모든 뷰 모델에 @MainActor를 표시하세요 (데이터 레이스 경고 방지) - 모든 비동기 작업에 async/await를 사용하세요 (완료 핸들러 사용 금지) - 액터 간 전달을 위해 값 타입을 Sendable로 만드세요 - 뷰에서 비동기 초기화에는 Task { }를 사용하세요 - nonisolated는 성능 필요성을 측정한 경우에만 사용하세요

Liquid Glass 디자인 시스템 (iOS 26+)

iOS 26에서 Liquid Glass 디자인 시스템이 도입되었습니다. 에이전트에게 명시적인 지침을 제공하면 잘 처리합니다:

// Glass effect on containers
VStack {
    // content
}
.glassEffect()

// Glass effect with tint
Button("Action") { }
    .glassEffect(.regular.tint(.blue))

// Glass effect on navigation bars (automatic in iOS 26)
NavigationStack {
    // content
}
// Navigation bar automatically uses glass material

// Custom glass shapes
RoundedRectangle(cornerRadius: 16)
    .fill(.ultraThinMaterial)
    .glassEffect()

CLAUDE.md에 포함할 내용: “섹션 배경과 카드 컨테이너에 .glassEffect()를 사용하세요. 내비게이션 바는 iOS 26에서 자동으로 글래스 머티리얼을 적용합니다. 커스텀 머티리얼로 글래스 효과를 수동 재현하지 마세요 — 시스템 수정자를 사용하세요.”


프레임워크별 컨텍스트

각 Apple 프레임워크에는 에이전트 관련 고려 사항이 있습니다. 이 섹션에서는 8개 앱에서 사용하는 프레임워크를 다룹니다.

HealthKit

사용하는 앱: Return, Water

HealthKit은 권한 처리와 플랫폼 가드를 신중하게 다뤄야 합니다:

// Always check availability and authorization
import HealthKit

@MainActor
@Observable
final class HealthKitManager {
    private let store = HKHealthStore()
    var isAuthorized = false

    func requestAuthorization() async {
        guard HKHealthStore.isHealthDataAvailable() else { return }

        let types: Set<HKSampleType> = [
            HKQuantityType(.dietaryWater),
            HKCategoryType(.mindfulSession)
        ]

        do {
            try await store.requestAuthorization(toShare: types, read: types)
            isAuthorized = true
        } catch {
            // User denied — do not retry automatically
        }
    }
}

에이전트를 위한 HealthKit 규칙: - 항상 HKHealthStore.isHealthDataAvailable()로 가드하세요 - 권한을 가정하지 마세요 — 모든 쓰기 시 확인하세요 - 멀티 플랫폼 코드에서는 #if canImport(HealthKit)을 사용하세요 (HealthKit은 tvOS에서 사용 불가) - HealthKit이 제공하는 것 이상으로 건강 데이터를 로컬에 저장하지 마세요 - Info.plist에 NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription을 모두 포함하세요

SpriteKit

사용하는 앱: TappyColor, Starfield Destroyer

SpriteKit의 씬 그래프 모델은 에이전트에게 명시적인 지침이 필요합니다:

## SpriteKit Rules

- Scene hierarchy is a tree of SKNodes with zPosition ordering
- Physics bodies use category bitmasks (UInt32) for collision detection
- Node pooling: pre-allocate reusable nodes (bullets, particles)
- Never add nodes directly to the scene — use layer nodes for organization
- Update loop: `update(_ currentTime:)` runs every frame — keep it fast
- Actions: use SKAction sequences for animations, not manual property updates
- Textures: use texture atlases for performance (.atlas directories)

에이전트가 잘하는 SpriteKit 작업: - SKAction 시퀀스와 그룹 생성 - 물리 바디 및 접촉 감지 설정 - 게임 상태 머신 구현 - HUD 오버레이 구축

에이전트가 어려워하는 SpriteKit 작업: - 성능에 민감한 게임 루프 (에이전트가 프레임당 불필요한 작업을 추가함) - 복잡한 물리 시뮬레이션 (정밀도를 위해서는 커스텀 물리가 SKPhysicsBody보다 나음) - 파티클 이펙트 튜닝 (시각적 작업이라 반복 조정이 필요함)

Metal

사용하는 앱: amp97, Water, Starfield Destroyer

Metal은 에이전트가 가장 어려워하는 프레임워크입니다. GPU 프로그래밍 모델은 CPU 측 Swift와 근본적으로 다르며, 에이전트는 시각적 출력을 검증할 수 없습니다.

## Metal Rules

- Shared types between Swift and Metal go in a bridging header (ShaderTypes.h)
- Triple buffer in-flight frames (semaphore with value 3)
- Test shaders on DEVICE, not simulator (Metal behavior differs)
- Compute shaders: threadgroup size must divide evenly into grid size
- Fragment shaders: output color must be in correct color space (sRGB or linear)
- DO NOT optimize shaders without Instruments GPU profiling data

Metal 프로젝트의 CLAUDE.md에 포함할 내용: - Uniforms 구조체 정의 (Swift와 MSL 간 공유) - 렌더 파이프라인 상태 설정 패턴 - 버퍼 인덱스와 각각의 용도 - 어떤 셰이더가 존재하고 각각 무엇을 하는지 - 알려진 정밀도 이슈 (half vs. float)

Live Activities

사용하는 앱: Return

Live Activities는 특정 설정이 필요하지만, 문서화하면 에이전트가 잘 처리합니다:

## Live Activities

- ActivityAttributes defined in `TimerActivityAttributes.swift`
- ActivityKit framework: `import ActivityKit`
- Widget extension: `ReturnWidgets/ReturnLiveActivity.swift`
- Start: `Activity<TimerActivityAttributes>.request(attributes:content:)`
- Update: `activity.update(ActivityContent(state:staleDate:))`
- End: `activity.end(ActivityContent(state:staleDate:), dismissalPolicy:)`
- Push token: register for updates via `activity.pushTokenUpdates`

Game Center

사용하는 앱: Starfield Destroyer

## Game Center

- Authentication: `GKLocalPlayer.local.authenticateHandler`
- Leaderboards: `GKLeaderboard.submitScore(_:context:player:leaderboardIDs:completionHandler:)`
- Achievements: `GKAchievement.report(_:withCompletionHandler:)` (takes `[GKAchievement]` array)
- Always check `GKLocalPlayer.local.isAuthenticated` before submitting
- Handle authentication failure gracefully (offline play must work)

멀티 플랫폼 패턴

Return은 iOS, watchOS, tvOS를 아우릅니다. 에이전트를 활용한 멀티 플랫폼 개발에서는 플랫폼 경계를 명확히 문서화해야 합니다.

공유 코드 구성

Shared/
├── MeditationSession.swift    # Data model (all platforms)
├── SessionStore.swift         # iCloud sync (all platforms)
└── SessionHistoryView.swift   # UI (adapts per platform)

Return/                        # iOS-specific
ReturnWatch Watch App/         # watchOS-specific
ReturnTV/                      # tvOS-specific

에이전트를 위한 규칙:Shared/에 있는 파일을 수정하면 모든 플랫폼에 영향을 줍니다. 플랫폼 전용 디렉토리에 있는 파일은 해당 플랫폼에만 영향을 줍니다. 파일을 수정하기 전에 반드시 어떤 디렉토리에 있는지 확인하세요.”

플랫폼 가용성 가드

// HealthKit: available on iOS and watchOS, not tvOS
#if canImport(HealthKit)
import HealthKit
// HealthKit code here
#endif

// ActivityKit: available on iOS only
#if canImport(ActivityKit)
import ActivityKit
// Live Activity code here
#endif

// WatchKit: available on watchOS only
#if os(watchOS)
import WatchKit
// Watch-specific code here
#endif

에이전트 가이드: “플랫폼 전용 프레임워크를 사용할 때는 반드시 #if canImport() 또는 #if os() 가드를 적용하세요. 특정 프레임워크가 모든 타겟에서 사용 가능하다고 가정하지 마세요.”

플랫폼별 UI 적용

struct SessionHistoryView: View {
    @Query var sessions: [MeditationSession]

    var body: some View {
        List(sessions) { session in
            SessionRow(session: session)
        }
        #if os(tvOS)
        .focusable()
        #endif
        #if os(iOS)
        .swipeActions {
            Button("Delete", role: .destructive) {
                // delete
            }
        }
        #endif
    }
}

고급 워크플로우

자율 빌드-테스트-수정 루프

가장 강력한 패턴은 에이전트에게 기능 명세를 주고 빌드-테스트-수정 사이클을 자율적으로 반복하게 하는 것입니다.

Implement a countdown timer that:
1. Starts from a user-selected duration (10, 20, or 30 minutes)
2. Shows remaining time with a circular progress indicator
3. Plays a bell sound on completion
4. Logs the session to HealthKit as mindful minutes

Build after each change. Fix all errors. Run tests when the build succeeds.
Continue until all tests pass and the build is clean.

에이전트는 코드를 작성하고, MCP를 통해 빌드하고, 구조화된 오류를 읽고, 수정한 뒤 이 과정을 반복합니다. 사람이라면 5~10번의 빌드-오류-수정 사이클이 필요한 기능이 단일 자율 루프로 완료됩니다.

효과적인 경우: 명확한 인수 조건이 있는 잘 정의된 기능.

실패하는 경우: 열린 형태의 기능(“보기 좋게 만들어 줘”), 성능에 민감한 코드, 시각적 검증이 필요한 작업.

iOS용 서브에이전트 위임

Claude Code의 서브에이전트 시스템은 iOS 프로젝트에서도 활용할 수 있습니다:

Use a subagent to research the best approach for implementing
iCloud key-value store sync for meditation sessions across iOS,
watchOS, and tvOS. Report back with the recommended pattern.

서브에이전트는 별도의 컨텍스트 윈도우에서 문서와 코드 패턴을 탐색한 뒤 요약을 반환하고, 메인 세션에서 해당 권장 사항을 구현합니다. 이렇게 하면 리서치가 주요 컨텍스트를 소모하는 것을 방지할 수 있습니다.

크로스 앱 패턴 적용

일관된 패턴을 가진 여러 iOS 앱을 관리할 때, 에이전트가 한 앱의 패턴을 다른 앱에 적용할 수 있습니다:

Look at how Settings.swift works in the Return project
(centralized singleton with validation). Apply the same pattern
to create a Settings.swift for the Water project.

에이전트는 소스 패턴을 읽고 구조를 파악한 뒤, 대상 프로젝트에 일관된 구현을 생성합니다.

듀얼 에이전트 리뷰 (Claude + Codex)

중요한 변경 사항에는 서로 다른 모델 패밀리의 두 에이전트를 활용하세요:

  1. Claude Code가 구현을 작성합니다
  2. Codex CLI가 별도 패스로 리뷰합니다
# After Claude implements the feature:
codex "Review the changes in the last commit. Focus on Swift 6.2
      concurrency correctness, SwiftData relationship integrity,
      and potential retain cycles. Report issues only — no praise."

서로 다른 모델 패밀리는 서로 다른 종류의 오류를 포착합니다. Metal 셰이더나 동시성 패턴처럼 미묘한 버그가 발생하기 쉬운 영역에서 특히 유용합니다.

듀얼 리뷰가 단일 리뷰에서 놓치는 것을 잡아내는 이유:

이슈 유형 Claude 강점 Codex 강점
SwiftData 관계 순환 보통 강함 (GPT-4o)
@MainActor 격리 누락 강함 보통
Metal 버퍼 정렬 보통 보통
순환 참조 탐지 강함 (Opus) 강함 (o3)
API 디프리케이션 인식 강함 (최신 학습 데이터) 보통
동시성 레이스 컨디션 강함 강함 (다른 패턴 포착)

듀얼 리뷰의 목적은 더 많은 버그를 찾는 것이 아니라 다른 종류의 버그를 찾는 것입니다. 각 모델 패밀리는 패턴 인식에서 서로 다른 실패 모드를 가지고 있습니다.

여러 앱에 걸친 일괄 작업

프레임워크나 패턴 변경이 여러 앱에 영향을 미칠 때:

# Update @Observable pattern across all projects
for project in BananaList Return Water Reps; do
  cd ~/Projects/$project
  claude -p "Audit all files for any remaining ObservableObject usage.
             Convert to @Observable following the pattern in CLAUDE.md.
             Build and test after changes." --dangerously-skip-permissions
done

주의해서 사용하세요. --dangerously-skip-permissions 플래그는 비대화형 모드에서 필수이지만 모든 안전 검사를 우회합니다. .pbxproj 파일을 보호하기 위해 PreToolUse 훅이 설정되어 있는지 반드시 확인하세요.


실전 사례 연구

추상적인 조언은 쉽습니다. 여기서는 8개 앱에서 경험한 구체적인 시나리오를 통해 에이전트 기반 iOS 개발이 실제로 어떻게 작동하는지 보여드립니다 — 실패 사례도 포함해서요.

사례 연구 1: Return에 TV 앱 추가 (성공)

과제: iOS와 watchOS 버전이 이미 있는 명상 타이머 앱 Return에 tvOS 타겟을 추가하는 것이었습니다. TV 앱에는 Siri Remote 내비게이션, 대화면 UI, iOS 앱과의 설정 동기화가 필요했습니다.

에이전트가 잘 처리한 부분: - 기존 iOS TimerManager를 분석하고, Live Activities와 HealthKit(tvOS에서 사용 불가)을 제외한 TVTimerManager를 생성 - Siri Remote 포커스 내비게이션을 위한 커스텀 버튼 스타일 생성 (TVCapsuleButtonStyle, TVCircleButtonStyle) - 휠 피커(Siri Remote으로 사용 불가)를 +/- 버튼으로 대체하는 TVStepper 컴포넌트 구현 - App Groups (group.com.941apps.Return)를 통한 설정 동기화 구현 - 공유 코드 전반에 #if os(tvOS) 가드 추가 - MCP를 통해 platform=tvOS Simulator,name=Apple TV로 빌드 및 테스트

직접 처리해야 했던 부분: - Xcode에서 tvOS 타겟 생성 (File > New > Target > tvOS App) - Xcode 프로젝트에 새 타겟 추가 (.pbxproj 변경) - TV 타겟에 대한 App Groups 엔타이틀먼트 설정 - 기존 스킴에 TV 타겟 추가 또는 새 스킴 생성 - 에이전트가 생성한 Swift 파일들을 TV 타겟에 수동으로 추가 - Siri Remote 내비게이션을 직접 테스트 (에이전트는 포커스 동작을 평가할 수 없음)

결과: 15개의 새로운 Swift 파일, 완전히 작동하는 TV 앱을 약 3시간의 에이전트 기반 작업으로 구현했습니다. 수동으로 구현했다면 1-2일이 걸렸을 것입니다. 에이전트가 코드의 약 80%를 처리했고, Xcode UI 상호작용과 수동 테스트가 필요한 나머지 20%는 제가 처리했습니다.

사례 연구 2: amp97의 Metal 셰이더 디버깅 (부분 실패)

과제: 오실로스코프 셰이더에 에너지 기반 강도 시스템을 추가하는 것이었습니다. 시각화가 오디오 에너지에 맞춰 맥동해야 했습니다.

진행 과정: 1. 에이전트가 uEnergy 유니폼과 HDR 톤매핑을 추가하는 유효한 Metal 셰이더 수정 코드를 작성 2. 코드가 오류 없이 컴파일됨 3. 기기에서 시각화가 완전히 하얗게 표시됨 — 강도 계수가 10배 너무 높았음 (0.30이어야 할 값이 3.5) 4. 에이전트는 하얀 화면을 볼 수 없어 피드백 신호가 없었음 5. 제가 시각적으로 문제를 파악하고 에이전트에게 계수를 줄이도록 요청 6. 에이전트가 계수를 줄였지만, 전체 에너지 상태 머신이 너무 복잡해져 다른 방식으로 시각화를 깨뜨림 7. 전체 되돌림 — 두 커밋 (67959ed, cda4830)을 869d914에서 리버트

교훈: Metal 셰이더는 피드백 루프가 끊어져 있어 에이전트 기반 개발에서 가장 어려운 영역입니다. 에이전트는 구문(컴파일됨)과 의미론(올바른 타입)은 검증할 수 있지만, 출력(제대로 보이는지)은 검증할 수 없습니다. 시각적 동작을 변경하는 셰이더 수정은 반드시 기기에서 직접 확인해야 합니다.

이 경험 후 CLAUDE.md에 추가한 내용: “극도로 신중한 계수 테스트 없이 오실로스코프 셰이더에 에너지 상태 수정을 시도하지 마세요. 이전 시도에서 10배 너무 높은 계수로 시각화가 깨졌습니다.”

사례 연구 3: Banana List의 SwiftData 마이그레이션 (성공)

과제: 데이터 모델을 V1에서 V2로 마이그레이션하여 GroceryItemquantity 필드를 추가하고, 관계를 포함한 새로운 Category 모델을 추가하는 것이었습니다.

에이전트가 수행한 작업: 1. 기존 V1 모델 정의를 분석 2. 새 필드와 관계가 포함된 V2 모델 정의 생성 3. SchemaMigrationPlan 프로토콜을 준수하는 GroceryMigrationPlan 작성 4. V1toV2 마이그레이션 단계 구현: 기본값 quantity: 1category: nil 추가 5. 새 필드를 지원하도록 모든 뷰 업데이트 6. 프리뷰를 위한 SampleData.swift 업데이트 7. MCP를 통해 빌드 및 테스트 실행 — 모두 통과 8. 마이그레이션 전용 유닛 테스트 생성

성공의 핵심: SwiftData 마이그레이션은 Apple 문서와 학습 데이터에 충분히 반영된 명확한 프로토콜 패턴을 따르기 때문에 에이전트가 성공할 수 있었습니다. CLAUDE.md에 V1 모델이 명시적으로 문서화되어 있어 에이전트가 무엇을 마이그레이션하는지 이해할 수 있었습니다.

사례 연구 4: Return의 iCloud 세션 동기화 (복잡하지만 성공)

과제: 크로스 디바이스 명상 세션 로깅을 구현하는 것이었습니다. Apple TV나 Mac에서 완료한 세션이 iPhone으로 동기화되어 HealthKit에 기록되어야 했습니다.

에이전트가 만들어낸 결과:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
    tvOS              Mac              Watch     
 TVTimerMgr        TimerMgr          WatchTimer  
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
                                             
       └───────────────────┼───────────────────┘
                           
                           
              ┌────────────────────────┐
                   SessionStore       
                (iCloud Key-Value)    
              └───────────┬────────────┘
                          
                          
              ┌────────────────────────┐
                iPhone (on foreground)
                 Write to HealthKit  
              └────────────────────────┘

에이전트의 작업: 1. UUID, 날짜, 시간, 소스 기기, HealthKit 동기화 상태를 포함한 MeditationSession 데이터 모델 생성 2. iCloud 동기화를 위해 NSUbiquitousKeyValueStore를 관리하는 SessionStore 싱글톤 구축 3. 병합 충돌 해결 구현 (UUID 기반 중복 제거) 4. 플랫폼별 적응이 적용된 SessionHistoryView 추가 (iOS에서는 스와이프 삭제, tvOS에서는 포커스 기반) 5. 다른 기기의 세션을 위한 iPhone 측 HealthKit 동기화 연결

반복이 필요했던 부분: 초기 구현에서는 iPhone 앱이 백그라운드에서 실행되는 경우(동기화를 위한 포그라운드 알림 없음)를 처리하지 못했습니다. 에이전트에게 구체적인 안내가 필요했습니다: “NSUbiquitousKeyValueStore.didChangeExternallyNotification을 사용하여 백그라운드 KV 변경 시 동기화를 트리거하세요.” 이 힌트 이후 구현이 올바르게 완성되었습니다.

교훈: 아키텍처가 명확하게 기술되어 있으면 에이전트는 멀티 플랫폼 아키텍처 패턴을 잘 처리합니다. iCloud 동기화 패턴은 간단하지 않지만, 에이전트가 이해할 수 있는 문서화된 Apple 패턴을 따릅니다. 엣지 케이스(백그라운드 동기화)는 잘 문서화되지 않아 인간의 도메인 지식이 필요했습니다.

사례 연구 5: Starfield Destroyer의 Game Center 통합 (성공)

과제: 슈팅 게임에 Game Center 리더보드와 업적을 추가하는 것이었습니다.

에이전트가 잘 처리한 부분: - 앱 진입점에 GKLocalPlayer.local.authenticateHandler 구현 - 점수 제출과 업적 보고 메서드가 포함된 GameCenterManager 생성 - 모든 Game Center 작업 전 인증 상태 확인 추가 - 오프라인 상황을 우아하게 처리 (Game Center 없이 게임 진행, 재연결 시 제출) - 8단계 함선 진행 시스템에 맞는 업적 정의 생성

수동 작업이 필요했던 부분: - App Store Connect에서 리더보드와 업적 생성 (웹 포털, 에이전트 접근 불가) - Xcode에서 Game Center 엔타이틀먼트 설정 - 샌드박스 Game Center 계정으로 테스트 (기기에서 수동 로그인 필요)


에이전트와 함께하는 프로젝트 생명주기

새 iOS 프로젝트 시작하기

에이전트 지원을 활용한 새 프로젝트 시작의 최적 워크플로우:

1단계: 직접 설정 (15-30분) 1. Xcode 프로젝트 생성 (File > New > Project) 2. 서명 및 기능 설정 3. 배포 대상 및 지원 기기 설정 4. 필요한 엔타이틀먼트 추가 (HealthKit, Game Center 등) 5. 프로젝트 정체성과 규칙이 담긴 초기 CLAUDE.md 생성

2단계: 에이전트 구현 (수 시간 ~ 수일) 1. 에이전트가 데이터 모델 생성 (SwiftData, Core Data, 또는 일반 구조체) 2. 에이전트가 문서화된 패턴에 따라 뷰 구축 3. 에이전트가 매니저/서비스 클래스에 비즈니스 로직 구현 4. 에이전트가 유닛 테스트 작성 5. MCP를 통한 빌드-테스트-수정 루프 (자율 수행)

3단계: 직접 통합 (30-60분) 1. 에이전트가 생성한 파일을 Xcode 타겟에 추가 2. 서명 및 엔타이틀먼트 확인 3. 실제 기기에서 테스트 4. 시각적 레이아웃과 UX 검토 5. App Store Connect에 제출

기존 프로젝트 유지보수

이미 완성된 앱의 지속적인 개발:

You: Here's the next feature for Return: add a "Focus Score" that
     calculates from session duration and consistency. Show it on
     the main timer view. Build and test.

Claude: [reads CLAUDE.md, understands patterns]
        [reads TimerManager.swift, Settings.swift]
        [creates FocusScore.swift  model with calculation logic]
        [creates FocusScoreView.swift  display component]
        [integrates into ContentView.swift]
        [calls build_sim  1 error: missing import]
        [fixes import]
        [calls build_sim  success]
        [writes FocusScoreTests.swift]
        [calls test_sim  all pass]

에이전트의 효과는 CLAUDE.md가 프로젝트의 현재 상태를 얼마나 잘 반영하느냐에 비례합니다. 중요한 새 기능을 추가하거나, 아키텍처 패턴을 변경하거나, 새 프레임워크를 도입할 때 CLAUDE.md를 업데이트하세요.

에이전트 활용이 적합한 작업 vs. 그렇지 않은 작업

작업 에이전트 활용? 이유
새 SwiftUI 뷰 선언적 UI에서 에이전트가 뛰어남
SwiftData 모델 변경 명확하게 정의되고 테스트 가능
유닛 테스트 기계적이고 패턴 기반
리팩토링 체계적이고 다중 파일 처리 가능
빌드 오류 진단 예 (MCP 활용) 구조화된 피드백 루프
새 Xcode 타겟 아니오 Xcode UI와 .pbxproj 변경 필요
서명 및 프로비저닝 아니오 포털 기반, 에이전트 접근 불가
시각적 다듬기 아니오 인간의 미적 판단 필요
Metal 셰이더 튜닝 아니오 기기에서 GPU 테스트 필요
App Store 제출 아니오 포털과 Xcode Organizer 필요
성능 프로파일링 아니오 Instruments 필요
접근성 감사 부분적 에이전트가 레이블 추가 가능, VoiceOver는 직접 확인

에이전트 정의 설정하기

Claude Code의 에이전트 정의 시스템(.claude/agents/)을 사용한다면, iOS 전용 에이전트를 생성하세요:

---
name: ios-developer
description: iOS development agent with MCP build tools and SwiftUI expertise
tools:
  - XcodeBuildMCP
  - xcode
---

# iOS Developer Agent

You are an iOS development agent for apps targeting iOS 26+ with SwiftUI.

## Architecture Rules
- @Observable for all view models (NEVER ObservableObject)
- NavigationStack for all navigation (NEVER NavigationView)
- SwiftData for persistence
- Swift 6.2 strict concurrency
- @MainActor on all Observable classes

## Build & Test — Always Use MCP

Prefer MCP tools over raw shell commands for ALL build operations:

- **Build**: `build_sim` / `build_device` (NOT `xcodebuild` via Bash)
- **Test**: `test_sim` / `test_device` (NOT `xcodebuild test` via Bash)
- **Simulators**: `list_sims`, `boot_sim`, `open_sim`
- **Debug**: `debug_attach_sim`, `debug_stack`, `debug_variables`
- **Apple docs**: `DocumentationSearch` (NOT WebSearch for Apple APIs)
- **Swift verification**: `ExecuteSnippet` (NOT `swift` via Bash)

MCP returns structured JSON. Bash returns unstructured text.

## File Management Rules
- NEVER modify .pbxproj, .xcodeproj/, .xcworkspace/, .xib, .storyboard
- Create Swift files in the correct directory
- Report files that need manual addition to Xcode targets

## SwiftData Rules
- @Model classes are automatically Observable — do not add @Observable
- Use @Bindable for form bindings to model properties
- Use @Query in views, modelContext.fetch() elsewhere
- Document relationship delete rules

## When You Get Stuck
- Build errors: use `build_sim` via MCP for structured output
- API questions: use `DocumentationSearch` via Apple MCP
- Swift verification: use `ExecuteSnippet` via Apple MCP
- Never guess — verify with tools

Claude Code 세션에서 @ios-developer로 이 에이전트를 참조할 수 있습니다.


에이전트 기반 iOS 테스트 패턴

에이전트는 명확한 가이드를 제공하면 훌륭한 유닛 테스트를 작성합니다. 최상의 결과를 내는 패턴을 소개합니다.

테스트 파일 구성

# In CLAUDE.md:

## Test Structure

Tests mirror source structure:
- `ReturnTests/TimerManagerTests.swift` tests `TimerManager.swift`
- `ReturnTests/SettingsTests.swift` tests `Settings.swift`
- `ReturnTests/ConstantsTests.swift` tests `Constants.swift`

Test naming: `test_<what>_<condition>_<expected>`
Example: `test_start_whenStopped_transitionsToRunning`

테스트 프롬프팅

효과적인 테스트 프롬프트:

Write unit tests for TimerManager covering:

1. Initial state is .stopped with timeRemaining == selectedDuration
2. start() transitions state to .running
3. pause() from .running transitions to .paused
4. reset() from any state returns to .stopped with original duration
5. start() from .paused resumes (state becomes .running)
6. Edge case: reset() when already stopped is a no-op
7. Edge case: pause() when already paused is a no-op

Follow the existing test pattern in SettingsTests.swift.
Use setUp() to create a fresh TimerManager for each test.

이 프롬프트가 효과적인 이유: 번호가 매겨진 수락 기준이 에이전트에게 체크리스트를 제공합니다. 기존 테스트 파일을 참조하면 패턴이 확립됩니다. setUp() 사용을 명시하면 에이전트가 복잡하게 얽힌 테스트 상태를 만드는 것을 방지할 수 있습니다.

비효과적인 테스트 프롬프트:

Write tests for TimerManager.

이렇게 하면 엣지 케이스를 놓치고 프로젝트 패턴을 따르지 않는 일반적이고 피상적인 테스트가 생성됩니다.

비동기 테스트 패턴

타이머 기반 및 비동기 코드 테스트 방법:

// Agent produces this pattern when guided correctly:
final class TimerManagerTests: XCTestCase {
    var sut: TimerManager!

    @MainActor
    override func setUp() {
        super.setUp()
        sut = TimerManager()
    }

    @MainActor
    func test_start_whenStopped_transitionsToRunning() {
        // Given
        XCTAssertEqual(sut.state, .stopped)

        // When
        sut.start()

        // Then
        XCTAssertEqual(sut.state, .running)
    }

    @MainActor
    func test_timerCountsDown_afterOneSecond() async throws {
        // Given
        sut.selectedDuration = 10
        sut.reset()
        sut.start()

        // When
        try await Task.sleep(for: .seconds(1.1))

        // Then
        XCTAssertLessThanOrEqual(sut.timeRemaining, 9.0)
    }
}

에이전트에게 반복적으로 알려줘야 하는 핵심 패턴: - @MainActor 클래스를 테스트하는 메서드에 @MainActor 적용 - Task.sleep이나 비동기 작업을 사용하는 테스트에 async throws 적용 - 시간 기반 어설션에서 허용 오차 설정 (정확히 1.0초가 아닌 1.1초) - 테스트 격리를 위한 깔끔한 setUp() / tearDown() 구현

스냅샷 테스트

시각적 회귀 감지를 위해 swift-snapshot-testing 추가를 고려해 보세요:

Add snapshot tests for the main timer view in three states:
1. Stopped (showing full duration)
2. Running (showing countdown)
3. Completed (showing 00:00 with completion state)

Use SnapshotTesting library. Create reference images on first run.

에이전트는 스냅샷 테스트를 정확하게 설정하지만, 참조 이미지를 직접 검토할 수는 없습니다. 초기 스냅샷은 사용자가 직접 검토하고, 이후 변경 사항에서 에이전트의 테스트가 시각적 회귀를 감지합니다.


iOS 프로젝트의 컨텍스트 윈도우 관리

1M 컨텍스트 윈도우(Opus 4.6)는 크지만 무한하지는 않습니다. iOS 프로젝트에는 고유한 컨텍스트 관리 고려사항이 있습니다.

iOS 파일의 토큰 비용

파일 유형 일반적인 크기 대략적인 토큰 수
SwiftUI 뷰 (단순) 50-100줄 500-1,000
SwiftUI 뷰 (복잡) 200-400줄 2,000-4,000
SwiftData 모델 30-80줄 300-800
매니저/서비스 클래스 100-300줄 1,000-3,000
Metal 셰이더 (.metal) 50-200줄 500-2,000
유닛 테스트 파일 50-200줄 500-2,000
CLAUDE.md 100-300줄 1,000-3,000
MCP 응답 (빌드) 가변 200-2,000
MCP 응답 (테스트) 가변 500-5,000

50개 파일 프로젝트의 경우: 모든 파일을 읽으면 약 50,000-100,000 토큰을 소비하며, 1M 윈도우 내에 충분히 들어갑니다. 에이전트가 전체 프로젝트를 컨텍스트에 유지할 수 있습니다.

100개 이상 파일 프로젝트의 경우: 선택적 읽기가 필요합니다. 에이전트가 먼저 CLAUDE.md를 읽어 파일 구조 주석을 확인한 후, 필요에 따라 특정 파일을 읽습니다. CLAUDE.md의 파일 주석이 중요한 이유가 바로 이것입니다 — 모든 파일을 읽지 않고도 에이전트를 올바른 파일로 안내합니다.

대규모 프로젝트 전략

  1. 상세한 CLAUDE.md 파일 주석 — 에이전트가 파일 맵을 읽고 관련 파일로 직접 이동합니다
  2. 서브에이전트 위임 — 탐색과 조사를 서브에이전트에 위임합니다 (깨끗한 컨텍스트, 요약 반환)
  3. 집중된 프롬프트 — “설정 업데이트”보다 “SettingsView.swift를 수정해서 새 토글 추가”가 더 효과적입니다
  4. 세션 경계 설정 — 관련 없는 기능은 긴 세션을 연장하기보다 새 세션을 시작하세요
  5. /compact 사용 — Claude Code의 압축 명령으로 대화를 요약하고 컨텍스트를 확보합니다

MCP 토큰 효율성

MCP를 사용해야 하는 가장 강력한 이유 중 하나: 구조화된 JSON 응답이 원시 xcodebuild 출력보다 훨씬 적은 토큰을 소비합니다.

시나리오 Raw Bash 토큰 MCP 토큰 절감 효과
빌드 성공 3,000-10,000 200-500 85-95%
빌드 실패 (오류 1개) 3,000-10,000 300-800 90-92%
테스트 결과 (20개) 2,000-5,000 500-1,000 75-80%
시뮬레이터 목록 500-2,000 200-400 60-80%

10-20회 빌드 사이클이 포함된 일반적인 개발 세션에서 MCP는 원시 xcodebuild 대비 30,000-150,000 토큰을 절약합니다 — 이 토큰은 실제 코드 추론에 활용할 수 있습니다.


문제 해결

“build_sim failed — scheme not found”

에이전트가 스킴 이름을 추측하고 있습니다. 해결 방법:

Use discover_projs and list_schemes to find the correct scheme name
for this project before building.

또는 CLAUDE.md에 스킴 이름을 명시적으로 추가하세요:

## 빌드
Primary scheme: `Return` (iOS)
Watch scheme: `ReturnWatch` (watchOS)
TV scheme: `ReturnTV` (tvOS)

“xcrun mcpbridge — command not found”

Xcode 26.3 이상이 필요합니다. xcodebuild -version으로 확인하세요. Xcode 26.3 이상인데도 명령이 실패하는 경우:

# Ensure Xcode command line tools are selected
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

# Verify
xcrun mcpbridge --help

“MCP 도구가 Claude Code에 표시되지 않는 경우”

세션 중간에 등록된 MCP 도구는 재시작 전까지 표시되지 않을 수 있습니다. Claude Code를 종료하고 새 세션을 시작하세요:

# Exit current session (Ctrl+C or /exit)
# Start fresh
claude

그런 다음 확인합니다:

You: List all available MCP tools from XcodeBuildMCP.

“에이전트가 MCP 대신 Bash를 통해 xcodebuild를 계속 사용하는 경우”

에이전트가 Tool Search를 통해 MCP 도구를 발견하지 못하고 있습니다. 두 가지 해결 방법이 있습니다:

  1. CLAUDE.md에 명시적 가이드 추가 (에이전트에게 MCP 사용법 알려주기 참조)
  2. 직접 프롬프트: “Bash에서 xcodebuild를 사용하지 말고 build_sim MCP 도구를 사용하세요”

“빌드는 성공했는데 에이전트가 실패로 보고하는 경우”

XcodeBuildMCP는 xcodebuild 출력을 파싱합니다. 빌드에서 오류처럼 보이는 경고가 발생하면(사용 중단 경고에서 흔히 발생), 에이전트가 결과를 잘못 해석할 수 있습니다. MCP 응답의 실제 상태 필드를 확인하세요.

“시뮬레이터가 부팅 중 멈추는 경우”

모든 시뮬레이터를 종료하고 재부팅하세요:

xcrun simctl shutdown all
xcrun simctl boot "iPhone 16 Pro"

또는 에이전트에게 요청하세요:

Shut down all simulators, then boot a fresh iPhone 16 Pro.

“CLAUDE.md 규칙에도 불구하고 에이전트가 .pbxproj를 수정하려는 경우”

CLAUDE.md 규칙은 제안에 불과합니다. 훅이 강제 수단입니다. .pbxproj 쓰기를 차단하는 PreToolUse 훅이 없으면 에이전트는 결국 수정을 시도하게 됩니다. 훅을 설치하세요:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode project files.\" >&2; exit 2; fi'"
      }
    ]
  }
}

규칙은 “하지 마세요”라고 말하고, 훅은 “할 수 없습니다”라고 말합니다.


FAQ

어떤 에이전트 런타임부터 시작해야 하나요?

XcodeBuildMCP와 함께 Claude Code CLI를 추천합니다. 가장 깊은 MCP 통합, 가장 성숙한 훅 시스템, 그리고 iOS 프로젝트 전체를 작업 메모리에 담을 수 있는 1M 컨텍스트 윈도우(Opus 4.6)를 갖추고 있습니다. 여기서 시작한 후, 워크플로가 성숙해지면 리뷰용 Codex와 빠른 인라인 편집용 Xcode 네이티브 에이전트를 추가하세요.

두 MCP 서버가 모두 필요한가요?

대부분의 개발자에게 XcodeBuildMCP만으로 필요한 기능의 90%를 커버합니다(빌드, 테스트, 시뮬레이터, 디버깅). 문서 검색, Swift REPL 검증, 또는 SwiftUI 프리뷰 렌더링이 필요하면 Apple의 Xcode MCP를 추가하세요. 나중에 언제든 추가할 수 있으며, 두 서버는 독립적으로 운영됩니다.

에이전트가 새 Xcode 프로젝트를 처음부터 만들 수 있나요?

XcodeBuildMCP에는 새 Xcode 프로젝트를 스캐폴딩하는 create_project 도구가 포함되어 있습니다. 하지만 프로덕션 앱의 경우, Xcode에서 직접 프로젝트를 생성하여 서명, 기능, 타겟 구성을 올바르게 설정한 후 모든 코드 구현에 에이전트를 활용하는 것을 권장합니다. Xcode의 새 프로젝트 마법사에서 보내는 5분이 에이전트가 생성한 프로젝트 구성 문제로 인한 수 시간을 절약해 줍니다.

에이전트가 Swift Package Manager 의존성을 어떻게 처리하나요?

잘 처리합니다. Package.swift는 에이전트가 안정적으로 읽고 편집할 수 있는 표준 Swift 파일입니다. 의존성 추가, 버전 범위 업데이트, 타겟 구성 모두 정상적으로 동작합니다. 제한 사항은 .xcodeproj 기반 의존성 관리(Xcode의 패키지 해결 UI)입니다 — 이는 Xcode가 관리하며 에이전트가 편집해서는 안 됩니다.

에이전트가 App Store에 제출할 수 있나요?

아닙니다. App Store 제출에는 Xcode의 Organizer, 프로비저닝 프로파일, 스크린샷, 메타데이터, App Store Connect 포털이 필요합니다. 이 중 에이전트가 의미 있게 운영할 수 있는 MCP나 커맨드 라인 도구로 접근 가능한 것은 없습니다. 에이전트는 아카이브까지의 모든 것을 처리합니다 — 구현, 테스트, 버그 수정, 문서화. 제출의 마지막 단계는 사람이 직접 수행합니다.

다만, 에이전트는 App Store 메타데이터를 도울 수 있습니다. 최근 변경 사항을 기반으로 앱 설명, 키워드, 새로운 기능 텍스트를 작성하도록 에이전트에게 요청하세요. 이는 에이전트가 뛰어난 텍스트 생성 작업입니다.

에이전트 기반 iOS 개발에서 시크릿과 API 키는 어떻게 관리하나요?

시크릿은 절대 커밋하지 마세요. 백엔드 API에 연결하는 iOS 앱의 경우:

  1. 환경별 구성에 .xcconfig 파일을 사용하세요
  2. .xcconfig 파일을 .gitignore에 추가하세요
  3. Info.plist 빌드 설정을 통해 구성 값을 참조하세요
  4. 실제 값을 포함하지 않고 필요한 시크릿을 CLAUDE.md에 문서화하세요
## Configuration

API base URL and keys are in `Config.xcconfig` (not committed).
Required keys:
- `API_BASE_URL` — Backend server URL
- `API_KEY` — Authentication token

Create `Config.xcconfig` from `Config.xcconfig.template`.

에이전트는 키가 존재하고 어디서 사용되는지 알지만, 실제 값은 볼 수 없습니다.

SwiftUI 애니메이션도 에이전트가 작성할 수 있나요?

에이전트는 구문적으로 애니메이션 코드를 잘 작성하지만 결과를 시각적으로 검증할 수 없습니다. 간단한 애니메이션(.animation(.spring()), .transition(.slide), withAnimation { })은 올바른 결과를 만들어 냅니다. 정밀한 타이밍이 필요한 복잡한 다단계 애니메이션은 에이전트가 수행할 수 없는 시각적 반복 조정이 필요합니다.

효과적: “타이머가 상태 간 전환될 때 스프링 애니메이션을 추가하세요.”

비효과적: “타이머 애니메이션이 만족스럽게 느껴지도록 만들어 주세요.” (주관적이며, 시각적 튜닝이 필요합니다.)

에이전트가 에러 처리 패턴을 어떻게 다루나요?

매우 잘 처리합니다. 에이전트는 Swift의 do/catch, Result, async throws 패턴을 이해합니다:

Implement error handling for the HealthKit authorization flow:
1. Check HKHealthStore.isHealthDataAvailable()  show alert if not
2. Request authorization  handle denial gracefully
3. On write failure  retry once, then show error
4. All errors should be user-facing with localized descriptions

에이전트는 적절한 사용자 대상 메시지와 함께 구조화된 에러 처리를 작성합니다. 때때로 에러를 과도하게 처리하는 경향이 있어(전파되어야 할 예외를 캐치) catch 블록을 검토하는 것이 좋습니다.

접근성 구현에 에이전트를 사용할 수 있나요?

부분적으로 가능합니다. 에이전트는 접근성 레이블, 힌트, 특성을 올바르게 추가합니다:

Add accessibility labels to all interactive elements in TimerView:
- Timer display: current time remaining
- Start/Pause button: current state and action
- Reset button: "Reset timer"
- Duration picker: selected duration

에이전트가 할 수 없는 것: VoiceOver 내비게이션 순서가 올바른지 확인, Dynamic Type 스케일링 테스트, 색상 대비 비율 평가. 검증에는 Xcode의 Accessibility Inspector를 사용하세요.

에이전트가 Core Data 마이그레이션을 어떻게 처리하나요(SwiftData를 사용하지 않는 경우)?

에이전트는 Core Data 마이그레이션 매핑과 모델 버전을 작성하지만, 수동 Xcode 단계(새 모델 버전 생성, 현재 버전 선택)는 자동화할 수 없습니다. SwiftData 대신 Core Data를 아직 사용 중이라면, CLAUDE.md에 모델 버전 이력을 문서화하세요:

## Core Data 모델 버전
- V1: 초기 버전 (GroceryList, GroceryItem)
- V2: Category 모델 추가 (현재)
- 마이그레이션: V1→V2 경량 자동 마이그레이션

에이전트는 SwiftUI 미리보기를 어떻게 처리하나요?

두 가지 방법이 있습니다: 1. Apple Xcode MCP의 RenderPreview 도구는 미리보기를 헤드리스로 렌더링하고 결과를 반환합니다. 에이전트는 미리보기가 컴파일되고 오류 없이 렌더링되는지 확인할 수 있지만, 시각적 정확성은 평가할 수 없습니다. 2. 빌드 기반 검증build_sim을 통해 미리보기 프로바이더가 컴파일되는지 확인합니다. 미리보기가 런타임에 크래시되더라도 빌드는 성공합니다 — 크래시는 Xcode가 미리보기를 렌더링하려고 할 때만 발생합니다.

미리보기의 시각적 검증을 위해서는 여전히 Xcode를 열어야 합니다.

visionOS와 Apple Vision Pro는 어떤가요?

동일한 패턴이 적용됩니다. XcodeBuildMCP는 visionOS 시뮬레이터를 지원하며, 아키텍처 패턴(@Observable, NavigationStack, SwiftData)은 동일합니다. RealityKit 전용 코드(3D 콘텐츠, 몰입형 공간, 손 추적)는 Metal과 동일한 제약이 있습니다 — 에이전트가 올바른 코드를 작성할 수 있지만 공간 출력을 검증할 수는 없습니다.

에이전트가 어려움을 겪기 전까지 프로젝트가 얼마나 클 수 있나요?

컨텍스트 윈도우 크기가 제한 요소입니다. Opus 4.6의 1M 토큰 윈도우를 사용하면, Claude Code는 약 50-70개의 Swift 파일을 동시에 활성 작업 메모리에 유지할 수 있습니다. 더 큰 프로젝트의 경우, 에이전트는 파일 검색과 선택적 읽기를 사용하여 코드베이스의 일부분을 처리합니다. 100개 이상의 파일이 있는 프로젝트도 잘 작동합니다 — 에이전트가 모든 것을 컨텍스트에 유지하는 대신 필요에 따라 파일을 읽을 뿐입니다.

실질적인 한계는 파일 수가 아니라 코드베이스의 일관성입니다. 상세한 CLAUDE.md가 있는 잘 문서화된 200개 파일 프로젝트가 문서화되지 않은 30개 파일 프로젝트보다 더 나은 결과를 만들어냅니다.

에이전트를 iOS 개발에 사용하려면 Swift를 알아야 하나요?

에이전트의 출력을 리뷰하고 아키텍처 결정을 내릴 수 있어야 합니다. 모든 줄을 직접 작성할 필요는 없지만, 에이전트가 잘못된 선택을 할 때 이를 발견할 수 있을 정도로 Swift를 이해해야 합니다 — 특히 동시성, 메모리 관리, 프레임워크별 패턴과 관련해서요. 에이전트는 기존 실력을 10배로 증폭시키는 도구이지, 실력을 대체하는 것이 아닙니다.

에이전트는 Swift 파일의 머지 충돌을 어떻게 처리하나요?

에이전트는 Swift 소스 파일의 머지 충돌을 안정적으로 해결합니다. 표준 충돌 마커(<<<<<<<, =======, >>>>>>>)는 모든 에이전트 런타임에서 잘 인식됩니다. 하지만 .pbxproj 파일의 머지 충돌은 여전히 수동으로 해결해야 합니다 — 에이전트에게 .pbxproj 충돌 해결을 요청하지 마세요.

iOS 개발에 에이전트를 실행하는 비용은 얼마인가요?

Anthropic의 Max 플랜(Opus 4.6, 1M 컨텍스트)을 사용하면, 일반적인 iOS 개발 세션은 30-120분 동안 진행되며 200K-800K 토큰을 처리합니다. MCP 도구 호출은 최소한의 오버헤드만 추가합니다(구조화된 JSON 응답은 원시 빌드 출력에 비해 토큰 효율적입니다). 비용은 다른 코드베이스에서 Claude Code를 실행하는 것과 비슷합니다 — iOS 개발이 웹 개발보다 의미 있게 더 비싸거나 저렴하지 않습니다.

UIKit 프로젝트에서 에이전트를 사용할 수 있나요?

네, 하지만 에이전트는 SwiftUI에서 더 효과적입니다. UIKit은 더 많은 보일러플레이트가 필요하고, 선언적 구조가 적으며, 에이전트가 편집할 수 없는 Interface Builder 파일을 사용하는 경우가 많습니다. UIKit 프로젝트가 있다면 모델 레이어와 비즈니스 로직에 에이전트를 활용하고 UI는 수동으로 처리하거나, 점진적으로 뷰를 SwiftUI로 마이그레이션하는 것을 고려해 보세요.

에이전트는 현지화를 어떻게 처리하나요?

에이전트는 .xcstrings(Xcode 문자열 카탈로그) 파일을 효과적으로 생성하고 편집합니다. 새로운 현지화 키를 추가하고, 번역을 제공하며, 언어 간 일관성을 유지할 수 있습니다. .xcstrings 파일의 구조화된 JSON 형식은 에이전트 친화적입니다. .strings 파일(레거시 형식)도 에이전트가 잘 처리합니다 — 키-값 형식이 단순하기 때문입니다.


iOS에서 흔한 에이전트 실수 (그리고 예방하는 방법)

8개의 iOS 프로젝트에서 수천 번의 에이전트 상호작용을 통해 관찰한 반복적인 오류들입니다. 각각에 대한 예방 전략이 있습니다.

실수 1: Observable 패턴 혼용

발생 상황: 에이전트가 한 파일에서는 @Observable을 사용하고 다른 파일에서는 ObservableObject를 사용하거나, 이미 Observable인 @Model 클래스에 @Observable을 추가합니다.

예방 방법: CLAUDE.md에 명시적 규칙을 포함하세요:

- NEVER use ObservableObject — use @Observable
- NEVER add @Observable to @Model classes (already Observable)
- NEVER use @StateObject — use @State with @Observable
- NEVER use @ObservedObject — access @Observable properties directly

실수 2: 클로저에서 순환 참조 생성

발생 상황: 에이전트가 Timer.publish, NotificationCenter, 완료 핸들러에서 self를 강하게 캡처하는 클로저를 생성합니다.

예방 방법: CLAUDE.md에 클로저 패턴을 포함하세요:

## Closure Pattern
- Timer callbacks: use `[weak self]` and guard
- NotificationCenter observers: store in `Set<AnyCancellable>` and use `[weak self]`
- Completion handlers: use `[weak self]` for any closure stored beyond the call site

실수 3: @MainActor 요구사항 무시

발생 상황: 에이전트가 @MainActor 격리 없이 @Observable 클래스를 생성하여 Swift 6.2 동시성 경고나 메인 스레드 외부에서 UI 업데이트 시 런타임 크래시가 발생합니다.

예방 방법:

## Concurrency Rule
ALL @Observable classes MUST be @MainActor:
```swift
@Observable
@MainActor
final class SomeManager { }
### 실수 4: Destination 클로저를 사용한 NavigationLink

**발생 상황:** 에이전트가 타입 세이프한 `NavigationLink(value:)` + `.navigationDestination(for:)` 패턴 대신  이상 사용되지 않는 `NavigationLink(destination:label:)`을 사용합니다.

**예방 방법:**
```markdown

## Navigation Pattern
ALWAYS use value-based navigation:
```swift
NavigationLink(value: item) { ItemRow(item: item) }
.navigationDestination(for: Item.self) { ItemDetailView(item: $0) }

NEVER use: NavigationLink(destination: ItemDetailView(item: item)) { }

### 실수 5: 시뮬레이터 이름 하드코딩

**발생 상황:** 에이전트가 시스템에 존재하지 않을  있는 특정 시뮬레이터 이름("iPhone 16 Pro")으로 빌드 명령을 작성합니다.

**예방 방법:** MCP가 이를 처리합니다  `list_sims` 사용 가능한 시뮬레이터를 검색합니다. CLAUDE.md에 다음을 추가하세요:
```markdown

## Simulators
Do NOT hardcode simulator names. Use `list_sims` MCP tool to discover
available devices, then `boot_sim` with the discovered device ID.

실수 6: 잘못된 디렉토리에 파일 생성

발생 상황: 에이전트가 Views/ 하위 디렉토리 대신 프로젝트 루트에 새 뷰 파일을 생성하거나, 모델을 잘못된 그룹에 배치합니다.

예방 방법: CLAUDE.md의 파일 구조 주석이 배치를 안내합니다. 추가로:

## File Placement Rules
- Views → `AppName/Views/`
- Models → `AppName/Models/`
- Managers → `AppName/Managers/`
- Extensions → `AppName/Extensions/`
- Tests → `AppNameTests/`

실수 7: 플랫폼 가용성 미처리

발생 상황: 에이전트가 HealthKit을 사용할 수 없는 tvOS용 공유 코드에서 HealthKit을 사용하거나, watchOS 코드에서 ActivityKit을 사용합니다.

예방 방법:

## Platform Guards
- HealthKit: `#if canImport(HealthKit)` (unavailable on tvOS)
- ActivityKit: `#if canImport(ActivityKit)` (iOS only)
- WatchKit: `#if os(watchOS)`
- UIKit haptics: `#if os(iOS)` (unavailable on tvOS, watchOS uses WKHaptic)

실수 8: 단순한 기능의 과잉 설계

발생 상황: 에이전트가 20줄짜리 유틸리티 함수면 충분한 것에 프로토콜, 프로토콜 확장, 구체적 구현, 팩토리, 의존성 주입 컨테이너를 생성합니다.

예방 방법: 단순성 원칙을 포함하세요:

## Architecture Principle
Prefer the simplest solution that handles the requirements.
- Direct implementation over protocol abstraction (unless you have 2+ conforming types)
- Concrete types over generics (unless reuse is proven)
- Extensions on existing types over new wrapper types

솔직한 평가

8개의 iOS 앱을 AI 에이전트와 함께 출시한 후의 요약입니다:

에이전트가 변화시킨 것: 구현 속도. 며칠 걸리던 작업이 몇 시간이면 됩니다. SwiftUI 뷰, SwiftData 모델, 단위 테스트, 리팩토링 — 이제 주로 에이전트가 생산하고 사람이 리뷰합니다.

에이전트가 변화시키지 못한 것: 아키텍처 결정, 비주얼 디자인, 성능 최적화, App Store 제출. 이것들은 여전히 사람이 주도합니다.

배율 효과는 실제이지만 한계가 있습니다. 적절한 MCP와 훅 설정이 된 잘 문서화된 프로젝트에서 3-5배의 생산성 향상이 있다고 추정합니다. 훅이 없는 문서화되지 않은 프로젝트에서는 1.5-2배 정도의 향상에 그칩니다 — 에이전트가 빌드하는 대신 추측하는 데 너무 많은 시간을 소비하기 때문입니다.

투자 대비 효과가 큰 것: CLAUDE.md, 훅, MCP 설정에 투자한 시간. 설정에 들인 한 시간이 에이전트 실수를 수정하는 여러 시간을 절약합니다. 설정이 제품이고 — 에이전트는 실행 엔진입니다.

놀라웠던 것: MCP 서버가 역학 관계를 얼마나 바꿨는지입니다. MCP 이전에는 에이전트가 Swift를 이해하는 고급 텍스트 편집기에 불과했습니다. MCP 이후에는 코드를 작성하고, 빌드하고, 테스트하고, 디버깅하고, 반복하는 개발 파트너가 되었습니다. 구조화된 피드백 루프가 코드를 작성하는 에이전트와 코드를 출시하는 에이전트의 차이를 만듭니다.

과거의 나에게 하고 싶은 말: 가장 작은 앱(Reps, 14개 파일)부터 시작해서 MCP와 훅 설정을 제대로 잡고, 꼼꼼한 CLAUDE.md를 작성한 다음, 패턴을 더 큰 프로젝트로 확장하세요. 63개 파일의 멀티 플랫폼 앱부터 시작하지 마세요. 인프라 투자는 프로젝트 크기와 관계없이 동일합니다 — 작은 프로젝트에서 한 번 하고, 나머지에 복사하세요.

미래: Xcode 26.3의 네이티브 에이전트 통합은 시작이지, 끝이 아닙니다. Apple이 MCP 지원을 출시했다는 것은 도구 체인이 에이전트 우선 개발로 나아가고 있다는 의미입니다. 에이전트 호환 프로젝트 구조 — 깔끔한 CLAUDE.md 파일, 테스트 가능한 아키텍처, 자동화된 훅 — 에 지금 투자하는 개발자들은 도구가 발전함에 따라 그 투자가 복리로 돌아올 것입니다.


빠른 참조 카드

설치 (최초 1회 설정)

# XcodeBuildMCP (59 tools)
claude mcp add XcodeBuildMCP -s user \
  -e XCODEBUILDMCP_SENTRY_DISABLED=true \
  -- npx -y xcodebuildmcp@latest mcp

# Apple Xcode MCP (20 tools)
claude mcp add --transport stdio xcode -s user -- xcrun mcpbridge

# Codex MCP setup
codex mcp add xcode -- xcrun mcpbridge

# Verify
claude mcp list

필수 CLAUDE.md 섹션

1. Project identity (bundle ID, target OS, architecture)
2. File structure with annotations
3. Build and test commands
4. Key patterns and rules
5. Prohibitions (NEVER touch .pbxproj)
6. Framework-specific context

필수 훅(Hook)

{
  "PreToolUse": [{ "matcher": "Edit|Write", "command": "block .pbxproj" }],
  "PostToolUse": [{ "matcher": "Edit|Write", "command": "swiftformat" }]
}

아키텍처 규칙

@Observable         (not ObservableObject)
NavigationStack     (not NavigationView)
@State              (not @StateObject)
SwiftData @Model    (not Core Data)
async/await         (not completion handlers)
@MainActor          (on all Observable classes)
.glassEffect()      (Liquid Glass, iOS 26+)

MCP 도구 우선순위

Build:     build_sim          (not xcodebuild via Bash)
Test:      test_sim           (not xcodebuild test via Bash)
Sim:       list_sims/boot_sim (not xcrun simctl via Bash)
Docs:      DocumentationSearch (not WebSearch)
REPL:      ExecuteSnippet     (not swift via Bash)

변경 이력

날짜 변경 사항
2026-04-13 최초 발행. 앱 8개, 런타임 3종, MCP 설정, CLAUDE.md 패턴, 훅, 사례 연구 포함.

참고 문헌


  1. XcodeBuildMCP는 기본적으로 Sentry 텔레메트리를 포함합니다. 프로젝트의 개인정보 문서에서 전송되는 데이터를 상세히 설명합니다: 오류 메시지, 스택 트레이스, 경우에 따라 파일 경로가 포함됩니다. XCODEBUILDMCP_SENTRY_DISABLED=true 환경 변수로 완전히 비활성화할 수 있습니다. 

  2. Anthropic, “Model Context Protocol Specification,” modelcontextprotocol.io/specification. MCP 사양은 XcodeBuildMCP와 Apple의 Xcode MCP가 구현하는 JSON-RPC 전송, 도구 탐색, 리소스 프로토콜을 정의합니다. 

  3. XcodeBuildMCP, github.com/getsentry/XcodeBuildMCP. 오픈 소스, Sentry에서 유지보수. 시뮬레이터, 실기기, 디버깅, UI 자동화 워크플로를 아우르는 59개 도구 제공. 시맨틱 버전 관리 및 변경 로그 포함. 

  4. Apple은 Xcode 26.3 지능형 개발 도구 이니셔티브의 일환으로 Xcode MCP 서버를 도입하여, MCP를 AI 코딩 어시스턴트와 Xcode 툴체인 사이의 인터페이스 레이어로 자리매김했습니다. 공식 문서는 Xcode Release Notes를 참고하세요. 

  5. Rudrank Riyam, “Exploring Xcode Using MCP Tools,” rudrank.com/exploring-xcode-using-mcp-tools-cursor-external-clients, 2026. Apple MCP 도구 수, XPC 의존성, 문서 검색 기능에 대한 독립적 검증. 

  6. Jimenez, C.E., Yang, J., Wettig, A., et al., “SWE-bench: Can Language Models Resolve Real-World GitHub Issues?” ICLR 2024. arxiv.org/abs/2310.06770. 구조화된 도구 접근 방식을 사용하는 에이전트가 비구조화된 셸 명령만 사용하는 에이전트보다 현저히 뛰어난 성능을 보였습니다. 이 발견은 에이전트 효과성을 위한 구조화된 MCP 인터페이스의 유효성을 입증합니다. 

  7. Claude Code CLI 문서, code.claude.com. 훅 시스템, MCP 구성, 서브에이전트 위임, 에이전트 정의. 

  8. SwiftFormat, github.com/nicklockwood/SwiftFormat. 일관된 코드 스타일을 위해 PostToolUse 훅에서 사용되는 Swift 포맷팅 도구. 

  9. XcodeBuildMCP 공식 사이트, xcodebuildmcp.com. CLI 출력 예시를 통해 59개 MCP 도구를 확인할 수 있습니다. 도구 카테고리: 시뮬레이터, 실기기, 디버깅, UI 자동화. Homebrew 또는 npx로 설치 가능. 

  10. Swiftjective-C, “Agentic Coding in Xcode 26.3 with Claude Code and Codex,” swiftjectivec.com, 2026년 2월. Xcode 26.3이 설정 > Intelligence를 통해 네이티브 Claude Agent 및 Codex 런타임을 지원함을 확인. xcrun mcpbridge를 통해 20개 MCP 도구 노출. 

  11. Blake Crosley, “Two MCP Servers Made Claude Code an iOS Build System,” blakecrosley.com/blog/xcode-mcp-claude-code, 2026년 2월. 동일 저자의 iOS 개발 워크플로에서 얻은 설정 가이드 및 실제 결과. 

NORMAL ios-agent-development.md EOF