Skip to content

Conversation

@youn9k
Copy link
Member

@youn9k youn9k commented Nov 25, 2024

🤔 배경

4인 참가를 위해 방 생성/참가 기능을 구현해야 했습니다.

📃 작업 내역

  • localVideoView 단일화
  • 방 생성 기능 구현
  • 방 참가 기능 50% 구현(서버로 요청 보내기만 구현됨)
  • UIActivityViewController를 통한 딥링크 초대 기능 구현
  • 게스트 입장 대기 화면(EnterLoadingViewController) 구현
  • WebSocketClient Delegate Deprecated 및 Publisher 구현

✅ 리뷰 노트

🚀 localVideoView 단일화

기존에는 아래와 같이 ConnectionRepository에 있는 clients 배열에서 직접 localVideoView를 가져왔습니다.

public protocol ConnectionRepository {
    var clients: [ConnectionClient] { get }
}

하지만 이렇게 할 경우 각 ConnectionClient들이 localVideoView를 가지고 있어서 총 3개의 localVideoView가 생성됩니다.
그래서 localVideoView는 ConnectionRepository에서 하나만 관리할 수 있도록 리팩토링했습니다.
우선 ConnectionRepository는 아래와 같이 변경했습니다.

public protocol ConnectionRepository {
    var clients: [ConnectionClient] { get }
    var localVideoView: UIView { get }
    var capturedLocalVideo: UIImage? { get }
    ...
}

하나의 localVideoView를 가지고 UseCase에서 접근이 가능하도록 했습니다. 그리고 ConnectionClient가 기존에 하나씩 생성해서 가지고 있던 localVideoView를 ConnectionRepository에 있는 localVideoView를 전달해서 사용하는 방식으로 바꿨습니다. 아래와 같이 bindLocalVideo 메서드를 사용해 Repository에 있는 localVideoView를 전달해서 바인딩해줄 수 있습니다.

public protocol ConnectionClient {
    func bindLocalVideo(_ localVideoView: UIView)
}

🚀 방 생성 기능 구현

방 생성 기능은 아래와 같은 흐름으로 진행됩니다.

  1. 호스트가 createRoom 명령을 서버로 보냄
  2. 서버는 UUID를 이용해 roomID와 userID를 만들고 이를 토대로 방을 만들어 호스트를 방에 입장시킵니다.
  3. 서버는 생성된 roomID와 userID를 데이터로 인코딩해서 호스트에게 다시 전달합니다.

Request의 형태는 다음과 같습니다. messageType으로 방을 생성할 것인지 이미 생성된 방에 들어갈 것인지 명령을 구분합니다. message에는 해당 명령에 필요한 데이터를 담습니다. 이렇게 담은 정보를 인코딩해서 서버로 보내줍니다.

struct RoomRequestDTO: WebSocketRequestable {
    var messageType: RoomMessageType
    var message: Data?
    
    init(messageType: RoomMessageType, message: Data? = nil) { }
    
    enum RoomMessageType: String, Encodable {
        case createRoom
        case joinRoom
    }
}

서버는 받은 정보를 디코딩하여 messageType을 분석하고 이에 해당하는 로직을 실행시킵니다. 그래서 createRoom의 명령이 들어오면 방을 생성하고 userID를 생성한 후 이를 구조체에 담아 인코딩하여 다시 호스트에게 전달합니다. 전달된 정보는 딥링크 URI 생성에 이용됩니다.

🚀 방 참가 기능 50% 구현(서버로 요청 보내기만 구현됨)

현재 방 참가 기능은 딥링크 URI로 받은 roomID 정보를 담아서 서버에 joinRoom의 messageType을 가진 명령을 전달하는 것 까지만 구현되어 있습니다.

public struct JoinRoomRequestMessage: Encodable {
    public let roomID: String
}

JoinRoomRequest의 메시지에는 위의 정보가 담겨서 서버로 전달됩니다. 이후 서버에서 이를 토대로 방에 입장할 수 있는 로직을 구현할 계획입니다.

🚀 UIActivityViewController를 통한 딥링크 초대 기능 구현

카톡으로 공유하면 아래와 같은 형태로 URI Scheme을 전달하고, 누르면 딥링크 방식을 통해 아래 게스트 입장 대기 화면으로 진입하게 됩니다.

🚀 게스트 입장 대기 화면(EnterLoadingViewController) 구현

텍스트 정렬 안맞는 문제는 가운데 정렬로 수정되었습니다 ! 스샷 귀차니즘 이슈로..

게스트 입장 대기 화면 ViewDidLoad 시점에 방 참가 요청을 보내게 됩니다.
그리고 방 참가 성공 여부에 따라 아래와 같이 분기 처리 됩니다.

  • 방 참가 성공 시: 게스트 입장으로 WaitingRoomViewController로 진입
  • 방 참가 실패 시: 호스트 입장으로 WaitingRoomViewController로 진입

🚀 WebSocketClient Delegate Deprecated 및 Publisher 구현

WebSocketClient는 이벤트를 전달하려면 Delegate를 사용해야 했습니다. 하지만 아래와 같이 여러 서비스들에서 하나의 WebSocketClient를 바라보게 되었고, Delegate 배열을 관리해야 했습니다. Delegate를 배열로 관리한다면 위임자(Delegate)라는 패턴의 의미와 맞지 않았고, Delegate의 순환 참조 위험성도 증가한다는 문제가 있었습니다.

그래서 아래와 같이 WebSocketClient 내에 Publisher들을 두어 필요한 이벤트들만 구독할 수 있도록 변경했습니다.

아직 모든 서비스들에서 WebSocketClientDelegate를 제거하지 못해 아래 코드와 같이 deprecated 시키고, 차차 걷어내려고 합니다.
그리고 클래스 내부에선 subject에 접근이 가능하고, 외부에는 publisher만 노출되도록 해 subject의 편리함은 가져가되 외부에서 send할 수 없는 단방향 구조를 만들었습니다.

public final class WebSocketClientImpl: NSObject, WebSocketClient {
    @available(*, deprecated, message: "Publisher로 교체될 예정입니다.")
    public var delegates: [WebSocketClientDelegate] = []

    private let _webSocketDidConnectPublisher = PassthroughSubject<Void, Never>()
    private let _webSocketDidDisconnectPublisher = PassthroughSubject<Void, Never>()
    private let _webSocketdidReceiveDataPublisher = PassthroughSubject<Data, Never>()

    public var webSocketDidConnectPublisher: AnyPublisher<Void, Never> {
        _webSocketDidConnectPublisher.eraseToAnyPublisher()
    }
    public var webSocketDidDisconnectPublisher: AnyPublisher<Void, Never> {
        _webSocketDidDisconnectPublisher.eraseToAnyPublisher()
    }
    public var webSocketdidReceiveDataPublisher: AnyPublisher<Data, Never> {
        _webSocketdidReceiveDataPublisher.eraseToAnyPublisher()
    }
}

🚀 테스트 방법

링크 버튼을 누르면 공유 시트가 올라오고, 딥링크 가능한 URI Scheme을 공유할 수 있습니다.
그리고 외부에서 해당 메시지를 누르면 게스트 모드로 앱에 진입합니다.
현재는 아래와 같이 서버 요청없이 3초 뒤에 WaitingRoom으로 이동되게 되어있습니다.

Kiyoung-Kim-57 and others added 12 commits November 25, 2024 13:46
- 서버에서 사용되는 Encodable Extension을 추가했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- 방 생성 명령을 받았을 때 서버의 동작 구현
- RoomRequest의 messageType에 따라 분기 처리

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- ByteBuffer 타입의 데이터를 DTO로 변환하기 위한 Extension을 추가했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- toData를 Encodable의 Extension으로 구현해서 기존 부분을 삭제했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- RoomServiceImpl에서 서버가 준 Response를 디코딩합니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
- LocalVideoView는 Repository에서 하나로 관리되어 RemoteVideoView만 캡쳐하도록 변경
- LocalVideoView에 대한 캡쳐는 추후 커밋에서 대응 예정
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
- CreateRoomUseCase
- CreateRoomUseCaseImpl
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
ConnectionRepository에서 하나만 들고 있어도 되겠다고 판단해 RoomService를 도메인으로 옮겨 ConnectionRepository에서 알 수 있도록 했습니다.
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
- localVideoView: 로컬 카메라 화면
- CapturedLocalVideo: localVideoView의 매 프레임 의미지
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
@youn9k youn9k added the ✨ feat 새로운 기능 추가 label Nov 25, 2024
@youn9k youn9k requested review from 0Hooni and hsw1920 November 25, 2024 06:36
@youn9k youn9k linked an issue Nov 25, 2024 that may be closed by this pull request
3 tasks
@youn9k youn9k marked this pull request as draft November 25, 2024 06:36
- RoomService의 메서드를 역할에 따라 명확하게 구분

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
@youn9k youn9k force-pushed the feat/#95-server-create-room branch from 8f70fb6 to f953cf3 Compare November 25, 2024 13:36
Kiyoung-Kim-57 and others added 9 commits November 25, 2024 22:36
- Repository에 LocalVideoView를 생성하면서 연관된 UseCase들을 수정했습니다.

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- 대문자로 시작한 속성명 변경

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- return 타입을 Bool로 변경했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- DomainInterface import
- 생성자에 추가된 부분 수정

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- CreateRoomUseCase 추가에 따라 의존성 주입 수정

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- SignalingService와 RoomService가 동시에 WebSocketClient의 delegate가 되면서 여러 개를 동시에 관리할 수 있도록 배열로 수정했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
WebSocketClientDelegate를 여러 곳에서 쓰게 되면서, 순환참조 문제나 배열을 순회하며 델리게이트 메소드를 호출해주는 구조보단 Pub/Sub 패턴을 활용해보고자 했습니다.
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
youn9k and others added 9 commits November 25, 2024 22:36
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- JoinRoomRequestMessage는 Request를 서버에 전달할 때 담길 메시지입니다
- JoinRoomResponseMessage는 Response로 내려올 메시지입니다
- JoinRoomEntity는 JoinRoomResponseMessage의 Entity입니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- WebSocketRequestable의 확장 메서드를 Encodable의 확장으로 옮겼습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- RoomService에서 서버로 요청을 보내는 로직을 구현했습니다.

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
@youn9k youn9k force-pushed the feat/#95-server-create-room branch from f953cf3 to bd7957e Compare November 25, 2024 13:37
 Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
@youn9k youn9k marked this pull request as ready for review November 25, 2024 14:05
Copy link
Collaborator

Choose a reason for hiding this comment

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

EditPhoto쪽에서는 이거 id랑 nickname만 받아서 Codable 채택하는 새로운 Entity로 관리할 수 있도록 해야할 것 같아요.

Copy link
Collaborator

@0Hooni 0Hooni 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

@hsw1920 hsw1920 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다. 코멘트 확인 부탁드려요오

Kiyoung-Kim-57 and others added 3 commits November 26, 2024 15:44
- flipVideoRender를 사용해 카메라 화면을 좌우반전 시킬 수 있도록 했습니다

- RTCVideoRender를 UIView로 캐스팅해서 transform을 변경해 적용했습니다

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- URLParser -> DeepLinkParser
- userID -> hostID

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- decoder를 생성하지 않고 파라미터로 받음
- map 내부에 [weak self]로 약한 캡쳐
- 안쓰이는 DTO 삭제
- 서버 코드 가독성 향상

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
@youn9k youn9k merged commit 31c07ae into develop Nov 26, 2024
1 check passed
@youn9k youn9k deleted the feat/#95-server-create-room branch November 26, 2024 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

서버에서 방 생성하고 클라이언트에 방 정보 전달

5 participants