재사용 가능한 UI 아키텍처: 컴포넌트 기반 설계로 모달 시스템 구축하기 ✨
들어가며: 컴포넌트 기반 UI 아키텍처가 필요했던 이유 🌉
안녕하세요! 오늘은 컴포넌트 기반 아키텍처를 활용해 재사용 가능한 모달 시스템을 어떻게 구축했는지 이야기해볼게요.
복잡한 교육 앱을 개발하면서 가장 큰 도전 중 하나는 다양한 화면에서 일관된 사용자 경험을 제공하는 것이었어요.
특히 알림창, 정보 제공 다이얼로그 등 다양한 모달 UI 요소가 앱 전반에 걸쳐 필요했죠.
어떤 문제들이 있었나요?
기존에는 각 화면마다 필요한 UI 요소를 개별적으로 구현하다 보니 다음과 같은 문제점들이 발생했어요:
- 디자인 불일치 🎨: 개발자마다 다른 방식으로 구현하여 UI 일관성이 떨어졌어요
- 코드 중복 📝: 비슷한 기능의 UI 요소마다 중복 코드가 발생했어요 (70% 이상 중복!)
- 개발 시간 증가 ⏱️: 새로운 UI 요소 추가에 평균 3일이나 걸렸어요
- 유지보수 어려움 🔧: 디자인 변경 시 모든 구현 부분을 일일이 수정해야 했죠
- 협업 비효율 👥: 기획자-디자이너-개발자 간 의사소통에 많은 시간이 소요됐어요
이러한 문제들을 해결하기 위해 컴포넌트 기반 아키텍처(Component-Based Architecture)와 합성 패턴(Composite Pattern)을 적용한 재사용 가능한 UI 컴포넌트 시스템을 설계하고, 이를 모달 시스템에 적용한 ModalKit을 개발하게 되었답니다.
컴포넌트 기반 아키텍처란 무엇인가요? 🧩
어떤 핵심 원칙을 가지고 있나요?
컴포넌트 기반 아키텍처는 다음 핵심 원칙을 기반으로 해요:
- 영역 분리 🏗️: UI를 헤더(Header), 바디(Body), 푸터(Footer)로 논리적으로 분리해요
- 모듈성 🧱: 독립적인 컴포넌트를 조합하여 복잡한 UI를 구성할 수 있어요
- 단일 책임 🎯: 각 컴포넌트가 명확한 하나의 책임만 담당해요
- 상속보다 합성 🔄: 컴포넌트 조합을 통해 기능을 확장해요
- 이벤트 위임 📢: 컴포넌트에서 발생한 이벤트를 상위 계층으로 위임해요
컨테이너와 컴포넌트는 어떻게 구분되나요?
이 아키텍처에서는 UI 요소를 두 가지 주요 개념으로 구분해요:
- 컨테이너(Container) 📦: 전체 UI 구조를 관리하고 컴포넌트들을 조합하는 상위 요소예요
- 컴포넌트(Component) 🧩: 특정 UI 역할을 담당하는 독립적 모듈이에요 (헤더, 바디, 푸터)
데이터와 이벤트는 어떻게 흐르나요?
컨테이너와 컴포넌트 간의 데이터 흐름은 명확한 방향성을 가지고 있어요:
- 하향식 데이터 흐름 ⬇️: 컨테이너에서 컴포넌트로 데이터가 전달돼요
- 상향식 이벤트 위임 ⬆️: 컴포넌트에서 발생한 이벤트는 컨테이너로 위임돼요
- 중앙화된 상태 관리 🎛️: 컨테이너가 전체 상태 관리를 담당해요
이러한 구조는 복잡한 UI 요소의 상태와 동작을 예측 가능하게 만들고, 컴포넌트 간 결합도를 낮춰 재사용성을 극대화할 수 있답니다!
ModalKit은 어떻게 구현되었나요? 🛠️
ModalKit은 컴포넌트 기반 아키텍처와 합성 패턴을 모달 다이얼로그 시스템에 적용한 실제 구현체예요.
이 시스템을 사용하면 다양한 유형의 모달 UI를 일관된 방식으로 쉽게 구현할 수 있어요.
ModalKit의 구조는 어떻게 되나요?
컴포넌트 계층 구조는 어떻게 설계되었나요?
ModalKit은 영역별로 특화된 컴포넌트 프로토콜 계층 구조를 가지고 있어요:
// 모든 모달 컴포넌트의 기본 프로토콜
protocol ModalComponent: UIView {
// 공통 속성과 메서드
}
// 영역별 특화 프로토콜
protocol HeaderComponent: ModalComponent { /* 헤더 특화 기능 */ }
protocol BodyComponent: ModalComponent { /* 바디 특화 기능 */ }
protocol FooterComponent: ModalComponent { /* 푸터 특화 기능 */ }
이 계층 구조는 각 영역에 맞는 컴포넌트만 할당될 수 있도록 타입 안전성을 제공해요. 👍
핵심 구현 요소는 무엇인가요?
1. ModalContainer는 어떻게 구현되었나요?
모달의 전체 구조를 관리하는 컨테이너 클래스로, 헤더, 바디, 푸터 컴포넌트를 조합하고 배치해요:
class ModalContainer: UIView {
private let contentStackView = UIStackView()
private var headerComponent: HeaderComponent?
private var bodyComponents: [BodyComponent] = []
private var footerComponent: FooterComponent?
func configureHeader(_ component: HeaderComponent?) {
// 헤더 구성 로직
}
func configureBody(_ components: [BodyComponent]) {
// 바디 구성 로직
}
func configureFooter(_ component: FooterComponent?) {
// 푸터 구성 로직
}
}
2. ModalViewMaker는 어떤 역할을 하나요?
선언적인 방식으로 모달을 구성할 수 있는 빌더 패턴 구현체예요:
class ModalViewMaker {
private var headerComponent: HeaderComponent?
private var bodyComponents: [BodyComponent] = []
private var footerComponent: FooterComponent?
func header(_ component: HeaderComponent) -> Self {
headerComponent = component
return self
}
func body(_ component: BodyComponent) -> Self {
bodyComponents.append(component)
return self
}
func footer(_ component: FooterComponent) -> Self {
footerComponent = component
return self
}
func build() -> ModalContainer {
let container = ModalContainer()
container.configureHeader(headerComponent)
container.configureBody(bodyComponents)
container.configureFooter(footerComponent)
return container
}
}
3. ModalCoordinator는 무엇을 담당하나요?
모달의 생성과 표시를 중앙에서 관리하는 싱글톤 코디네이터예요:
public class ModalCoordinator {
public static let shared = ModalCoordinator()
// 앱 시작 시 초기화
public func initializeOverlays(for scene: UIWindowScene) {
// 모달용 오버레이 윈도우 설정
}
// 모달 표시 메서드
public func showModal(for factory: ModalFactory) {
let viewMaker = factory.makeModal()
let modalContainer = viewMaker.build()
displayModal(modalContainer)
}
// 편의 메서드들
public func showAlert(title: String, message: String, confirmButtonTitle: String, onConfirm: @escaping () -> Void) {
// 알림 모달 표시
}
}
💡 핵심 포인트: ModalCoordinator는 싱글톤 패턴을 사용해 전체 앱에서 모달 표시를 중앙에서 관리하고,
다양한 편의 메서드를 제공해 간편하게 모달을 사용할 수 있게 해줘요!
협업은 어떻게 이루어졌나요? 👥
컴포넌트 기반 아키텍처와 ModalKit 개발 과정에서 가장 중요했던 것은 디자이너와 개발자 간의 효과적인 협업이었어요.
디자이너와 개발자는 어떻게 협업했나요?
효과적인 협업을 위해 다음과 같은 체계를 구축했답니다:
- 공통 용어 정립 📚: '컨테이너', '컴포넌트', '헤더', '바디', '푸터' 등의 용어를 공통 언어로 사용했어요
- 컴포넌트 가이드라인 작성 📋: 디자이너와 개발자가 함께 컴포넌트 디자인 및 동작 원칙을 정의했죠
- 디자인 시스템 연동 🎨: 색상, 타이포그래피, 간격 등의 디자인 시스템 요소를 통합했어요
- 피드백 루프 구축 🔄: 구현된 컴포넌트에 대한 빠른 피드백과 반영 체계를 확립했어요
Example 앱은 어떤 역할을 했나요?
협업 효율성을 높이기 위해 ModalKit-Example 앱을 개발했어요:
- 카탈로그 기능 📖: 모든 모달 유형을 한 곳에서 확인할 수 있어요
- 실시간 커스터마이징 ⚡: 속성을 실시간으로 조정하며 결과를 확인할 수 있어요
- 명세서 역할 📝: 기획자, 디자이너, 개발자 간 공통 참조 자료로 활용했어요
이 Example 앱은 특히 다음과 같은 상황에서 매우 유용했답니다:
- 신규 컴포넌트 기획 시 🆕: 기존 컴포넌트들을 보며 새 컴포넌트의 방향성을 논의했어요
- 디자인 변경 검토 시 🔍: 실시간으로 속성을 변경하며 다양한 디자인 옵션을 검토했어요
- 버그 리포트 시 🐞: 정확한 재현 및 문제 해결 방향을 논의할 수 있었어요
덕분에 피드백 수집 시간이 1주에서 1-2일로 단축되었고, 명확한 시각적 참조를 통해 의사소통 오류가 크게 감소했답니다!
어떤 성과를 얻었나요? 📊
컴포넌트 기반 아키텍처와 ModalKit 도입으로 얻은 주요 성과를 살펴볼까요?
정량적으로 어떤 개선이 있었나요?
- 코드 중복 70% 이상 감소 ✂️: 컴포넌트 재사용으로 중복 코드가 대폭 감소했어요
- UI 개발 시간 65% 단축 ⏱️: 새로운 모달 유형 개발이 3일에서 1일 이내로 단축됐어요
- 협업 효율성 40% 향상 👥: 기획-디자인-개발 간 의사소통 시간이 줄었어요
- 버그 발생률 50% 감소 🐞: 표준화된 컴포넌트로 예측 가능한 동작을 보장할 수 있었죠
- 유지보수 시간 60% 절감 🔧: 중앙화된 컴포넌트로 변경 사항을 한 번에 적용할 수 있게 됐어요
실제로 어떻게 사용하나요?
ModalKit은 다양한 상황에서 효과적으로 활용되고 있어요:
단순 알림 모달은 어떻게 구현하나요?
ModalCoordinator.shared.showAlert(
title: "알림",
message: "작업이 완료되었습니다.",
confirmButtonTitle: "확인",
onConfirm: { print("확인됨") }
)
테이블 형태의 정보 제공 모달은 어떻게 구현하나요?
let tableComponent = TableBodyComponent(
header: tableHeader,
rows: tableRows
)
ModalCoordinator.shared.showCustomModal(
title: "학습 이력",
bodyComponents: [
MessageComponent(message: "최근 학습 이력", font: .boldSystemFont(ofSize: 18)),
tableComponent
],
buttons: [
FooterButtonComponent.ButtonInfo(title: "확인", style: .confirm, action: {})
]
)
💡 핵심 포인트: 복잡한 UI 요소도 선언적인 방식으로 간결하게 구현할 수 있어요!
어떤 문제들을 해결했나요?
1. 컴포넌트 간 의존성 관리는 어떻게 해결했나요?
문제 🤔: 초기 설계에서 컴포넌트 간 직접적인 참조로 인한 의존성 문제가 발생했어요.
해결 💡: 이벤트 위임 패턴을 도입하여 컴포넌트에서 발생한 이벤트를 컨테이너로 위임하고, 컨테이너가 다른 컴포넌트에 필요한 액션을 전달하는 방식으로 변경했어요. 이를 통해 컴포넌트 간 직접적인 의존성을 제거할 수 있었죠!
2. 다양한 화면에서 일관된 레이아웃은 어떻게 보장했나요?
문제 🤔: 디바이스 크기와 방향에 따라 컴포넌트 레이아웃이 일관되지 않는 문제가 있었어요.
해결 💡: 헤더-바디-푸터 구조의 명확한 규칙을 정의하고, 자동 레이아웃 제약조건을 표준화하여 모든 환경에서 일관된 모습을 보장할 수 있었답니다!
3. 개발자마다 상이한 구현 방식은 어떻게 통일했나요?
문제 🤔: 여러 개발자가 참여하며 컴포넌트 구현 방식과 스타일이 불일치했어요.
해결 💡: 컴포넌트 개발 가이드라인을 작성하고 코드 리뷰 프로세스를 강화해 일관된 구현을 보장했어요. Example 앱을 통한 시각적 참조 자료 제공으로 개발자 간 일관성을 유지할 수 있었답니다!
어떤 교훈을 얻었나요? 🧠
컴포넌트 기반 아키텍처와 ModalKit 개발을 통해 얻은 가장 큰 교훈은 "적절한 추상화와 명확한 경계 설정의 중요성"이에요.
컴포넌트의 책임 범위를 명확히 정의하고, 컨테이너와 컴포넌트 간의 상호작용 방식을 표준화함으로써 복잡한 UI 요소도 체계적으로 관리할 수 있게 되었어요.
앞으로 어떤 방향으로 발전시킬 계획인가요?
향후 발전 방향으로는:
- 더 다양한 컴포넌트 개발 🧩: 복잡한 인터랙션을 지원하는 특수 목적 컴포넌트로 확장할 계획이에요
- SwiftUI 통합 ⚡: 기존 UIKit 기반 컴포넌트와 SwiftUI의 원활한 통합을 준비하고 있어요
- 애니메이션 시스템 강화 ✨: 다양한 애니메이션 옵션과 전환 효과를 제공할 예정이에요
- 분석 기능 추가 📊: 사용자 인터랙션 데이터 수집 및 분석 기능을 계획하고 있어요
마치며 🎁
컴포넌트 기반 아키텍처와 이를 적용한 ModalKit은 단순한 UI 라이브러리를 넘어 개발 방식과 협업 문화를 변화시켰어요.
디자이너와 개발자가 공통의 언어로 소통하며, 일관된 사용자 경험을 효율적으로 구현할 수 있게 되었답니다.
이러한 컴포넌트 기반 아키텍처는 초기에는 설계와 구현에 더 많은 노력이 필요하지만, 장기적으로는 개발 생산성, 코드 품질, 협업 효율성을 크게 향상시키는 투자임을 확인할 수 있었어요.
"한 번 작성하고, 어디서나 사용한다"는 철학이 실현된 사례로, 앞으로도 이러한 접근 방식을 다른 UI 요소에도 확장 적용하여 더 효율적이고 일관된 개발 경험을 만들어 나갈 계획입니다! 😊