Search

iOS P2P 연결 서비스 구현 (MultipeerConnectivity)

What I Learned

1. MultipeerConnectivity 프레임워크 이해

iOS에서 Wi-Fi, Bluetooth를 통한 근거리 P2P 통신을 구현할 때 MultipeerConnectivity 프레임워크를 사용한다.

핵심 컴포넌트

클래스
역할
MCPeerID
네트워크 상의 기기 식별자
MCSession
연결된 피어들과의 통신 세션
MCNearbyServiceAdvertiser
자신을 다른 기기에 광고
MCNearbyServiceBrowser
주변 기기 탐색

연결 흐름

sequenceDiagram
    participant A as 기기 A (Advertiser)
    participant B as 기기 B (Browser)
    
    B->>A: 탐색 (discovery)
    B->>A: 초대 (invitation)
    A->>B: 수락 (accept)
    A-->>B: 연결됨
    B-->>A: 연결됨
Mermaid
복사

2. iOS 17+ @Observable 매크로

기존 ObservableObject + @Published 대신 @Observable 매크로를 사용하면 보일러플레이트가 크게 줄어든다.
// Before (iOS 13+) class MyViewModel: ObservableObject { @Published var count: Int = 0 } // After (iOS 17+) @Observable class MyViewModel { var count: Int = 0 // 자동으로 observable }
Swift
복사
장점:
@Published 키워드 불필요
View에서 @ObservedObject 대신 일반 변수로 사용 가능
성능 향상 (필요한 프로퍼티만 추적)

3. Thread Safety in MultipeerConnectivity

MC 프레임워크의 delegate 콜백은 백그라운드 스레드에서 호출된다. UI 업데이트는 반드시 메인 스레드에서 수행해야 한다.
func session( _ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState ) { DispatchQueue.main.async { [weak self] in self?.handleStateChange(peerID, state: state) } }
Swift
복사

4. Discovery Info를 활용한 피어 정보 전달

MCNearbyServiceAdvertiser 생성 시 discoveryInfo를 통해 연결 전에 피어 정보를 전달할 수 있다.
let discoveryInfo: [String: String] = [ "number": "\(userNumber)", "stage": userStage.rawValue ] advertiser = MCNearbyServiceAdvertiser( peer: peerID, discoveryInfo: discoveryInfo, // 최대 400바이트 serviceType: serviceType )
Swift
복사
제한사항:
Key-Value 모두 String 타입만 가능
총 크기 400바이트 이하

Trouble Shooting

문제: 피어 연결 후 정보가 누락됨

원인: MCSessiondidChange 콜백이 먼저 호출되고, discoveryInfo 처리가 늦게 완료됨
해결: pendingPeerInfo 딕셔너리를 사용하여 연결 완료 전에 피어 정보를 저장
// Browser에서 피어 발견 시 savePendingInfo((number, peerStage), for: peerID) // Session 연결 완료 시 if let pending = getPendingInfo(for: peerID) { let peer = P2PPeer(id: peerID, number: pending.number, stage: pending.stage) addPeer(peer, for: peerID) }
Swift
복사

Project Structure (MV Pattern)

Nesty/ ├── Models/ │ ├── NestStage.swift # 성장 단계 enum │ ├── P2PConnectionState.swift # 연결 상태 │ ├── P2PMessageType.swift # 메시지 타입 │ ├── P2PPayload.swift # 전송 데이터 구조체 │ └── P2PPeer.swift # 피어 정보 └── Services/ └── P2PService.swift # P2P 연결 서비스 (Singleton)
Plain Text
복사

Key Takeaways

1.
응집도 우선: 서비스 클래스는 한 파일로 유지, 400~500줄도 단일 책임이면 OK
2.
Thread Safety: MC 콜백은 항상 메인 스레드로 디스패치
3.
@Observable: iOS 17+ 타겟이라면 적극 활용
4.
Extension 분리 주의: private 접근 제어자로 인해 분리가 어려울 수 있음

References