소프트웨어 마에스트로 15기에서, Best Practice 용도로 만든 코인뷰어 어플리케이션 입니다.
실시간 코인 가격 변동 | 코인 디테일 정보(오더북테이블, 채결 거래정보) |
![]() |
![]() |
언어 및 화폐 동적변경 | 리스트 타입 동적변경 |
![]() |
![]() |
메인 프레임워크 | SwiftUI |
아키텍처 | 클린아키텍처 |
디자인 패턴 | MVVM |
외부 의존성 | Swinject, SimpleImageProvider(자체구현 라이브러리) |
CI | github actions |
해당 애플리케이션은 테스트 플라이트에 배포되어 있습니다. 아래 링크를 통해 실행가능합니다.
- 레포지토리를 다운로드 합니다.
tuist install
을 통해 외부의존성을 install합니다. (Tuist버전: 4.12.1)- 요구하는 xcconfig파일을 생성합니다.
tuist generate
를 CLI에 입력합니다.
※ xcconfig파일이 해당 레포지토리에 포함되어 있지않아 따로 생성이 필요합니다.
(./Secrets/xcconfigs/Release and Debug.xcconfig) Debug빌드시 xcconfig파일내 값을 사용하지 않기 때문에 실행시 Debug스킴을 사용하시길 바랍니다.
두 xcconfig파일에 "OPENEX_API_KEY"키값을 할당해야합니다. 환율 API 사이트(Open exchange rates)의 API_KEY값을 할당해주면 프로젝트가 정상적으로 실행됩니다.
OPENEX_API_KEY="API Key for open exchange rates"
사용한 이유
- 빌드세팅을
Swift코드
로 관리 - 프레임워크
Embed여부
자동화 - 타겟의
Mach-O
타입 설정 간편화 - 템플릿을 사용한 모듈 생성
tuist graph
를 사용한 의존성 시각화
해당 프로젝트는 Presentation, Domain, Data 총 3가지로 이루어진 계층으로 분리되었습니다.
변경 가능성이 높은 UI와 데이터 소스의 경우 Domain계층과 직접적인 의존관계를 지정하지 않고 protocol
인터페이스를 통해서 가능하도록 설계했습니다.
protocol
타입의 구현체는 런타임에 의존성을 주입하여, 객체간 유연한 협력이 가능하도록 했습니다.

레이어 | 책임 |
Presentation | 텍스트 가공, UI업데이트 주기 관리(throttle) |
Domain | 도메인 로직 기반 코인 리스트 가공(가장 거래량이 높은 코인 상위 30개) |
Data - Repository | DTO를 엔티티화 |
Data - DataSource | 리스트 저장 및 관리(HashMap으로 관리) |

- (상단) 실시간 오더북 테이블 아키텍처
레이어 | 책임 |
Presentation | 텍스트 가공, UI업데이트 주기 관리(throttle), 테이블 크기 결정 |
Domain | 테이블 정렬방식 결정(매수가 매도가에 따라 상이), 요청된 테이블 크기로 테이블을 가공 |
Data - Repository | DTO를 엔티티화 |
Data - DataSource | 전체 테이블 저장 및 관리(HashMap으로 관리) |
- (하단) 실시간 코인 거래내역 리스트 아키텍처
레이어 | 책임 |
Presentation | UI업데이트 주기 관리(throttle), 테이블 크기 결정 |
Domain | 데이터 업데이트 주기 결정(정보의 최신성 포기), 테이블 정렬방식 결정, 요청받은 테이블 개수로 데이터 가공 |
Data - Repository | DTO를 엔티티화 |
Data - DataSource | 전체 개래내역 테이블을 관리(DTO형태), 전달받은 도메인 로직에 따라 테이블 업데이트 주기를 조절 |
웹소켓 연결관리는 WebSocketManagementHelper
객체를 통해서 진행됩니다.
애플리케이션 런칭 이후 시점의 플로우는 아래와 같습니다.
- AppDelegate에서 앱을 론칭한 이후
WebSocketManagementHelper
객체를 통해 웹소켓 연결을 시도합니다. - 루트 모듈은 일정 시간 동안 의도적으로 딜레이를 발생시켜 유저를 스플래시 화면에 머무르게 합니다. 해당 기간 동안 웹소켓 연결이 대부분 완료됩니다.
- 각 화면마다 원하는 스트림을
WebSocketManagementHelper
객체를 통해 구독하고 화면 이탈 시 해제합니다.
전체적인 객체 협력구조는 다음과 같습니다.

애플리케이션이 백그라운드로 진입할 경우 웹소켓 연결을 의도적으로 끊습니다.
불필요한 리소스 사용으로 인해 애플리케이션이 시스템에 의해 강제 종료되는 상황을 최대한 배제하기 위해서입니다.
연결 및 해제는 SceneDelegate
의 생명주기 함수를 통해서 현재 관리되고 있습니다.
웹소켓을 재연결하는 경우 WebSocketManagementHelper
객체에 캐싱 되어 있던 이전 연결 스트림들이 자연스럽게 복원됩니다.

웹소켓이 의도치 않게 끊어지거나 스트림 구독 메시지 전송에 실패한 경우 유저에게 적절한 에러 상황을 표현해야 한다고 판단했습니다.
WebSocketManagementHelper
객체는 웹소켓 연결 상태 및 메시지 전송 성공 여부에 대한 결과를 수신하고 에러를 발생시킵니다.
하지만 해당 객체는 특정 ViewModel
에 속한 객체가 아니라 UI를 표시하기 어려운 구조를 지녔습니다.
따라서 AlertShooter
객체를 통해 View와 WebsocketHeleper
를 매개하는 방식을 선택했습니다.
AlertShooter
의 경우 SwiftUI EnvironmentalObject
로 지정되어 Alert 표시를 희망하는 뷰에서 오류 모델을 수신하여 화면상에 표시합니다.

화폐와 언어에 대한 정보는 I18NManager
객체를 통해서 접근할 수 있습니다.
ViewModel은 i18NManager
객체를 의존하여 해당 정보를 획득한 후 UI에 적절한 변화를 발생시킵니다.
※ 언어별 텍스트 제공자의 경우 단일체 패턴을 활용하여 접근성을 높이는 선택을 했습니다.
