Skip to content

Conversation

@YunDaeHyeon
Copy link
Collaborator

@YunDaeHyeon YunDaeHyeon commented Feb 4, 2026

🔗 연관된 이슈

📝 작업 내용

📌 요약

Browser 책임을 분리하여 코드 가독성과 유지보수성을 개선했습니다.

🔍 상세

분리된 파일(기능)은 아래 표로 정리했습니다.

파일 담당 기능 Browser에서 분리 된 요소
BrowserStreamManager AsyncStream/Continuation 관리 5개의 AsyncStream 생성 및 yield 로직
BrowsingManager 기기 탐색 MCNearbyServiceBrowserDelegate, discoveredPeers 상태 관리
BrowserCommandManager 명령 수신 처리 executeCommand 로직, BrowserCommandDelegate 프로토콜 정의

주요 변경 사항은 아래와 같습니다.

  • 기존 Browser에서 AsyncStream 관리, 기기 탐색, 명령 처리 로직을 별도 매니저로 분리했습니다. BrowserStreamManager
  • Browser는 각 매니저를 조율하는 역할로 단순화하고자 했습니다.
  • 외부에서 사용하는 public API는 기존과 동일하게 유지되도록 했습니다. (최대한 영향 없게..)
  • 비즈니스 로직 변경 없이 순수 코드 이동 작업만 진행했습니다!

위 작업으로 기존 500줄이 넘어가는 Browser 코드를 약 100줄 이상 감소시킬 수 있었습니다.
또한 #298 에서 작업한 테스트 코드를 토대로 1개의 파일 분리 후 테스트 실행 -> 통과 확인 후 실기기 테스트 진행 -> 문제 없으면 다른 파일 분리의 순서로 점진적 분리를 진행했습니다.

💬 리뷰 노트

현재 Lint에서 file_length 경고가 발생합니다 (400줄 제한, 현재 412줄).
추가 분리를 고민했으나 아래와 같은 이유로 현 상태에서 마무리했습니다.

  • MCSessionDelegate를 별도 파일로 분리할 수 있지만, 세션 로직과 밀접하게 연결되어 있어 오히려 코드 흐름 파악이 어려워질 수 있을 것이라고 판단했고 더 분리하면 다른 작업에 영향이 크게 있을 것으로 예상했습니다.
  • 물론 MCSessionDelegate 부분도 분리할 수 있을 것 같지만.. 우선 현재 작업까지 분리하는 것으로 마무리했습니다.

@YunDaeHyeon YunDaeHyeon requested review from GRJeon, opficdev and sangYuLv and removed request for GRJeon and sangYuLv February 4, 2026 13:31
@YunDaeHyeon YunDaeHyeon self-assigned this Feb 4, 2026
@YunDaeHyeon YunDaeHyeon added the 🔨 refactor 코드 리팩토링 (기능 변화 없이 구조 개선, 불필요한 import 제거) label Feb 4, 2026
@YunDaeHyeon YunDaeHyeon added this to the WEEK05 milestone Feb 4, 2026
Copy link
Collaborator Author

@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는 Browser 클래스의 핵심 로직을 BrowsingManager, BrowserCommandManager, BrowserStreamManager와 같은 전용 매니저 클래스들로 성공적으로 분리하여 모듈화 수준을 크게 향상시켰습니다. 이는 코드의 가독성, 유지보수성, 그리고 테스트 용이성을 비약적으로 개선했습니다. 새로운 Swift Testing 프레임워크를 활용한 테스트 코드도 인상적입니다.

전반적으로 코드는 잘 작성되었으며 의도된 기능을 수행할 것으로 보입니다. 다만, .xcscheme 파일의 잠재적인 오타와 Browser 초기화 로직의 미묘한 순서 문제, 그리고 몇몇 테스트 케이스의 논리적 결함이 발견되어 생산 환경 배포 전 수정이 필요합니다.

⚠️ Key Issues

  • Xcode Scheme 파일 (mirroringBoothTests.xcscheme)의 LastUpgradeVersion 오타 추정: LastUpgradeVersion = "2610"으로 설정되어 있습니다. 이는 현재 Xcode 버전인 15.x 또는 미래 버전의 16.x와 비교했을 때 비정상적으로 높은 값입니다. 실제 사용 중인 Xcode 버전과 맞지 않을 경우 빌드 시스템에서 예상치 못한 문제가 발생할 수 있습니다. 1610 또는 1510 등의 오타일 가능성이 높으니 확인이 필요합니다.

  • Browser 초기화 시 super.init() 호출 순서 오류: Browser 클래스의 init 메서드에서 commandManager.delegate = selfmirroringHeartBeater.delegate = selfsuper.init() 호출 이전에 설정하고 있습니다. Swift에서 selfsuper.init()이 완료된 후에야 완전히 초기화된 것으로 간주됩니다. self를 delegate로 할당하기 전에 super.init()이 먼저 호출되어야 합니다. 이는 런타임에 미묘한 문제를 일으킬 수 있으므로, delegate 할당 코드를 super.init() 호출 뒤로 옮겨야 합니다.

  • BrowserTests.swift 내 명령 수신 테스트의 논리적 결함:

    • heartBeat_명령_수신시_HeartBeater가_beat를_호출한다
    • 알_수_없는_명령_수신시_크래시하지_않는다
      이 두 테스트 케이스는 browser.session(_:didReceive:fromPeer:) 메서드를 직접 호출하며 MCSession의 목(mock) 객체를 전달합니다. 그러나 Browser 내부의 session(_:didReceive:fromPeer:) 메서드는 수신된 세션이 mirroringCommandSession 또는 remoteSession 중 하나와 일치하는지 확인하는 조건문(if session === mirroringCommandSession || session === remoteSession)을 가지고 있습니다. 현재 테스트에서 전달되는 목 세션은 이 조건에 해당하지 않으므로, commandManager.execute(data: data) 코드가 실제로 호출되지 않습니다. 결과적으로 이 테스트들은 의도된 명령 처리 로직을 검증하지 못하고 단순히 "크래시가 발생하지 않는다"는 것만을 확인합니다. 해당 테스트 케이스들이 BrowserCommandManager로의 위임을 제대로 검증할 수 있도록 목 세션을 Browser의 내부 세션 중 하나로 설정하거나, BrowserCommandManagerexecute 메서드가 호출되었는지 직접 검증하는 방식으로 수정이 필요합니다.

🛠 Improvement Suggestions

  • BrowserMirroringDeviceCommandRemoteDeviceCommand 의존성: BrowserCommandManager에서 Advertiser.CameraDeviceCommand를 사용하여 명령을 파싱합니다. 이는 BrowserAdvertiser 간의 명령어 정의에 대한 암묵적인 결합을 나타냅니다. 현재는 잘 작동하지만, 장기적으로는 AdvertiserCommandManagerBrowserCommandManager가 공유하는 공통 명령 Enum을 정의하여 두 모듈 간의 결합도를 낮추는 것을 고려해볼 수 있습니다. (예: SharedCommand Enum)

  • 로그 가독성 개선: 일부 로그 메시지에 기기 ID나 연결된 세션 타입과 같은 중요한 정보가 포함되어 있지만, 모든 관련 정보가 일관적으로 포함되지는 않습니다. 예를 들어, logger.warning("사진 전송 실패 : \(error.localizedDescription)") 메시지에는 어느 기기로의 전송이 실패했는지와 같은 추가 정보가 있으면 디버깅에 더 유용할 것입니다.

✅ Positive Observations

  • 성공적인 모듈화 및 책임 분리: Browser 클래스의 로직을 BrowsingManager, BrowserCommandManager, BrowserStreamManager로 깔끔하게 분리한 점은 매우 훌륭합니다. 각 매니저가 명확한 책임을 가지게 되어 코드의 이해도를 높이고 잠재적인 버그 발생 가능성을 줄였습니다.

  • AsyncStreamContinuation 관리 개선: BrowserStreamManager를 도입하여 모든 비동기 스트림과 그에 대한 Continuation을 한 곳에서 관리하도록 한 점은 비동기 이벤트 처리의 일관성과 안전성을 크게 향상시켰습니다. bufferingNewest(1) 정책도 실시간 스트림 데이터에 적합한 현명한 선택입니다.

  • 안정적인 명령어 처리 로직: BrowserCommandManager에서 DispatchQueue.main.async { [weak self] in ... }를 사용하여 UI 업데이트 및 상태 변경을 메인 스레드에서 안전하게 처리하고, 순환 참조를 방지하기 위해 [weak self]를 사용한 점은 모범적인 패턴입니다.

  • Swift Testing 프레임워크의 적극적인 활용: 새롭게 도입된 Swift Testing 프레임워크를 사용하여 비동기 로직과 스트림 이벤트를 효과적으로 테스트한 점이 뛰어납니다. EventCollectorHeartbeatEventCollector와 같은 액터 기반 테스트 헬퍼는 비동기 테스트의 견고함을 높여줍니다.

  • 명령어 Enum의 중앙 집중화: Browser.MirroringDeviceCommand와 같이 중첩되어 있던 명령어 Enum들을 BrowserManager/BrowserCommands.swift 파일로 이동시켜 독립적인 전역 Enum으로 만든 점은 코드 탐색성과 재사용성을 개선합니다.

@YunDaeHyeon YunDaeHyeon changed the base branch from develop to feat/#288-browser-test-code February 4, 2026 13:57
@YunDaeHyeon YunDaeHyeon force-pushed the refactor/#289-browser-refactoring branch from d9533b8 to 62baf66 Compare February 4, 2026 15:16
Copy link
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.

고생하셨습니다!

폴더링을 BrowserManager 대신 Manager로 하는건 별로일까요?

Image

static let advertiser = Logger(subsystem: subsystem, category: "Advertiser")
static let advertisingManager = Logger(subsystem: subsystem, category: "AdvertisingManager")
static let advertiserCommandmanager = Logger(subsystem: subsystem, category: "AdvertiserCommandmanager")

Copy link
Collaborator

Choose a reason for hiding this comment

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

이 공란엔 왜 diff가 찍혔을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Advertiser랑 Browser 로거를 좀 구분짓고 싶어서 개행 하나 넣은게 잡힌 것 같네요..ㅎㅎ

@YunDaeHyeon
Copy link
Collaborator Author

@opficdev

하단 Streaming 폴더 하위에 CameraManager라는 폴더가 있어서 네이밍을 동일하게 맞추고 싶었습니다!

하지만.. 말씀해주신 부분을 생각해보니 현재 분리 된 파일들이 Browser 라는 폴더 하위에 위치하니.. Manager 라는 이름이 현재 상황에선 더 적합할 것 같네용 👀

바로 적용하겠습니다~

Copy link
Collaborator

@GRJeon GRJeon left a comment

Choose a reason for hiding this comment

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

고생하셨습니다.. 정말 많이 줄었네요

@YunDaeHyeon YunDaeHyeon force-pushed the feat/#288-browser-test-code branch from 22aee42 to c9cd211 Compare February 5, 2026 00:21
Base automatically changed from feat/#288-browser-test-code to develop February 5, 2026 00:55
Copy link
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.

분리 최고...
수고 많으셨습니다 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

이거 파일 이름도 BrowsingEvents로 가능한가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아! 수정해놓겠습니당

- Browser는 계산 프로퍼티로 스트림을 노출
- HeartBeeater는 스트림 매니저의 yield 메서드 사용
- 테스트 코드 모두 통과로 동등성 확인 완료
- MCNearbyServiceBrowserDelegate 분리
- discoveredPeers 상태 관리 분리
- Browser는 browsingManager 인스턴스를 사용하도록 개선
- delegate extenstion 제거
- executeCommand 메서드를 CommandManager의 execute(data:)로 분리
- BrowserCommandDelegate Protocol 정의
- Browser에서 delegate 채택
- 기존 Browser.MirroringDeviceCommand, Browser.RemoteDeviceCommand 사용처에서 Browser. 만 제거
- BrowserManager 폴더 생성 및 관련 파일 이동
@YunDaeHyeon YunDaeHyeon force-pushed the refactor/#289-browser-refactoring branch from 68f6835 to 85405e8 Compare February 5, 2026 01:48
@YunDaeHyeon YunDaeHyeon merged commit 3f7aea2 into develop Feb 5, 2026
2 checks passed
@YunDaeHyeon YunDaeHyeon deleted the refactor/#289-browser-refactoring branch February 5, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 refactor 코드 리팩토링 (기능 변화 없이 구조 개선, 불필요한 import 제거)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browser 책임을 분리한다.

4 participants