Skip to content

[Refactor/#315] 와이파이 연결 없이 연결 개선#316

Merged
GRJeon merged 8 commits intodevelopfrom
refactor/#315-OffAdvertiserBrowser
Mar 9, 2026
Merged

[Refactor/#315] 와이파이 연결 없이 연결 개선#316
GRJeon merged 8 commits intodevelopfrom
refactor/#315-OffAdvertiserBrowser

Conversation

@GRJeon
Copy link
Copy Markdown
Collaborator

@GRJeon GRJeon commented Mar 2, 2026

🔗 연관된 이슈

📝 작업 내용

📌 요약

  • command session, streaming session 통합
  • Invite 과정 진입 시 browser, advertiser 일시 정지
  • 위 수정을 반영하여 advertiser, browser에 대한 테스트 수정
  • Invite 타임아웃 10초 -> 20초

🔍 상세

  • 왜 바뀌어야 했는가?
    멀티피어는 블루투스와 P2P 와이파이를 사용할 수 있음에도 와이파이 공유기에 연결되지 않은 상태에서는 연결이 원활하지 않았습니다. 와이파이가 연결되지 않은 상태에서도 로컬 네트워크를 사용해 앱을 사용할 수 있도록 개선하고자 했습니다.

  • command session, streaming session 통합 과정

    • 최초에 분리했던 이유: 카메라로 촬영 중인 화면을 실시간으로 미러링하는 세션과, 앱 동작 흐름을 제어하는 세션을 분리하여 실시간 스트리밍 데이터의 전달 상태에 영향을 받지 않고 흐름 제어 명령을 전송하는 것을 기대하였습니다. 또한 두 세션의 암호화 수준을 다르게 설정할 수 있다는 장점이 존재합니다.
    • 세션 통합을 결정한 이유: 분리했던 의도와는 다르게, reliable, unreliable을 구분하는 것으로 어느정도 전송하는 데이터의 구분이 가능합니다. 반면 세션을 분리함으로써 Invite 과정의 시간이 2배가 되는 문제가 생깁니다.
    • 통합 방법 : 헤더
      Common/MultipeerHeader에서 확인할 수 있듯, 전송하는 데이터 앞에 0x00(커맨드) 또는 0x01(스트리밍)의 헤더를 붙여 데이터를 전송하는 방식으로 구분한 데이터를 하나의 세션을 통해 보내도록 수정했습니다.
  • Invite 과정 진입 시 browser, advertiser 일시 정지
    https://loud-peach-31c.notion.site/Wireshark-3104c58a09fa80bf826af45748474bc2?source=copy_link
    위 링크는 Wireshark를 통해 와이파이 없이 연결을 시도할 때 오가는 패킷 관측을 포함한 연구 내용입니다. 주요 포인트는 다음과 같습니다.

    • Invite 과정은 P2P 와이파이를 통해 이루어진다.
    • 실패한 연결에서는 잦은 Retransmission이 관측된다.
      위 연구에서 관측한 결과 중에서, 잦은 Retransmission이 관측되는 이유는 advertiser나 browser가 블루투스를 사용하고, Invite가 P2P 와이파이를 사용하며 네트워크 칩이라는 하드웨어를 나눠 사용, 네트워크 간섭 등이 발생하는 탓으로 예상했습니다.
      따라서 browser는 connecting 상태가 되는 순간, advertiser는 invite를 받는 순간 검색을 종료하고, 실패나 성공으로 완료되었을 때 다시 작동하도록 수정하였습니다.
  • Invite 타임아웃 10초->20초
    테스트 중 타임아웃을 늘렸을 때 실제 연결 시간이 20초까지 걸리지 않더라도 안정적으로 연결이 되는 모습을 관측하여 적용하였습니다.

💬 리뷰 노트

  • 테스트 환경은 맥북 시뮬레이터(와이파이 연결), 아이폰(와이파이 미연결)입니다. 코드 리뷰어님 혹시나 와이파이가 연결되지 않은 다른 환경에서 테스트가 가능하다면 부탁드립니다.

@GRJeon GRJeon self-assigned this Mar 2, 2026
@GRJeon GRJeon added ☑️ test 테스트 코드 추가 및 리팩토링 🔨 refactor 코드 리팩토링 (기능 변화 없이 구조 개선, 불필요한 import 제거) labels Mar 2, 2026
Copy link
Copy Markdown
Collaborator

@YunDaeHyeon YunDaeHyeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📄 PR Code Review Report

🔍 Summary

이번 Pull Request는 Multipeer Connectivity 세션 관리를 크게 개선하는 중요한 리팩터링을 포함하고 있습니다. 기존에 미러링 및 명령 전송을 위해 분리되었던 MCSession을 단일 세션으로 통합하고, MultipeerHeader를 사용하여 데이터 유형(스트리밍/명령)을 구분하는 방식으로 변경되었습니다. 이는 코드의 복잡성을 줄이고 리소스 효율성을 높이는 긍정적인 변화입니다. 전반적으로 변경 사항이 양측(브라우저 및 광고주)에서 일관성 있게 적용된 것으로 보이며, 테스트 코드도 새로운 프로토콜에 맞춰 업데이트되었습니다.

주요 변경사항은 잘 처리된 것으로 보이나, 몇 가지 세부적인 개선 사항이 식별되었습니다.

⚠️ Key Issues

  • 알 수 없는 멀티피어 헤더 처리 미흡: BrowserAdvertisersession(_:didReceive:fromPeer:) 메서드에서 수신된 데이터의 첫 번째 바이트(MultipeerHeader)가 command 또는 streaming이 아닌 경우 해당 데이터를 조용히 무시하고 있습니다. 이는 예기치 않은 데이터 형식이나 프로토콜 불일치가 발생했을 때 디버깅을 어렵게 만들 수 있습니다. 최소한 경고 로그를 남겨서 알 수 없는 데이터가 수신되었음을 명시적으로 알려주는 것이 좋습니다.

🛠 Improvement Suggestions

  • 명령 데이터 암호화 변경에 대한 문서화: 기존 commandSessionencryptionPreference: .none으로 설정되어 있었으나, 단일 세션으로 통합되면서 모든 데이터(명령 포함)가 encryptionPreference: .required로 암호화되게 변경되었습니다. 이는 보안 측면에서 긍정적이지만, 변경된 사항과 그 결정에 대한 간략한 주석이나 문서화가 있다면 향후 유지보수 시 더 명확할 것입니다.
  • 연결 초과 시간(timeout) 상수로 관리: Browser에서 invitePeertimeout 값이 10초에서 20초로 변경되었습니다. 이 값은 네트워크 환경에 따라 중요할 수 있으므로, 하드코딩된 리터럴 대신 적절한 상수(예: MultipeerConstants.invitationTimeout)로 정의하여 관리하면 가독성과 유지보수성이 향상될 것입니다.
  • 연결 관련 로그 추가: Browser에서 session(_:peer:didChange:) 메서드에 browsingManager.stopIfBrowsing() 또는 startIfBrowsing() 로직이 추가되었습니다. 이 로직이 언제 실행되고 어떤 의도를 가지고 있는지 명시하는 로그를 추가하면 연결 상태 변화와 브라우징 동작 간의 관계를 이해하는 데 도움이 될 것입니다. Advertiser 측의 connectionManager.stopIfAdvertising() / startIfAdvertising()도 마찬가지입니다.

✅ Positive Observations

  • 세션 통합 및 프로토콜 명확화: 스트리밍과 명령 채널을 단일 MCSession으로 통합하고 MultipeerHeader를 도입하여 데이터 유형을 명확히 구분한 것은 Multipeer Connectivity의 모범 사례에 가까우며, 코드 베이스를 단순화하고 잠재적인 리소스 중복을 줄이는 훌륭한 리팩터링입니다.
  • 일관된 변경 적용: BrowserAdvertiser 양쪽 컴포넌트 모두에서 세션 관리 로직, 데이터 전송 및 수신 처리, 테스트 코드까지 새로운 프로토콜에 맞춰 일관성 있게 업데이트되었습니다. 이는 복잡한 변경 사항에서 발생할 수 있는 휴먼 에러를 최소화하는 좋은 접근 방식입니다.
  • 연결 상태에 따른 브라우징/광고 최적화: MCSessionState.connecting일 때 브라우징/광고를 중지하고, 연결 성공/실패 시 다시 시작하는 로직이 추가되었습니다. 이는 불필요한 네트워크 활동을 줄이고 기기 배터리 소모를 최적화하는 현명한 개선 사항입니다.
  • 테스트 코드의 견고함: 새로운 MultipeerHeader 프로토콜에 맞춰 AdvertiserTestsBrowserTests 내의 didReceive 테스트 케이스들이 모두 업데이트되어 변경 사항에 대한 테스트 커버리지를 잘 유지하고 있습니다. 특히 데이터에 헤더 바이트를 추가한 후 전달하는 방식은 실제 동작을 정확히 반영합니다.

Copy link
Copy Markdown
Collaborator

@opficdev opficdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!

아이폰, 아이패드 둘다 와이파이를 끄고 블루투스로만 시도해봤는데 연결이 안돼요..

다만 한 기기라도 와이파이에 연결되어 있다면 기존보단 연결 속도가 조금 빨라지긴 한 것 같습니다 👍

Comment on lines +147 to +151
var payload = Data([MultipeerHeader.streaming.rawValue])
payload.append(data)

do {
try mirroringSession.send(data, toPeers: connectedPeers, with: .unreliable)
try mirroringSession.send(payload, toPeers: connectedPeers, with: .unreliable)
Copy link
Copy Markdown
Collaborator

@opficdev opficdev Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

payload를 구성할 때 Data에 배열로 한 이유가 PR에 써진 것처럼 0x00, 0x01로 구분하기 위함인가요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네, 헤더 붙이는 과정입니다!

Copy link
Copy Markdown
Collaborator

@sangYuLv sangYuLv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미러링 세션 하나로 합친 것 좋네요!
아래처럼 테스트 결과가 나왔는데, 초대 받는 측은 와이파이에 꼭 연결 되어야하는 건가보다 싶네요 🧐

아이폰 아이패드 결과
와이파이 킴 (연결은 안 함) 와이파이 킴 (연결은 안 함) ❌ 연결 안 됨
핫스팟 킴 핫스팟에 연결 ❌ 연결 안 되고, 아이폰에서 연결 시도 중 앱 튕김
와이파이 킴 (연결은 안 함) 와이파이 연결 ✅ 연결 됨
와이파이 연결 와이파이 킴 (연결은 안 함) ❌ 연결 안 됨

payload,
toPeers: connectedPeers,
with: .reliable
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세션.send()를 호출할 때, 한 줄로 처리하기도 하고, 이렇게 매개변수 별로 줄바꿈을 하기도 하네요!
만약 린트에 걸리지 않는다면, sendStreamData()에 있는 것처럼 한 줄로 처리하도록 통일하면 어떨까요?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(슬랙 알림 연동 테스트)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(슬랙에서 스레드로 추가되는지 테스트)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sangYuLv 멘션 테스트

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendStreamData에서만 한 줄로 처리하는 것으로 확인되어 여기서 개행 처리 하는 방향으로 통일했습니다. 매개변수가 나름 의미를 갖는 것들이 들어가면 보기 편한 것이 좋다고 생각해요. 한 줄이 좋다고 생각하신다면 추가 의견 남겨주세요

commandManager.execute(data: payload)
} else if firstByte == MultipeerHeader.streaming.rawValue {
logger.info("스트림 세션에서 데이터 수신: \(payload.count) bytes")
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명령이 들어왔을 때도 아래 스트림처럼 로그를 찍어도 좋을 것 같아요!
아니면, 브라우저는 스트림 세션에서 데이터를 수신할 일이 없을 것 같은데 아래처럼 커멘드만 처리할까요?
스트림 세션에 대한 수신 로그를 찍어야했던 이유가 있다면 설명부탁드립니다!

if (session === mirroringSession || session === remoteSession)
  && firstByte == MultipeerHeader.command.rawValue {
    commandManager.execute(data: payload)
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다

@GRJeon GRJeon merged commit 815c9cc into develop Mar 9, 2026
2 checks passed
@GRJeon GRJeon deleted the refactor/#315-OffAdvertiserBrowser branch March 9, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 refactor 코드 리팩토링 (기능 변화 없이 구조 개선, 불필요한 import 제거) ☑️ test 테스트 코드 추가 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invite 단계에서 일시적으로 browser/advertiser를 종료하더라도 연결 목록에서 사라지지 않는다.

4 participants