SwiftUI Study – 접근성(Accessibility)을 고려한 UI 설계: Label·Value·Hint·Actions를 활용해 앱 완성도 높이기
Dev Study/SwiftUI 2025. 12. 9. 10:43반응형
SwiftUI Study – 접근성(Accessibility)을 고려한 UI 설계: Label·Value·Hint·Actions를 활용해 앱 완성도 높이기
1. 왜 중요한가 (문제 배경)
SwiftUI는 기본적으로 접근성(VoiceOver 등)을 잘 지원하지만,
다음과 같은 상황에서는 명시적인 접근성 처리가 필요합니다.
- 커스텀 버튼·커스텀 토글 등 기본 위젯을 재구성한 경우
- 이미지, 아이콘 버튼 등 시각적으로만 의미가 있는 요소
- 복잡한 화면(카드, 리스트, 대시보드 등)의 요소 간 정보 구조를 정리할 필요가 있을 때
- UI는 예쁘지만 VoiceOver 사용 시 정보가 뒤엉키거나 잘 전달되지 않는 경우
접근성 처리를 잘하면 다음 장점이 있습니다.
- VoiceOver 사용자의 실제 사용성이 크게 향상
- 테스트 자동화에서 UI 요소 식별이 쉬워짐
- 국제 기준 WCAG 충족 → 서비스 품질 상승
- iOS 생태계에서 “올바른 앱”으로 평가받음
SwiftUI는 .accessibilityLabel, .accessibilityValue, .accessibilityHint,
그리고 .accessibilityAddTraits, .accessibilityActions 같은 강력한 API를 제공합니다.
2. 잘못된 패턴 예시
❌ 예시 1: 아이콘 버튼에 접근성 레이블이 없음
Button {
deleteItem()
} label: {
Image(systemName: "trash") // ❌ 시각적 의미만 있고 VoiceOver는 “휴지통 이미지”라고 읽음
}
문제점
- VoiceOver는 이 버튼이 “삭제” 버튼인지 알 수 없다
- 같은 화면에 아이콘 버튼이 여러 개 있으면 구분할 수 없음
❌ 예시 2: 카드 UI가 너무 많은 정보를 한 번에 읽음
struct WrongCard: View {
let item: Item
var body: some View {
VStack(alignment: .leading) {
Text(item.title)
Text("금액: \(item.amount)")
Text("날짜: \(item.dateString)")
}
}
}
문제점
- 카드 전체가 하나의 덩어리로 읽히며 구조가 불명확
- 제목, 금액, 날짜를 따로 탐색할 수 없어 사용성 저하
❌ 예시 3: 커스텀 토글을 만들었지만 접근성 동작을 제공하지 않음
struct CustomToggle: View {
@Binding var isOn: Bool
var body: some View {
Circle()
.fill(isOn ? .green : .gray) // ❌ VoiceOver는 단순한 원으로 인식
.onTapGesture {
isOn.toggle()
}
}
}
문제점
- VoiceOver 사용자는 이 UI가 “토글”이라는 사실을 모름
- 더블탭 제스처로 토글을 변경할 수 없음
- 상태(on/off)를 전달할 방법도 없음
3. 올바른 패턴 예시
✅ 예시 1: 아이콘 버튼에 명확한 레이블과 힌트 제공
Button {
deleteItem()
} label: {
Image(systemName: "trash")
}
.accessibilityLabel("항목 삭제")
.accessibilityHint("현재 선택된 항목을 삭제합니다")
장점
- VoiceOver는 “항목 삭제, 버튼”으로 읽음
- 기능이 명확하게 전달됨
✅ 예시 2: 카드 UI를 접근성 그룹으로 정돈
struct AccessibleCard: View {
let item: Item
var body: some View {
VStack(alignment: .leading) {
Text(item.title)
Text("금액: \(item.amount)원")
Text("날짜: \(item.dateString)")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(item.title), 금액 \(item.amount)원, 날짜 \(item.dateString)")
}
}
장점
- 카드 전체를 하나의 요소로 읽되, 구조적 문장으로 변환
- 리스트 탐색이 훨씬 자연스러움
✅ 예시 3: 커스텀 토글에 접근성 동작과 값 제공
struct AccessibleToggle: View {
@Binding var isOn: Bool
var body: some View {
Circle()
.fill(isOn ? .green : .gray)
.frame(width: 40, height: 40)
.accessibilityLabel("알림 설정")
.accessibilityValue(isOn ? "켜짐" : "꺼짐")
.accessibilityAddTraits(.isButton)
.accessibilityAction {
isOn.toggle() // VoiceOver 더블탭으로 토글 가능
}
.onTapGesture {
isOn.toggle()
}
}
}
장점
- VoiceOver가 “알림 설정, 켜짐/꺼짐, 버튼”으로 읽어줌
- 접근성 사용자도 동일한 기능을 수행 가능
- 커스텀 UI도 기본 토글처럼 동작
✅ 예시 4: Dynamic Type(폰트 크기 확대) 대응
Text("설정")
.font(.title2)
.dynamicTypeSize(..<DynamicTypeSize.accessibility3)
장점
- 일반 사용자와 접근성 사용자 모두 가독성 향상
- UI가 깨지지 않도록 확대 제한을 둘 수도 있음
4. 실전 적용 팁
✔ 팁 1 – 아이콘 버튼에는 반드시 Label을 붙인다
UI가 예뻐도 VoiceOver는 “이미지”라고만 읽는다.
✔ 팁 2 – 커스텀 UI는 ‘Accessibility Action’을 직접 지정
더블탭으로 실행 가능한 행동을 제공해야 한다.
✔ 팁 3 – 카드형 UI는 “combine” 또는 “ignore” 를 적절히 사용
정보가 너무 많으면 combine으로 요약해서 읽히도록 조정.
✔ 팁 4 – Dynamic Type 대응은 접근성의 핵심
폰트 크기를 제한하지 말고 UI가 유연하도록 설계.
✔ 팁 5 – 프리뷰에서 VoiceOver 시뮬레이션
Xcode Accessibility Inspector 또는 iOS 기능으로 확인 가능.
5. 정리
- SwiftUI는 접근성 API를 풍부하게 제공하지만,
커스텀 UI에서는 반드시 명시적으로 Label·Value·Hint·Actions를 설정해야 한다. - 접근성을 설계하면 시각장애인을 위한 기능뿐만 아니라,
테스트 자동화·국제화·UI 구조 정리까지 자연스럽게 개선된다. - 앱의 완성도는 “보이기만 예쁜 UI”가 아니라
“모든 사용자가 사용할 수 있는 UI”에서 결정된다.
반응형

