SwiftUI Study – FocusState 고급 활용: 입력 폼 흐름 제어·자동 포커싱·키보드 관리 패턴
1. 왜 중요한가 (문제 배경)
SwiftUI의 @FocusState는 UIKit의 becomeFirstResponder()를 대체하는 핵심 기능이지만, 실무에서는 다음 문제가 자주 발생한다.
- 여러 TextField 간 포커스 이동이 불안정
- 화면 전환 직후 포커스가 올라오지 않음
- 키보드가 닫혀도 FocusState 값은 true로 남아 UI와 불일치
- 조건부 렌더링(if) 때문에 포커스가 사라지거나 적용 실패
- 비동기 로직 이후 포커스 주기 실패
이는 FocusState의 렌더링 타이밍, 바인딩 구조, 입력 흐름 제어 방식을 정확히 이해하면 해결할 수 있다.
2. 잘못된 패턴 예시
❌ 예시 1: TextField가 렌더되기 전에 포커스 적용
@FocusState private var focused: Bool
if isVisible {
TextField("이름", text: $name)
.focused($focused)
.onAppear {
focused = true // ❌ 렌더 이전 호출 → 포커스 실패
}
}
문제점
- 조건부 렌더 상황에서 TextField가 생성되기도 전에 포커스 명령 실행
- SwiftUI는 이를 무시 → 포커스 실패
❌ 예시 2: 여러 TextField가 동일 FocusState 사용
@FocusState private var isFocused: Bool
TextField("A", text: $a).focused($isFocused)
TextField("B", text: $b).focused($isFocused) // ❌ 어느 필드가 활성인지 모름
문제점
- 어떤 필드를 선택해야 하는지 구분 불가
- 입력 흐름 제어가 어려워짐
❌ 예시 3: 키보드 내려가도 FocusState는 true
TextField("메모", text: $memo)
.focused($isFocused)
Button("완료") {
isFocused = false // ❌ 상태와 UI 타이밍이 맞지 않을 수 있음
}
문제점
- UI와 상태의 불일치 가능
- 애니메이션/비동기 전환 중 특히 빈번하게 나타나는 문제
❌ 예시 4: onSubmit 자동 이동 불안정
.onSubmit {
focusedField = .password // ❌ 조건부 렌더 + 비동기 업데이트 시 실패
}
3. 올바른 패턴 예시
✅ 예시 1: FocusState는 enum 기반이 가장 안전
enum Field: Hashable {
case name
case email
case password
}
@FocusState private var focusedField: Field?
적용:
TextField("이름", text: $name)
.focused($focusedField, equals: .name)
장점
- 어떤 필드인지 명확
- 입력 흐름 제어가 구조적으로 깔끔
✅ 예시 2: 렌더링 이후 포커스 안정 적용
.onAppear {
DispatchQueue.main.async {
focusedField = .name
}
}
또는 Swift concurrency:
.task {
try? await Task.sleep(nanoseconds: 50_000_000)
focusedField = .name
}
장점
- TextField가 실제 View 계층에 올라온 후 포커스 적용
- sheet·navigation push 환경에서도 안정적
✅ 예시 3: 자연스러운 자동 이동 구현
TextField("이메일", text: $email)
.focused($focusedField, equals: .email)
.submitLabel(.next)
.onSubmit {
focusedField = .password
}
장점
- UIKit 수준의 자연스러운 입력 플로우 구현
- 키보드 Return 키 자동 변경
✅ 예시 4: 키보드와 FocusState 타이밍 보정
Button("확인") {
focusedField = nil
Task {
try? await Task.sleep(nanoseconds: 10_000_000)
hideKeyboard() // UIViewController extension 활용
}
}
장점
- SwiftUI 상태 변경과 UIKit 키보드 dismiss 타이밍 불일치 해결
✅ 예시 5: 조건부 렌더링 환경에서 포커스 유지
.onChange(of: step) { newStep in
if newStep == .email {
DispatchQueue.main.async {
focusedField = .email
}
}
}
장점
- multi-step 입력 UI에서도 안정적 포커스 흐름 제공
4. 실전 적용 팁
✔ 팁 1
FocusState는 enum 기반이 가장 안정적이다.
✔ 팁 2
포커스는 렌더 후에 적용하는 습관을 들여라
→ DispatchQueue.main.async 또는 Task.sleep 사용.
✔ 팁 3
submitLabel(.next) + onSubmit 조합을 적극 활용하면
입력 흐름이 매우 자연스러워진다.
✔ 팁 4
조건부 렌더링(UI 전환)에는 반드시 delay 기반 포커스 적용을 사용해야 한다.
✔ 팁 5
키보드와 FocusState는 타이밍이 항상 100% 일치하지 않는다.
필요하면 UIKit 키보드 dismiss와 조합해 조정해야 한다.
5. 정리
- @FocusState는 SwiftUI 입력 UI를 구성할 때 필수적인 기술이다.
- 렌더 타이밍, enum 기반 포커스, 자동 이동 플로우, 키보드 타이밍 조절을 이해하면
SwiftUI에서도 UIKit 수준의 안정적 입력 UX를 구현할 수 있다. - 회원가입, 결제, 주소 입력 같은 복잡한 폼에서 특히 강력한 효과를 발휘한다.

