@FocusState — SwiftUI에서 포커스 및 키보드 관리

저자: IT Sectr 게시일: 2026-06-26 읽는 시간: 9 분

@FocusState는 iOS 15에서 도입된 SwiftUI의 property wrapper로, 텍스트 필드 및 기타 요소에서 입력 포커스를 프로그래밍 방식으로 제어할 수 있게 해줍니다. 이 기능이 도입되기 전에는 개발자가 becomeFirstResponder 및 resignFirstResponder 같은 UIKit 메서드에 접근하기 위해 UIViewRepresentable을 사용해야 했습니다. @FocusState는 이 문제를 네이티브하게 해결합니다. .focused() 수정자를 통해 속성을 필드에 바인딩하면, 포커스 설정 또는 해제는 간단한 값 할당으로 이루어집니다. Apple Developer Documentation — FocusState (2025)에 따르면, @FocusState는 두 가지 모드를 지원합니다. 간단한 관리를 위한 Bool(포커스 켜짐 또는 꺼짐)과 여러 필드를 위한 enum으로, 각 케이스는 특정 입력 필드에 해당합니다.

핵심 사항

  • @FocusState — SwiftUI에서 프로그래밍 방식 포커스 관리를 위한 property wrapper, iOS 15부터 사용 가능.
  • Bool 모드 — 단일 필드의 경우 .focused($isFocused)와 함께 @FocusState var isFocused: Bool을 사용.
  • Enum 모드 — 여러 필드의 경우 FocusStateValue 프로토콜을 준수하는 enum과 .focused($field, equals: .fieldName)을 사용.
  • 키보드 숨기기 — 포커스를 nil 또는 false로 설정하여 키보드를 숨깁니다.
  • 자동 포커스 — 화면 열릴 때 키보드를 표시하려면 .onAppear에서 초기 값을 설정.

SwiftUI에서 @FocusState란

@FocusState는 SwiftUI에서 포커스 상태를 특정 입력 필드 또는 기타 포커스 가능한 요소에 바인딩하는 property wrapper입니다. becomeFirstResponder 및 resignFirstResponder 메서드를 통해 포커스 관리가 이루어지는 UIKit과 달리, SwiftUI는 선언적 접근 방식을 사용합니다. 상태(@FocusState)를 선언하고 .focused() 수정자를 통해 요소에 바인딩합니다. 상태를 변경하면 자동으로 포커스가 변경됩니다.

iOS 15에서 @FocusState가 도입되기 전에는 개발자가 UITextField 주변에 UIViewRepresentable 래퍼를 만들거나 타사 라이브러리를 사용해야 했습니다. @FocusState는 SwiftUI에 직접 통합되어 있으며 TextField, TextEditor, SecureField 및 SearchField와 함께 작동합니다. 이는 코드를 더 깔끔하게 만들고 UIKit 브리지 수를 줄이며 테스트 용이성을 향상시킵니다.

WWDC Session 10136 — What's new in SwiftUI (2024)에 따르면, @FocusState는 SwiftUI의 preference key 시스템을 사용하여 요소 간에 포커스 정보를 전달합니다. 필드가 포커스를 받으면 SwiftUI가 연결된 @FocusState 속성을 자동으로 업데이트하여 코드에서 포커스 변경에 반응할 수 있게 합니다.

Bool로 포커스 관리

@FocusState를 사용하는 가장 간단한 방법은 Bool 타입을 사용하는 것입니다. 필드가 포커스되면 속성이 true가 됩니다. 포커스를 잃으면 false가 됩니다. true로 설정하여 포커스를 강제하거나 false로 설정하여 포커스를 해제할 수 있습니다.

swift
struct LoginForm: View {
    @State var email = ""
    @FocusState var isEmailFocused: Bool
    
    var body: some View {
        VStack {
            TextField("Email", text: $email)
                .focused($isEmailFocused)
            
            Button("키보드 표시") {
                isEmailFocused = true
            }
            Button("키보드 숨기기") {
                isEmailFocused = false
            }
        }
    }
}

이 예제에서 isEmailFocused는 사용자가 텍스트 필드를 탭하면 자동으로 true가 되고, 키보드가 숨겨지면 false가 됩니다. 버튼을 사용하면 프로그래밍 방식으로 포커스를 관리할 수 있습니다. 사용자 정의 키보드, "다음" 버튼 및 폼 제출 후 키보드를 강제로 숨겨야 하는 상황에 유용합니다.

여러 필드의 Enum으로 포커스 관리

여러 필드가 있는 폼의 경우 @FocusStateFocusStateValue(또는 Hashable) 프로토콜을 준수하는 enum을 지원합니다. 각 enum 케이스는 특정 필드에 해당합니다. 이를 통해 필드 간에 포커스를 전환할 수 있습니다. 예를 들어, 사용자가 키보드에서 "다음"을 눌러 다음 필드로 이동하는 경우입니다.

swift
struct RegistrationForm: View {
    enum Field: Hashable {
        case email
        case password
        case confirmPassword
    }
    
    @State var email = ""
    @State var password = ""
    @State var confirmPassword = ""
    @FocusState var focusedField: Field?
    
    var body: some View {
        Form {
            TextField("Email", text: $email)
                .focused($focusedField, equals: .email)
                .onSubmit { focusedField = .password }
            
            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)
                .onSubmit { focusedField = .confirmPassword }
            
            SecureField("Confirm", text: $confirmPassword)
                .focused($focusedField, equals: .confirmPassword)
                .onSubmit { submitForm() }
        }
    }
}

.onSubmit 수정자에 주목하세요. 사용자가 키보드에서 "Return"을 누를 때 호출됩니다. .onSubmit 내부에서 focusedField를 다음 필드로 전환하여 자동으로 포커스를 이동합니다. 마지막 필드는 submitForm()을 호출하여 폼을 제출합니다.

키보드 표시 및 숨기기

@FocusState는 키보드를 숨기는 간단한 방법을 제공합니다. 속성을 nil(enum의 경우) 또는 false(Bool의 경우)로 설정하면 됩니다. 그러나 특정 필드에 바인딩하지 않고 키보드를 숨겨야 하는 경우도 있습니다. 예를 들어, 빈 공간을 탭할 때입니다. 이 경우 여러 가지 접근 방식이 있습니다.

swift
struct DismissKeyboardView: View {
    @State var text = ""
    @FocusState var isFocused: Bool
    
    var body: some View {
        TextField("Enter text", text: $text)
            .focused($isFocused)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("완료") {
                        isFocused = false
                    }
                }
            }
    }
}

placement .keyboard와 함께 .toolbar 수정자는 키보드 위에 버튼을 추가합니다. 이는 iOS에서 키보드를 숨기는 표준 UX 패턴입니다. 대안적인 접근 방식은 루트 VStack에 .onTapGesture를 사용하여 배경을 탭할 때 포커스를 재설정하는 것입니다.

포커스와 폼 유효성 검사

@FocusState는 폼 유효성 검사와 완벽하게 결합됩니다. 일반적인 패턴: "제출" 버튼을 누른 후 모든 필드를 검증하고 오류가 있는 첫 번째 필드에 포커스를 설정합니다. 이는 사용자 경험을 향상시킵니다. 사용자는 어떤 필드를 수정해야 하는지 즉시 확인할 수 있습니다.

swift
struct ValidatedForm: View {
    enum Field: Hashable { case name; case phone }
    
    @State var name = ""
    @State var phone = ""
    @FocusState var focusedField: Field?
    @State var errors: [String] = []
    
    var body: some View {
        Form {
            TextField("Name", text: $name)
                .focused($focusedField, equals: .name)
            TextField("Phone", text: $phone)
                .focused($focusedField, equals: .phone)
            
            Button("제출") { validateAndSubmit() }
        }
    }
    
    func validateAndSubmit() {
        if name.isEmpty {
            focusedField = .name
            return
        }
        if phone.isEmpty {
            focusedField = .phone
            return
        }
        // 폼 제출
    }
}

이 예제에서 name 필드가 비어 있으면 포커스가 해당 필드로 이동하고, 사용자는 오류가 어디 있는지 즉시 확인할 수 있습니다. name이 채워져 있으면 phone이 확인됩니다. 이는 폼의 자연스러운 동작입니다. 사용자는 위에서 아래로 필드를 채우고 유효성 검사도 동일한 순서를 따릅니다.

@FocusState 관련 일반적인 실수

가장 흔한 실수는 Hashable을 준수하지 않는 타입과 함께 @FocusState를 사용하려는 것입니다. @FocusState는 속성 타입이 Hashable이어야 합니다(Bool 및 옵셔널 enum은 이미 준수함). 사용자 정의 구조체를 사용하려는 경우 Hashable을 구현하는지 확인하세요.

  • .focused() 수정자 누락 — @FocusState 자체로는 포커스를 관리하지 않습니다. .focused($property) 또는 .focused($property, equals: .case)를 통해 필드에 바인딩해야 합니다.
  • 하나의 View에 여러 @FocusState — 여러 필드의 경우 여러 @FocusState 속성 대신 enum과 함께 하나의 @FocusState를 사용하세요. 여러 Bool 속성은 서로 동기화되지 않습니다.
  • 메인 스레드 외부에서 @FocusState 변경 — @FocusState는 SwiftUI의 모든 UI 속성과 마찬가지로 메인 스레드에서만 변경해야 합니다. 비동기 작업은 변경하기 전에 MainActor로 전환해야 합니다.
  • View 재구축 시 포커스 재설정 — View가 재구축되면 @FocusState가 재설정될 수 있습니다. 안정적인 View 식별을 위해 .id() 수정자를 사용하세요.
swift
// ❌ 오류: enum 대신 두 개의 @FocusState Bool
@FocusState var isNameFocused: Bool
@FocusState var isEmailFocused: Bool

// ✅ 정답: 단일 enum @FocusState
enum Field: Hashable { case name; case email }
@FocusState var focusedField: Field?

자주 묻는 질문

@FocusState는 어떤 iOS 버전부터 사용할 수 있나요?

@FocusState는 iOS 15, iPadOS 15, macOS 12, tvOS 15 및 watchOS 8부터 사용할 수 있습니다. iOS 14 이하를 지원하는 프로젝트의 경우 UITextField 및 becomeFirstResponder와 함께 UIViewRepresentable을 사용하거나, 사용자 정의 포커스 관리 구현이 있는 타사 라이브러리를 사용하세요.

사용자 정의 UIViewRepresentable과 함께 @FocusState를 사용할 수 있나요?

네, 사용자 정의 UIViewRepresentable에서 UIViewRepresentable 프로토콜을 통해 FocusState 지원을 구현해야 합니다. 사용자 정의 View에는 becomeFirstResponder와 resignFirstResponder가 있어야 합니다. .focused() 수정자를 지정하면 SwiftUI가 @FocusState를 이러한 메서드에 자동으로 연결합니다.

List에서 TextField와 함께 @FocusState가 작동하지 않는 이유는 무엇인가요?

List 또는 Form에서 셀이 재사용될 수 있으며, 이로 인해 필드와의 @FocusState 바인딩이 깨집니다. 해결 방법: 각 TextField에 고유 식별자와 함께 .id() 수정자를 추가하세요. 예: .id(fieldName). 이렇게 하면 SwiftUI가 각 필드에 대해 별도의 View 인스턴스를 생성합니다.

빈 공간을 탭할 때 키보드를 어떻게 숨기나요?

루트 컨테이너(VStack, ZStack)에 .onTapGesture를 추가하고 포커스를 재설정합니다: focusedField = nil. 그러나 .onTapGesture는 내부 버튼 탭을 차단할 수 있습니다. .contentShape(Rectangle())이 있는 컨테이너와 그 위에 .onTapGesture를 사용하거나, 사용자 정의 UIKitBackgroundView를 사용하세요.

@FocusState로 키보드 등장을 애니메이션화하는 방법은?

@FocusState는 키보드 애니메이션을 위한 직접적인 API를 제공하지 않습니다. 이는 iOS 시스템 동작입니다. 그러나 .onChange(of: focusedField) 또는 .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification))을 사용하여 사용자 정의 콘텐츠 애니메이션을 위해 포커스 변경에 반응할 수 있습니다.

요약

  • @FocusState — 입력 포커스 관리를 위한 네이티브 SwiftUI property wrapper, iOS 15부터 사용 가능.
  • 두 가지 모드 — 단일 필드용 Bool, 여러 폼 필드용 Hashable enum.
  • .focused() 수정자 — @FocusState를 특정 입력 필드에 바인딩하는 데 필수.
  • 프로그래밍 방식 제어 — 값을 nil 또는 false로 설정하면 키보드가 숨겨집니다.
  • 폼 유효성 검사 — @FocusState를 사용하면 검증 후 오류가 있는 첫 번째 필드에 포커스를 설정할 수 있습니다.
  • 여러 필드용 Enum — 여러 Bool 속성보다 enum이 있는 단일 @FocusState가 더 좋습니다.
  • iOS 15+ — 이전 버전의 경우 becomeFirstResponder와 함께 UIViewRepresentable을 사용하세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기