CodeRabbit iOS & SwiftUI AI 코드 리뷰 설정 가이드
CodeRabbit을 그대로 켜 두기만 해도 코드 리뷰 자체는 잘 굴러갑니다. 다만 iOS 프로젝트는 순수 Swift 코드와는 다른 문법과 런타임 모델(SwiftUI 선언형 뷰, Combine/Observation, Swift Concurrency, Swift Package Manager, Info.plist·entitlements 등)을 동시에 다루기 때문에, iOS & SwiftUI 환경에 맞춘 컨텍스트를 .coderabbit.yaml에 알려주면 리뷰의 깊이가 눈에 띄게 달라집니다.
이 글에서는 iOS & SwiftUI 프로젝트에 적용 가능한 .coderabbit.yaml을 단계별로 작성해 봅니다. 같은 결을 가진 Android 버전을 함께 보고 싶다면 CodeRabbit Android & Jetpack Compose 설정 가이드를, .coderabbit.yaml 옵션 자체가 처음이시라면 코드래빗 설정 완벽 가이드를 함께 보시면 좋습니다.
왜 iOS 전용 설정이 필요한가요?
기본 설정은 "Swift 코드" 만을 가정합니다. 그래서 다음 같은 iOS 특유의 안티 패턴이 잘 잡히지 않습니다.
- SwiftUI
View가 거대해지고 네트워크 호출·비즈니스 로직을body안에 직접 들고 있는 경우 @StateObject로 소유해야 할 객체를@ObservedObject로 받아 화면 갱신마다 상태가 초기화되거나, 반대로 주입받아야 할 객체를@StateObject로 새로 만드는 경우- 클로저나
Task안에서[weak self]를 빠뜨려 강한 순환 참조(retain cycle)로 메모리가 누수되는 경우 @MainActor격리(isolation) 누락으로 백그라운드 스레드에서 UI 상태를 갱신해 런타임 경고나 크래시가 나는 경우- 강제 언래핑(
!),try!, 강제 캐스팅(as!)을 남용해 프로덕션에서 크래시 위험을 키우는 경우 - Combine 구독을
store(in: &cancellables)하지 않아 구독이 즉시 해제되어 이벤트가 흐르지 않는 경우 Info.plist에 개인정보 권한(NSCameraUsageDescription등)이 추가됐는데 사유 문자열이 부실하거나, 런타임 권한 요청 코드가 PR에 함께 보이지 않는 경우- App Transport Security 예외(
NSAllowsArbitraryLoads)를 도메인 구분 없이 전역으로 열어 두는 경우
이런 항목들을 path_instructions로 명시하면, AI 코드 리뷰가 사람 시니어 iOS 개발자가 PR을 본 듯한 코멘트를 달아 줍니다.
기본 골격 만들기
루트 디렉터리에 .coderabbit.yaml을 만들고, 다음과 같은 헤더로 시작합니다.
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: ko-KR
early_access: false
reviews:
profile: chill # 처음에는 chill, 익숙해지면 assertive
high_level_summary: true
collapse_walkthrough: true
poem: falselanguage: ko-KR: 리뷰 코멘트를 한국어로 받습니다. CodeRabbit이 PR description, walkthrough, 인라인 코멘트 모두를 한국어로 생성합니다.profile: chill: 처음 도입하는 팀에 권장합니다. 사소한 스타일 nitpick까지 받고 싶다면assertive로.
path_filters: 어떤 파일을 보고 어떤 파일을 무시할까
iOS 프로젝트는 빌드 산출물(.build, DerivedData), 의존성 디렉터리(Pods, Carthage), 코드 생성 산출물(Sourcery, R.swift, protobuf), 그리고 거대한 project.pbxproj 등이 PR에 섞여 들어오곤 합니다. 이런 파일까지 리뷰 대상에 들어가면 정작 봐야 할 변경에 코멘트가 묻혀 버리니, 처음부터 제외해 두는 편이 깔끔합니다.
reviews:
path_filters:
# Swift / SwiftUI 소스
- "**/*.swift"
# 패키지·빌드 설정
- "**/Package.swift"
- "**/*.xcconfig"
# Tuist / XcodeGen 매니페스트 (사용하는 경우)
- "**/Project.swift"
- "**/project.yml"
# 권한·보안 표면
- "**/Info.plist"
- "**/*.entitlements"
# 빌드 산출물
- "!**/.build/**"
- "!**/DerivedData/**"
- "!**/build/**"
# 의존성 디렉터리
- "!**/Pods/**"
- "!**/Carthage/**"
# 코드 생성 산출물
- "!**/*.generated.swift"
- "!**/*.pb.swift"
- "!**/R.generated.swift"
# Xcode 프로젝트 메타(머지 충돌 잡음 방지)
- "!**/*.xcodeproj/**"
- "!**/*.xcworkspace/**"
# 의존성 잠금 파일
- "!**/Package.resolved"!**/*.generated.swift 같은 패턴 덕분에, Sourcery나 R.swift가 만들어 둔 코드를 두고 "이 프로퍼티는 사용되지 않습니다" 같은 잡음 코멘트가 달리지 않습니다.
auto_review: 자동 리뷰 동작
reviews:
auto_review:
enabled: true
auto_incremental_review: true
drafts: false
base_branches:
- main
- develop
ignore_usernames:
- dependabot
- "dependabot[bot]"
- renovate
- "renovate[bot]"auto_incremental_review: true: 새 커밋이 푸시될 때마다 변경분에 대해 추가 리뷰를 받을 수 있습니다.drafts: false: 작성 중인 Draft PR에는 리뷰를 달지 않아 알림 피로를 줄입니다.ignore_usernames: dependabot/renovate가 만든 PR은 리뷰를 건너뜁니다. 이들은 보통Package.swift/Package.resolved의 의존성 버전만 올리며, CI가 이미 검증하므로 중복 리뷰가 불필요합니다.
path_instructions: iOS & SwiftUI 전용 컨텍스트 주입
여기가 이 가이드의 핵심입니다. 같은 .swift 확장자라도 위치/역할에 따라 리뷰 기준을 다르게 줍니다.
Swift 일반 규칙
모든 .swift 파일에 공통으로 적용되는 베이스라인입니다. SwiftUI View, ViewModel, DI 등 더 좁은 path 규칙이 뒤에 붙어도, 이 베이스라인은 모든 Swift 파일에 동시에 적용됩니다. 옵셔널 안전성, Swift Concurrency, 시크릿 하드코딩 같은 "안 지키면 사고로 이어지는" 항목 위주로 채웠습니다.
reviews:
path_instructions:
- path: "**/*.swift"
instructions: |
Swift best practices to check:
Language correctness:
- Optionals: 강제 언래핑(!) 금지, guard let/if let/nil coalescing(??)로 안전하게 처리
- Value semantics: 모델·DTO는 struct/enum 우선, 참조 의미가 꼭 필요할 때만 class
- Immutability: var보다 let 우선, 변경되지 않는 프로퍼티는 let으로
- Enum: 제한된 상태/경우의 수는 associated value를 가진 enum으로 표현
Swift Concurrency:
- async/await 우선, 콜백 지옥(completion handler 중첩) 회피
- Task의 취소 처리: 긴 작업은 Task.isCancelled/try Task.checkCancellation 확인
- @MainActor: UI/상태 갱신은 메인 액터에서, 백그라운드 컨텍스트에서 직접 갱신 금지
- actor/Sendable로 데이터 경합(data race) 방지, 가변 공유 상태 보호
- DispatchQueue 수동 사용보다 structured concurrency가 적합한지 검토
Error handling:
- try! 금지, 실패 가능한 작업은 throws 또는 Result로 표현
- 에러 삼키기(빈 catch) 금지, 의미 있는 복구/전파
Memory:
- 클로저/Task 캡처에서 강한 순환 참조 방지([weak self]/[unowned self] 적절히)
- delegate 프로퍼티는 weak로 선언
Security:
- 하드코딩된 secret/API key/credential 금지
- 민감 데이터는 UserDefaults가 아니라 Keychain에 저장
- 난수는 SecRandomCopyBytes 등 암호학적으로 안전한 소스 사용
- 안전하지 않은 역직렬화/평문 전송 금지SwiftUI 전용 규칙
SwiftUI는 일반 함수와 다른 선언형 UI 모델 위에서 동작하므로, 별도 path로 분리해 더 깊은 컨텍스트를 줍니다.
path glob을 정할 때 한 가지 중요한 점이 있습니다. Ice Cubes처럼 기능을 로컬 Swift Package로 나눈 모던 모듈식 프로젝트는 화면 단위 뷰를 Packages/<Feature>/Sources/<Feature>/...Timeline View.swift처럼 feature 패키지 안에 두고, 재사용 컴포넌트만 디자인 시스템 패키지에 모읍니다. 즉, **/Views/** 같은 단일 디렉터리 패턴으로는 화면 단위 뷰가 대부분 빠집니다. 따라서 파일명 컨벤션(*View.swift)을 1차 신호로 삼고, 디자인 시스템·단일 모듈 프로젝트용 패턴을 보조로 같이 거는 형태가 가장 안전합니다. 아래처럼 같은 instructions를 path별로 각각 걸어 주시면 됩니다(path_instructions 스키마는 path당 한 개의 instructions를 받습니다).
# 1) 화면/뷰 단위 - 파일명 컨벤션, 모듈 깊이와 무관
- path: "**/*View.swift"
instructions: |
SwiftUI 뷰 best practices:
- state hoisting: 비즈니스 상태는 ViewModel/상위로 올리고, body 안에서 네트워크·로직 직접 수행 금지
- 프로퍼티 래퍼 올바른 사용: 소유는 @StateObject/@State, 주입은 @ObservedObject/@Binding,
전역 공유는 @EnvironmentObject (iOS 17+면 @Observable + @Bindable/@Environment)
- 비동기 작업은 onAppear + Task 대신 .task 수정자로 뷰 생명주기에 묶어 자동 취소
- ForEach는 안정적인 id를 갖는 Identifiable로(인덱스 id 회피)
- 색상/문자열 하드코딩 회피: 에셋 카탈로그 색상, LocalizedStringKey/String(localized:)
- 접근성: 의미 있는 accessibilityLabel, Dynamic Type 대응, 최소 44pt 터치 영역
# 2) 재사용 UI / 디자인 시스템 패키지
- path: "**/{DesignSystem,Components,UIComponents}/**/*.swift"
instructions: |
디자인 시스템·재사용 컴포넌트 best practices:
- public View/이니셜라이저에는 문서 주석과 명확한 매개변수 기본값
- 큰 body는 computed property나 하위 뷰로 분해(재구성 비용·가독성)
- 색상/간격/타이포는 디자인 토큰으로, 하드코딩(Color(hex:), 임의 padding 수치) 회피
- @ViewBuilder/제네릭으로 조합 가능하게 설계, 외부에서 Modifier 주입 허용
- #Preview에 라이트/다크 모드, Dynamic Type 크기 변형 제공
# 3) 보조 패턴 - 단일 모듈 프로젝트의 Views/Screens 디렉터리
- path: "**/{Views,Screens,Scenes}/**/*.swift"
instructions: |
SwiftUI best practices:
뷰 구조:
- 거대한 body는 하위 뷰/computed property로 분해
- body 안에서 비싼 연산·네트워크 호출 금지(재구성마다 실행됨)
- 뷰는 가능한 한 상태를 직접 소유하지 않고 stateless하게, 상태는 위로 hoisting
상태 관리(프로퍼티 래퍼):
- @State: 뷰 로컬의 값 타입 상태에만
- @StateObject: 뷰가 "소유"하는 ObservableObject (생성 1회)
- @ObservedObject: 외부에서 "주입"받는 ObservableObject (직접 생성 금지)
- @EnvironmentObject/@Environment: 트리 전역 공유 상태
- iOS 17+ Observation: @Observable 모델 + @Bindable, @Environment(Type.self)
부수 효과(side effects):
- .task { } 는 뷰 생명주기에 묶여 사라질 때 자동 취소(권장)
- .onAppear에서 Task를 띄울 경우 취소·중복 실행을 직접 관리
- .onChange/.task(id:)의 트리거 값이 의도와 일치하는지 확인
리스트/성능:
- ForEach는 Identifiable의 안정적 id 사용(offset/index id 회피)
- LazyVStack/List로 대량 데이터 지연 로딩, 무거운 뷰는 등가 뷰로 분리
- Equatable 뷰/.equatable()로 불필요한 재구성 줄이기 검토
리소스/국제화:
- 사용자 노출 문자열은 String(localized:)/LocalizedStringKey
- 색상은 에셋 카탈로그 또는 디자인 토큰, 하드코딩 RGB 회피
- 이미지/심볼은 SF Symbols 또는 카탈로그 참조
Preview:
- #Preview 매크로 사용, 라이트/다크/Dynamic Type 변형 제공
- Preview에서 실제 네트워크 ViewModel 대신 더미 상태/목 데이터 주입
접근성:
- 의미 있는 accessibilityLabel/Value/Hint, 장식 요소는 .accessibilityHidden(true)
- 충분한 터치 영역(최소 44pt), Dynamic Type에서 레이아웃이 깨지지 않는지ViewModel / 상태 모델 규칙
ViewModel(또는 @Observable 상태 모델)은 iOS에서 가장 메모리 누수와 스레드/생명주기 버그가 자주 발생하는 지점입니다. [weak self]를 빠뜨리거나, 메인 액터 격리를 누락하거나, Combine 구독을 보관하지 않으면 화면이 사라진 뒤에도 작업이 살아남아 크래시, 중복 네트워크 호출로 이어집니다. 파일명 컨벤션(*ViewModel.swift, *Store.swift)을 활용해 별도로 더 엄격히 검사합니다.
상태 관리는 enum 기반 UI 상태(Loading/Loaded/Empty/Error) + @MainActor 격리 + 생성자 주입 의존성 + private(set) 캡슐화를 기준선으로 두면 리뷰 기준이 명확해집니다. 아래 instructions에 이 패턴을 그대로 옮겼습니다.
- path: "**/*{ViewModel,Store,Model}.swift"
instructions: |
ViewModel / 상태 모델 best practices:
격리와 스레드:
- ViewModel은 @MainActor로 격리(UI 상태가 메인에서 갱신되도록)
- 무거운 작업은 액터/백그라운드로 보내되, 상태 반영은 메인 액터에서
- 가변 공유 상태는 actor 또는 동기화로 데이터 경합 방지
관찰 가능 상태(Observation/Combine):
- iOS 17+: @Observable 매크로 + private(set)로 쓰기 캡슐화
- 그 이전: ObservableObject + @Published, 외부에는 읽기 전용으로 노출
- Combine 구독은 반드시 store(in: &cancellables)로 보관(즉시 해제 방지)
- 가능하면 Combine 대신 async/await·AsyncSequence로 단순화 검토
UI 상태 모델링(enum):
- 화면 상태는 enum으로 명시(예: enum State { case loading; case loaded([Item]); case empty; case failed(Error) })
- 중간 상태(Loading/Empty/Error)를 누락하지 않기. 빈 결과와 미초기화 상태 구분
- 단일 source of truth로 상태 프로퍼티 하나를 외부에 노출
의존성과 테스트:
- 의존성은 protocol로 추상화하고 init으로 주입(싱글턴 직접 참조 금지)
- 네트워크/시계/스케줄러를 주입해 테스트 결정성 확보
- URLSession.shared 등 전역 의존성 하드코딩 회피
생명주기와 누수:
- 클로저/Task 캡처에 [weak self] 적용, 강한 순환 참조 방지
- View/UIViewController 참조를 ViewModel이 보관 금지
- 진행 중인 Task는 화면 종료 시 취소(deinit 또는 .task 자동 취소 활용)
일회성 이벤트:
- 토스트·네비게이션 같은 단발 이벤트는 지속 상태로 두지 말 것
- PassthroughSubject/AsyncStream 또는 소비 후 비우는 옵셔널 상태로 모델링의존성 주입(DI)
DI 코드는 잘못 짜면 앱 전체에 잘못된 인스턴스가 흘러 들어가는 영향이 큰 영역입니다. 숨은 싱글턴/전역 상태, 프로토콜 추상화 누락, 스코프(수명) 오설정처럼 "동작은 하지만 점진적으로 문제를 만들어 내는" 항목을 자동으로 잡도록 합니다.
DI는 디렉터리(DI/, Dependencies/)뿐 아니라 확장 파일(예: *+Dependency.swift)이나 등록 코드에 흩어져 있는 경우가 많습니다. 그래서 디렉터리 패턴과 파일명 패턴을 함께 걸어 누락을 줄입니다. swift-dependencies, Factory, Swinject 등 어떤 라이브러리를 쓰든 핵심 원칙은 같습니다.
# 1) DI 디렉터리 안의 모든 코드
- path: "**/{DI,Dependencies,Injection}/**/*.swift"
instructions: |
Dependency Injection best practices:
- 구체 타입 대신 protocol로 의존성을 추상화(테스트에서 fake 교체 가능)
- 생성자 주입(init injection) 우선, 숨은 싱글턴/서비스 로케이터 회피
- 스코프(수명) 명시: 앱 전역 singleton vs 화면 단위 인스턴스 구분
- swift-dependencies 사용 시 DependencyKey의 liveValue/testValue/previewValue 제공
- Factory/Swinject 사용 시 등록 스코프(.singleton/.shared 등)가 의도와 일치하는지
- 순환 의존성이 생기지 않는지 확인
# 2) /DI 디렉터리 밖에 흩어진 등록·확장도 잡기 위한 파일명 패턴
- path: "**/*+Dependency.swift"
instructions: |
의존성 등록 확장 검증:
- 새 의존성에 test/preview 구현이 함께 제공되는지
- 전역 가변 상태로 인한 테스트 간 오염이 없는지Swift Package Manager
Package.swift는 단순한 패키지 설정처럼 보이지만 빌드 재현성, 보안, 의존성 관리가 모두 모이는 곳입니다. 브랜치/리비전 고정 의존성, 너무 넓은 버전 범위, 구현 의존성의 public 노출 같은 항목은 사람이 매 PR마다 일일이 짚기 어렵습니다.
- path: "**/Package.swift"
instructions: |
Swift Package Manager (Package.swift) review:
- 의존성 버전 고정: .upToNextMajor/.upToNextMinor 등 명시적 범위 사용
(릴리스 대상에서 .branch("main")/.revision(...) 의존성은 지양)
- platforms 최소 버전이 프로젝트 지원 정책과 일치하는지
- 테스트 전용 의존성은 test target에만 연결(앱 타깃 오염 방지)
- target dependencies를 최소화하고, 구현 의존성을 public product로 새어 나가지 않게
- resources는 .process/.copy로 명시적으로 선언
- 내부(internal) 모듈과 공개(public) product의 경계가 분명한지Info.plist / entitlements
Info.plist와 *.entitlements는 권한, 노출 표면, 보안 정책이 한 자리에 모이는 파일이라, 잘못 추가된 한 줄이 그대로 보안 사고나 심사 반려로 이어질 수 있습니다. 새 개인정보 권한 키가 추가됐는데 런타임 요청 코드가 함께 들어왔는지, ATS 예외가 전역으로 열렸는지, URL scheme·연관 도메인이 너무 넓지 않은지 같은 "리뷰어가 매번 일일이 머리에 떠올려야 하는 체크리스트" 를 자동화합니다.
- path: "**/Info.plist"
instructions: |
Info.plist review:
- 새로 추가된 개인정보 권한 키(NSCameraUsageDescription, NSPhotoLibraryUsageDescription,
NSLocationWhenInUseUsageDescription, NSMicrophoneUsageDescription, NSContactsUsageDescription 등)에
구체적인 사유 문자열과, 해당 권한을 요청하는 런타임 코드가 PR에 함께 있는지 확인
- App Transport Security: NSAllowsArbitraryLoads 전역 허용 금지, 필요한 도메인만 예외 처리
- URL scheme(CFBundleURLTypes)·연관 도메인이 너무 광범위하지 않은지
- 백그라운드 모드(UIBackgroundModes)가 실제 기능과 일치하고 과도하지 않은지
- 디버그/테스트용 설정이 릴리스 빌드에 섞여 들어가지 않았는지
- path: "**/*.entitlements"
instructions: |
Entitlements review:
- keychain-access-groups, App Groups, associated domains의 식별자가 정확하고 최소 범위인지
- 푸시(aps-environment)가 빌드 구성(development/production)과 일치하는지
- get-task-allow(디버그 권한) 같은 항목이 릴리스 entitlements에 남아 있지 않은지
- 새 권한 추가 시 실제 사용 코드와 심사 정당성이 동반되는지테스트 코드
테스트 코드는 "통과만 하면 OK" 로 넘어가기 쉽지만, iOS에서는 flaky 테스트와 비동기 타이밍 문제가 CI 비용을 가장 많이 차지하는 부분입니다. 테스트 이름 패턴, given-when-then, 비동기는 expectation/await, 시계·스케줄러 주입, SwiftUI는 스냅샷/ViewInspector, UI 테스트는 접근성 식별자로 규약을 명시해 두면 신규 입사자도 동일한 패턴으로 테스트를 작성하게 됩니다.
- path: "**/{Tests,UITests}/**/*.swift"
instructions: |
iOS 테스트 best practices:
공통:
- 테스트 이름은 시나리오와 기대 결과가 드러나도록(예: test_login_emptyEmail_showsError
또는 Swift Testing의 @Test("빈 이메일이면 에러를 표시한다"))
- given-when-then(Arrange-Act-Assert) 구조
- 테스트는 한 가지 논리적 개념만 검증, 공유 가변 상태로 인한 테스트 간 의존성 금지
비동기/동시성:
- async 테스트는 await로 결과 검증, sleep 기반 대기 금지
- 시간 의존 로직은 Clock/스케줄러를 주입해 결정론적으로(테스트용 시계 사용)
- Combine 검증은 test scheduler 또는 expectation으로
의존성:
- 네트워크/디스크 등 외부 의존성은 protocol 기반 fake로 교체(단위 테스트에서 실제 통신 금지)
- URLProtocol stub 등으로 네트워크 경계 격리
SwiftUI/스냅샷:
- 뷰 로직 검증은 ViewInspector, 시각 회귀는 swift-snapshot-testing 활용
- 라이트/다크/Dynamic Type 변형을 스냅샷으로 함께 커버
UI 테스트(XCUITest):
- accessibilityIdentifier로 요소를 찾고(텍스트 매칭 의존 회피)
- 고정 sleep 대신 expectation/waitForExistence로 안정적으로 대기tools: 정적 분석 통합
CodeRabbit은 자체 AI 리뷰 외에도 외부 정적 분석기를 PR에서 같이 돌리는 기능을 제공합니다. iOS 프로젝트라면 다음 조합을 권장합니다.
reviews:
tools:
# Swift 정적 분석
swiftlint:
enabled: true
# 프로젝트 루트에 .swiftlint.yml이 있다면 명시
# config_file: ".swiftlint.yml"
# 시크릿 스캐닝
gitleaks:
enabled: true- SwiftLint: Swift 전용 린터. 코드 스타일, 복잡도, 잠재 버그를 잡습니다. 팀에서 SwiftLint baseline을 운영 중이라면
config_file로 그대로 연결하시면 됩니다. - gitleaks: API 키, OAuth 토큰, 인증서 같은 시크릿이 커밋에 섞였는지 확인합니다. iOS 프로젝트는
*.xcconfig나GoogleService-Info.plist,local.xcconfig에 자칫 키를 두기 쉬워, 머지 전 단계에서 잡는 것이 중요합니다. 보안 관점에서 더 깊은 맥락은 Vercel 침해 사고가 엔터프라이즈 코드 보안에 던지는 세 가지 교훈에서 이어 보실 수 있습니다.
참고: 포매터, SwiftFormat
많은 팀이 SwiftFormat으로 포매팅을 일괄 적용합니다. 팀이 이미 SwiftFormat이나 SwiftLint의 autocorrect를 CI에서 돌리고 있다면, CodeRabbit은 코드 스타일이 아닌 로직·아키텍처 차원에 집중하게 두고, 포매팅은 CI에 맡기는 분담이 효과적입니다.
이외에 OSV-Scanner는 Package.resolved를 읽어 Swift Package 의존성에 대한 알려진 CVE를 함께 점검할 수 있습니다.
모듈식 프로젝트(로컬 Swift Package) 추가 팁
Ice Cubes처럼 기능을 로컬 Swift Package로 분리하는 패턴은 점점 표준이 되고 있습니다. 디자인 시스템·네트워크·기능 모듈을 각각의 패키지로 떼어 내면 빌드 시간과 의존성 경계가 명확해지는 대신, 패키지 매니페스트와 모듈 경계를 잘못 잡으면 모든 모듈에 영향이 가므로 별도 path로 더 엄격히 검사하면 좋습니다.
# 기능 모듈 패키지(로컬 SPM) - 공개 API 경계 점검
- path: "**/Packages/**/Sources/**/*.swift"
instructions: |
모듈식(로컬 Swift Package) best practices:
- public으로 노출하는 타입을 최소화하고, 구현 세부는 internal로 숨기기
- 모듈 간 의존성은 단방향(기능 → 코어/디자인시스템)으로, 순환 의존성 금지
- struct/enum 등 안정적 값 타입 위주로 모듈 경계 API 설계
- 기능 모듈이 다른 기능 모듈을 직접 import하지 않도록(코어/공유 모듈을 경유)
# Tuist / XcodeGen 매니페스트
- path: "**/{Project.swift,project.yml}"
instructions: |
프로젝트 생성 매니페스트 best practices:
- 타깃 의존성 그래프가 단방향이고 순환이 없는지
- 빌드 설정/시그니처가 .xcconfig 또는 공유 설정으로 일원화되어 있는지
- 새 모듈 추가 시 스킴/타깃 등록 누락이 없는지The Composable Architecture(TCA) 기반의 모듈식 구조가 궁금하시다면 isowords가 좋은 실제 사례입니다. 기능별 패키지, 디자인 시스템 패키지, :Tests 분리 같은 구조라면, 각각에 맞는 path 패턴을 위와 같이 한두 개 더 얹는 방식으로 확장하실 수 있습니다.
전체 예시: iOS 팀이 그대로 가져다 쓸 수 있는 yaml
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: ko-KR
early_access: false
reviews:
profile: chill
high_level_summary: true
collapse_walkthrough: true
poem: false
path_filters:
- "**/*.swift"
- "**/Package.swift"
- "**/*.xcconfig"
- "**/Project.swift"
- "**/project.yml"
- "**/Info.plist"
- "**/*.entitlements"
- "!**/.build/**"
- "!**/DerivedData/**"
- "!**/Pods/**"
- "!**/Carthage/**"
- "!**/*.generated.swift"
- "!**/*.pb.swift"
- "!**/*.xcodeproj/**"
- "!**/*.xcworkspace/**"
- "!**/Package.resolved"
auto_review:
enabled: true
auto_incremental_review: true
drafts: false
base_branches:
- main
- develop
ignore_usernames:
- dependabot
- "dependabot[bot]"
- renovate
- "renovate[bot]"
path_instructions:
- path: "**/*.swift"
instructions: |
Swift best practices: 강제 언래핑(!)/try!/as! 금지, struct/enum·let 우선,
async/await + Task 취소 처리, @MainActor로 UI 갱신, actor/Sendable로 데이터 경합 방지,
클로저 캡처 [weak self]로 순환 참조 방지, 하드코딩 시크릿 금지(민감 데이터는 Keychain).
# 화면/뷰 (파일명 기반 - 모듈식 프로젝트 대응)
- path: "**/*View.swift"
instructions: |
SwiftUI 뷰: state hoisting, 프로퍼티 래퍼 정확히(@StateObject 소유/@ObservedObject 주입,
iOS 17+ @Observable), .task로 생명주기 묶기, Identifiable 안정 id,
LocalizedStringKey·에셋 색상 사용, accessibilityLabel/Dynamic Type/44pt 접근성.
# 디자인 시스템·재사용 UI
- path: "**/{DesignSystem,Components,UIComponents}/**/*.swift"
instructions: |
디자인 시스템: public View 문서화, body 분해로 재구성 최적화,
색상/간격/타이포는 디자인 토큰(하드코딩 회피), #Preview 라이트/다크/Dynamic Type 변형.
# 단일 모듈 프로젝트 보조 패턴
- path: "**/{Views,Screens,Scenes}/**/*.swift"
instructions: |
SwiftUI: body 안 비싼 연산·네트워크 금지, 하위 뷰 분해, 프로퍼티 래퍼 정확히,
.task 자동 취소, ForEach 안정 id, String(localized:)·에셋 색상, #Preview에 목 데이터,
accessibilityLabel/Dynamic Type/44pt 점검.
- path: "**/*{ViewModel,Store,Model}.swift"
instructions: |
상태 모델: @MainActor 격리, @Observable/@Published는 private(set)로 캡슐화,
Combine 구독은 store(in: &cancellables), enum 기반 UI 상태(Loading/Loaded/Empty/Error),
의존성 protocol 생성자 주입, 클로저 [weak self], 화면 종료 시 Task 취소.
- path: "**/{DI,Dependencies,Injection}/**/*.swift"
instructions: |
DI: protocol 추상화 + 생성자 주입, 숨은 싱글턴/서비스 로케이터 회피,
스코프(수명) 명시, swift-dependencies는 live/test/preview 제공, 순환 의존성 금지.
- path: "**/*+Dependency.swift"
instructions: |
의존성 등록 확장: test/preview 구현 동반, 전역 가변 상태로 인한 테스트 오염 점검.
- path: "**/Package.swift"
instructions: |
SPM: 명시적 버전 범위(branch/revision 의존 지양), platforms 최소 버전 일치,
test 의존성은 test target에만, 구현 의존성 public 노출 금지, resources 명시.
- path: "**/Info.plist"
instructions: |
Info.plist: 개인정보 권한 키에 사유 문자열 + 런타임 요청 코드 동반,
ATS 전역 허용(NSAllowsArbitraryLoads) 금지, URL scheme·백그라운드 모드 과도 여부 점검.
- path: "**/*.entitlements"
instructions: |
Entitlements: keychain/App Groups/associated domains 최소 범위,
aps-environment 빌드 구성 일치, 릴리스에 get-task-allow 잔존 금지.
- path: "**/{Tests,UITests}/**/*.swift"
instructions: |
iOS 테스트: 시나리오/기대가 드러나는 이름, given-when-then, async는 await(sleep 금지),
시계/스케줄러 주입, 의존성 protocol fake, SwiftUI는 ViewInspector/스냅샷,
XCUITest는 accessibilityIdentifier + waitForExistence.
# 모듈식(로컬 SPM) 패키지
- path: "**/Packages/**/Sources/**/*.swift"
instructions: |
모듈 경계: public 최소화·internal 은닉, 단방향 의존성(순환 금지),
기능 모듈 간 직접 import 금지(코어/공유 경유).
tools:
swiftlint:
enabled: true
gitleaks:
enabled: true
chat:
auto_reply: true도입 후 점검 포인트
설정이 적용된 PR을 한두 개 받아 본 뒤, 다음을 가볍게 점검해 보시는 것을 추천 드립니다.
- 자주 쓰는 디렉터리/패키지 경로(
Features/,Packages/,Sources/등)가 path_instructions의 glob 패턴에 잘 들어오는가? 구조가 다르다면path부분만 팀 컨벤션에 맞게 바꿔 주세요. - SwiftLint가 이미 잡는 항목과 CodeRabbit 코멘트가 중복되는 비율이 너무 높지 않은가? 중복이 심하면
instructions에서 "SwiftLint가 잡는 스타일 nitpick은 제외" 같은 한 줄을 추가해 둘 수 있습니다. - SwiftUI 관련 코멘트의 정확도가 낮다면 패키지 컨벤션과 glob을 다시 맞추거나, 팀 내부 컴포넌트 컨벤션(예: 자체 디자인 토큰, 래퍼 뷰)을 instructions에 추가하면 효과가 큽니다.
조직 단위로 동일한 정책을 강제하고 싶다면, 조직에 coderabbit 레포지터리를 만들고 같은 yaml을 Central Configuration으로 올려두면 모든 iOS 레포가 같은 기준으로 리뷰됩니다. 더 큰 그림에서 AI 코드 리뷰 도구를 평가하고 비교하는 관점은 AI 코드 리뷰 도구 완벽 가이드 2026에서, CodeRabbit이 처음이시라면 CodeRabbit으로 깃헙 PR 코드 리뷰 자동화 하기를 함께 보시기를 추천 드립니다.
이 설정을 들고 PR을 한 번 올려 보시고, 실제로 받은 리뷰를 팀에 공유해 보세요. AI 코드 리뷰의 톤과 깊이가 "팀 시니어 iOS 개발자가 본 듯" 한 모습으로 바뀌는 게 가장 큰 변화일 겁니다.