재사용 가능한 SwiftUI 컴포넌트 라이브러리 구축

발행: (2025년 12월 2일 오전 09:37 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

SwiftUI는 UI를 쉽게 만들 수 있게 해 주지만, 전체 앱에서 일관된 모습을 유지하는 재사용 가능한 컴포넌트를 만드는 것은 또 다른 도전 과제입니다.

앱이 성장함에 따라 UI 중복이 실제 문제로 떠오릅니다:

  • 반복되는 버튼 스타일
  • 일관되지 않은 카드 형태
  • 중복된 텍스트 모디파이어
  • 복사‑붙여넣기 그림자
  • 동일 레이아웃의 여러 버전

컴포넌트 라이브러리가 이 모든 문제를 해결합니다.

오늘은 다음을 포함하는 현대적이고 확장 가능한 Apple‑스타일 SwiftUI 컴포넌트 라이브러리를 만드는 방법을 배웁니다:

  • 버튼
  • 카드
  • 텍스트 필드
  • 플로팅 패널
  • 디자인 토큰(색상, 라디우스, 그림자)
  • 재사용 가능한 모디파이어
  • 일관된 스타일링

이것은 제가 실제 프로덕션 앱에서 사용하는 정확한 구조입니다. 함께 만들어 봅시다. 🚀

1. Design 폴더부터 시작하기

Design/
├── Colors.swift
├── Radii.swift
├── Shadows.swift
└── Typography.swift

Colors.swift

enum AppColor {
    static let primary = Color.blue
    static let background = Color(.systemBackground)
    static let glassStroke = Color.white.opacity(0.25)
}

Radii.swift

enum AppRadius {
    static let small: CGFloat = 10
    static let medium: CGFloat = 16
    static let large: CGFloat = 22
}

Shadows.swift

enum AppShadow {
    static let card = Color.black.opacity(0.18)
    static let glow = Color.blue.opacity(0.3)
}

Typography.swift

enum AppFont {
    static let title = Font.system(.title3, design: .rounded).bold()
    static let body = Font.system(.body, design: .rounded)
}

한 곳에서 관리 → 전체 앱 일관성 확보.

2. 재사용 가능한 버튼 스타일

PrimaryButton

struct PrimaryButton: View {
    let title: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(AppFont.body.bold())
                .padding(.horizontal, 28)
                .padding(.vertical, 14)
                .background(AppColor.primary)
                .foregroundColor(.white)
                .clipShape(RoundedRectangle(cornerRadius: AppRadius.medium, style: .continuous))
                .shadow(color: AppShadow.card, radius: 16, y: 8)
        }
    }
}

사용 예시

PrimaryButton(title: "Continue") {
    print("Pressed")
}

3. Glass Card 컴포넌트 (재사용 가능)

struct GlassCard: View {
    @ViewBuilder let content: () -> Content

    var body: some View {
        content()
            .padding(20)
            .background(.ultraThinMaterial)
            .clipShape(RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous))
            .overlay(
                RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous)
                    .stroke(AppColor.glassStroke, lineWidth: 1)
            )
            .shadow(color: AppShadow.card, radius: 24, y: 12)
    }
}

사용 예시

GlassCard {
    VStack(alignment: .leading) {
        Text("Glass Card")
            .font(AppFont.title)
        Text("Reusable glassmorphic component.")
            .foregroundColor(.secondary)
    }
}

4. TextField 컴포넌트 (모던하고 깔끔)

struct AppTextField: View {
    var title: String
    @Binding var text: String

    var body: some View {
        TextField(title, text: $text)
            .padding(.horizontal, 14)
            .padding(.vertical, 12)
            .background(.ultraThinMaterial)
            .clipShape(RoundedRectangle(cornerRadius: AppRadius.medium))
            .overlay(
                RoundedRectangle(cornerRadius: AppRadius.medium)
                    .stroke(AppColor.glassStroke)
            )
            .shadow(color: AppShadow.card.opacity(0.25), radius: 12, y: 6)
    }
}

사용 예시

@State private var name = ""

AppTextField(title: "Name", text: $name)

5. Chips / Tags

struct Chip: View {
    let label: String
    let icon: String?

    var body: some View {
        HStack(spacing: 6) {
            if let icon { Image(systemName: icon) }
            Text(label)
        }
        .font(.caption)
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
        .background(.ultraThinMaterial)
        .clipShape(Capsule())
        .overlay(Capsule().stroke(AppColor.glassStroke))
    }
}

예시

HStack {
    Chip(label: "SwiftUI", icon: "swift")
    Chip(label: "Design", icon: "paintbrush")
}

6. Floating Panel (재사용 가능한 Bottom Sheet 상단 섹션)

struct FloatingPanel: View {
    @ViewBuilder let content: () -> Content

    var body: some View {
        VStack(spacing: 0) {
            Capsule()
                .fill(AppColor.glassStroke)
                .frame(width: 40, height: 6)
                .padding(.top, 10)

            content()
                .padding()
        }
        .background(.regularMaterial)
        .clipShape(RoundedRectangle(cornerRadius: AppRadius.large, style: .continuous))
        .shadow(color: .black.opacity(0.25), radius: 30, y: 14)
    }
}

7. 재사용 가능한 모디파이어 (강력한 도구!)

struct CardPadding: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding(20)
            .clipShape(RoundedRectangle(cornerRadius: AppRadius.large))
    }
}

extension View {
    func cardPadding() -> some View {
        modifier(CardPadding())
    }
}

모디파이어는 뷰를 깔끔하고 선언적으로 유지해 줍니다.

8. 권장 폴더 구조

Design/
│  Colors.swift
│  Radii.swift
│  Shadows.swift
│  Typography.swift

Components/
│  Buttons/
│  Cards/
│  TextFields/
│  Panels/
│  Chips/
│  Modifiers/

모든 것이 깔끔하고, 모든 것이 확장 가능합니다.

마무리 생각

SwiftUI 컴포넌트 라이브러리를 사용하면 다음을 얻을 수 있습니다:

  • 일관된 디자인
  • 빠른 반복 작업
  • 재사용 가능한 빌딩 블록
  • 확장 가능한 아키텍처
  • 팀 온보딩 용이
  • 더 다듬어진 UI

이것이 “스크린을 만들기”에서 “시스템을 만들기”로 전환하는 방법입니다.

Back to Blog

관련 글

더 보기 »