SwiftUI에서 Atomic Design 적용하기

JaeEun Lee·2024년 11월 3일

SwiftUI & Jetpack compose

목록 보기
8/10

SwiftUI에서 Atomic Design을 적용할 때 실제 컴포넌트 구조를 어떻게 나눌지와 코드 스타일에 중점을 두고 설명하겠습니다.

Atomic Design 적용기준

Atomic Design을 SwiftUI에 적용할 때는 아래의 기준을 고려했습니다.

  • 단일 책임 원칙에 따라 분리
    • 각 뷰는 하나의 역할만 담당하도록 설계하여 가독성을 높입니다.
  • 파일 단위로 분리할 시점
    • 재사용 가능성이 높거나 코드가 50줄 이상일 때는 별도의 파일로 분리하여 관리합니다.
  • private 접근 제어자 사용
    • 특정 뷰 파일 내에서만 사용할 경우 private으로 제한하여 코드의 의도를 명확히 합니다.
  • 중첩이 깊어질 때 분리
    • 중첩 구조가 3단계 이상 깊어질 경우 별도의 컴포넌트로 분리하여 가독성을 개선합니다.
  • ViewModel을 사용해 로직과 상태 관리 분리
    • 뷰가 단순히 UI 역할만 하고 상태와 로직은 ViewModel로 관리합니다.

Atoms

Atoms는 SwiftUI에서 가장 작은 UI 구성 요소로 단일 역할을 수행하고 독립적으로 사용할 수 있는 컴포넌트입니다. 예를 들어 색상이나 크기 스타일이 적용된 Text, Button 등을 독립된 컴포넌트로 정의합니다. Atoms는 앱 전체에서 재사용될 가능성이 높기 때문에 일관된 스타일과 기능을 적용하기 좋습니다.

import SwiftUI

// 버튼 Atom
struct PrimaryButton: View {
    let title: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(8)
        }
    }
}

// 아이콘 Atom
struct Icon: View {
    let systemName: String
    var size: CGFloat = 24

    var body: some View {
        Image(systemName: systemName)
            .resizable()
            .frame(width: size, height: size)
    }
}

Molecules

Molecules는 Atoms를 조합하여 간단한 기능을 구현하는 컴포넌트입니다. SwiftUI에서 Molecules는 하나의 목적을 가진 UI 요소로 UI의 일부 기능을 수행하는 작은 컴포넌트로 만듭니다. 여기서는 Atoms를 조합하여 특정한 형태의 뷰를 구성합니다.

// 텍스트와 아이콘을 조합한 Molecule
struct LabelWithIcon: View {
    let text: String
    let iconName: String

    var body: some View {
        HStack {
            Icon(systemName: iconName)
            Text(text)
                .font(.headline)
        }
    }
}

// 입력 필드 Molecule
struct InputField: View {
    @Binding var text: String
    let placeholder: String

    var body: some View {
        TextField(placeholder, text: $text)
            .padding()
            .background(Color.gray.opacity(0.2))
            .cornerRadius(8)
    }
}

Organisms

Organisms는 여러 Molecules와 Atoms를 결합하여 특정 기능을 완성하는 컴포넌트입니다. SwiftUI에서는 Organisms가 실질적으로 UI 기능을 담당하므로, 독립적인 로직을 가지고 상호작용을 수행하는 컴포넌트가 됩니다. Organisms는 보통 하나의 기능 단위를 수행하며 자주 사용되는 기능들을 그룹화합니다.

// 검색창 Organism
struct SearchBar: View {
    @State private var searchText = ""

    var body: some View {
        VStack {
            InputField(text: $searchText, placeholder: "Search")
            PrimaryButton(title: "Search") {
                print("Search action with: \(searchText)")
            }
        }
        .padding()
    }
}

// 프로필 카드 Organism
struct ProfileCard: View {
    let name: String
    let jobTitle: String

    var body: some View {
        VStack {
            Icon(systemName: "person.circle.fill")
                .frame(width: 64, height: 64)
                .padding(.bottom, 8)
            Text(name)
                .font(.title2)
            Text(jobTitle)
                .font(.subheadline)
                .foregroundColor(.gray)
        }
        .padding()
        .background(Color.white)
        .cornerRadius(10)
        .shadow(radius: 5)
    }
}

Templates

Templates는 Atoms, Molecules, Organisms를 조합하여 화면의 전체 레이아웃을 구성합니다. SwiftUI에서는 Templates를 통해 기능을 구현하지 않고 배치와 레이아웃만을 설정합니다. 예를 들어 로그인 화면, 사용자 프로필 화면 등의 레이아웃을 잡을 때 사용합니다.

// 프로필 화면 레이아웃 템플릿
struct ProfileTemplate: View {
    let name: String
    let jobTitle: String

    var body: some View {
        VStack(spacing: 20) {
            ProfileCard(name: name, jobTitle: jobTitle)
            SearchBar()
        }
        .padding()
        .background(Color.gray.opacity(0.1))
    }
}

Pages

Pages는 Templates에 실제 데이터를 주입하여 최종 화면을 구성하는 단계입니다. SwiftUI에서는 최종 사용자가 보게 될 화면을 Pages에서 구현하며, ViewModel을 주입하거나 데이터 로딩 로직을 구현합니다. 이 단계에서 API 호출이나 로컬 데이터베이스에서 데이터를 가져와서 화면에 반영할 수 있습니다.

// ViewModel 정의
class ProfileViewModel: ObservableObject {
    @Published var name: String = "John Doe"
    @Published var jobTitle: String = "iOS Developer"

    func loadProfile() {
        // 프로필 데이터 로드 로직
    }
}

// 최종 프로필 페이지
struct ProfilePage: View {
    @StateObject private var viewModel = ProfileViewModel()

    var body: some View {
        ProfileTemplate(name: viewModel.name, jobTitle: viewModel.jobTitle)
            .onAppear {
                viewModel.loadProfile()
            }
    }
}

SwiftUI와 AtomicDesign 코드스타일

  • 최대한 재사용 가능한 컴포넌트를 만든다
  • 뷰 계층을 단순화한다
  • ViewModel은 Pages 단계에서 주입한다
  • SwiftUI 미리보기 기능을 활용하여 빠르게 테스트 한다.(각 단계별 컴포넌트를 미리보기해 본다.)

마무리

이렇게 Atomic Design을 SwiftUI에 적용하면 구조가 명확하고 재사용 가능한 컴포넌트로 앱을 구성할 수 있습니다. Atoms부터 Pages까지 각 단계를 명확히 구분하여 앱의 확장성과 유지보수성을 높일 수 있습니다.

profile
공업철학프로그래머

0개의 댓글