
👋 들어가기 전

회사 일이 조금 소강 상태에 들어, 사이드 프로젝트를 본격적으로 시작하려고한다.
우리는 SwiftUI를 사용하기로 결정했고, 혼자 SwiftUI를 사용하여, 커스텀 뷰를 만들때
고민한 점을 시작으로, 이번 사이드 프로젝트의 여정을 기록하려고한다.
🏁 학습할 내용
- 커스텀 뷰를 만들 때
- ~View
- ViewModifier
- View Style
- 비교
❓커스텀 뷰를 만들 때
SwiftUI를 통해 커스텀 뷰를 만드는 방법은 정말 많은 것 같다.
여기서 뷰를 만드는 의미는 실제 어떤 ~View 만드는 것이 아닌 UI적인 커스텀을 의미한다.
나는 대표적으로 3가지 방법을 알고있다.
- ~View 정의
- ViewModifier 정의
- View Style 정의
각 내용을 먼저 알아보고 마지막에 비교를 해보자.
🏭 ~View 정의
📌 개요
SwiftUI에서 가장 기본적인 커스텀 방식
View 프로토콜을 따라는 struct를 정의해서 새로운 뷰를 만든다.
struct MyButton: View {
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(.headline)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(12)
}
}
}
✅ 장점
- 재사용성과 조합성이 뛰어남내부에
- @Binding, @State, @Environment 등 사용 가능 → 상태 기반 UI 구현 가능
- 뷰 계층을 명확하게 분리할 수 있어 대규모 UI 구성에 유리
❌ 단점
- 뷰 계층이 깊어질 경우 오히려 코드가 분산되어 추적이 어려울 수 있음
- 단순한 스타일 변경에는 오버엔지니어링일 수 있음
🧑💻 사용 예시
- 상태를 가진 독립적인 UI 컴포넌트 (예: 카드 뷰, 커스텀 버튼, 셀 등)
- 뷰 계층을 분리하고자 할 때
- View 타입 전체를 추상화하고자 할 때
✍️ ViewModifier
반복적으로 적용하는 스타일이나 효과를 커스텀 modifier로 추출할 때 사용.
구현 후, View protocol의 extension의 함수 형태로 만들면, 조금 더 쉽게 사용할 수 있다.
import SwiftUI
struct EmphasizedTextModifier: ViewModifier {
func body(content: Content) -> some View {
content
.font(.system(size: 20, weight: .bold))
.foregroundColor(.orange)
.padding(8)
.background(Color.orange.opacity(0.1))
.cornerRadius(8)
}
}
// Modifier를 더 자연스럽게 쓰기 위한 확장
extension View {
func emphasized() -> some View {
self.modifier(EmphasizedTextModifier())
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Text("중요한 텍스트")
.emphasized()
Text("일반 텍스트")
}
.padding()
}
}
✅ 장점
- 기존 뷰에 체이닝 방식으로 적용 가능 → 선언적 스타일 유지
- 코드 중복 제거에 효과적
- 애플도 공식 프레임워크 내부에서 매우 많이 사용 (예: .buttonStyle, .frame 등도 내부적으로 ViewModifier 기반)
❌ 단점
- 여러 modifier가 중첩되면 구현 추적이 어려울 수 있음
🧑💻 사용 예시
- 버튼, 텍스트, 이미지 등에 스타일을 공통으로 적용할 때
- 특정 스타일을 추출
🪮 View Style
애플이 제공하는 스타일 프로토콜 (TextFieldStyle, ButtonStyle, ToggleStyle 등)을 구현하여
뷰 구성 요소의 전체적인 인터랙션과 외형을 완전히 바꾸는 방식.
import SwiftUI
struct CustomTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding(12)
.background(RoundedRectangle(cornerRadius: 8).stroke(Color.blue, lineWidth: 1))
.padding(.horizontal)
}
}
struct ContentView: View {
@State private var name: String = ""
var body: some View {
TextField("이름을 입력하세요", text: $name)
.textFieldStyle(CustomTextFieldStyle())
}
}
✅ 장점
- 시스템 뷰와의 일관성을 유지하면서 동작 방식까지 제어 가능
- 스타일끼리 쉽게 교체 가능 (다형성)
❌ 단점
- 적용 대상이 제한적 (예: TextField, Toggle, Button, ProgressView 등)
- 복잡한 UI 조합에는 부적합
🧑💻 사용 예시
- 앱 전역에서 일관된 스타일을 적용하고자 할 때
- 특정 컴포넌트 (Button, TextField, etc.)의 기본 동작 및 외형을 커스터마이징할 때
😀 비교
마지막으로 위 내용을 표로 정리해보자.
| 구분 | 사용 위치 | 재사용성 | 특징 | 대표 예시 |
| struct View | 전체 UI | ✅ 높음 | 컴포넌트 단위 분리 | 카드, 셀, 커스텀 뷰 |
| ViewModifier | 스타일 추출 | ✅ 중간 | 선언적 스타일 유지 | 폰트, 테두리, 그림자 |
| Style 프로토콜 | 시스템 뷰 스타일링 | ✅ 높음 | 동작 + 외형 변경 | TextFieldStyle |
출처
https://developer.apple.com/documentation/swiftui/view
View | Apple Developer Documentation
A type that represents part of your app’s user interface and provides modifiers that you use to configure views.
developer.apple.com
https://developer.apple.com/documentation/swiftui/viewmodifier
ViewModifier | Apple Developer Documentation
A modifier that you apply to a view or another view modifier, producing a different version of the original value.
developer.apple.com
https://developer.apple.com/documentation/swiftui/view-styles
View styles | Apple Developer Documentation
Apply built-in and custom appearances and behaviors to different types of views.
developer.apple.com
'iOS > SwiftUI' 카테고리의 다른 글
| .contentShape (0) | 2025.07.19 |
|---|---|
| 커스텀 SwipePopNavigationStack 구현하기 (6) | 2025.07.13 |
| @Observable 매크로 (0) | 2025.07.03 |
| Custom Carousel 만들기 (0) | 2025.03.16 |
| .scrollTargetLayout (0) | 2025.03.15 |