| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 파노라마 뷰
- completion handler
- React-Native
- React Native
- 앱 서명 키 인증서
- 네이티브
- 다가구 전세
- react
- 안드로이드
- 구조적 정체성
- 리액트
- 리액트 네이티브
- 명시적 정체성
- requirenativecomponent
- data driven construct
- 뷰 정체성
- native
- SwiftUI
- ssot
- HUG보증보험지사
- 뷰 생명주기
- @sendable
- panorama view
- ios
- Android
- presentationbackgroundinteraction
- 업로드 키 인증서
- launch screen
- 다가구 전세보증보험
- 앱 성능 개선
- Today
- Total
NEON의 이것저것
[SwiftUI] 단일 TextField로 여러 정보 연속적으로 입력하기 본문
1. 구현 목표
회원가입 플로우에서 사용자가 화면을 터치하지 않고도 키보드의 "다음" 버튼만으로 모든 정보를 순차적으로 입력할 수 있는 UX를 구현하고자 했습니다. 이 과정에서 일반 텍스트 입력, 그리고 비밀번호 입력이 모두 필요했기에, 평문 입력을 위한 TextField와 입력 내용을 점으로 표기해주는 SecureField 간의 자연스러운 전환이 핵심 요구사항이었습니다.
세부 목표
1. TextField와 SecureField 간 전환 시 키보드가 내려가지 않을 것
2. 입력 정보 종류에 따라 적절한 키보드 타입(.default, .asciiCapable, .numberPad)이 즉시 적용될 것
2. 시행착오 과정
1차 시도: 조건부 렌더링
if type.isSecure && isPasswordHidden {
SecureField(...)
.focused($isFocused)
} else {
TextField(...)
.focused($isFocused)
}

결과: ❌ 실패
첫 번째 시도에서는 조건부 렌더링을 통해 구현을 시도했습니다. 하지만 뷰가 교체되는 순간 FocusState 추적이 끊어져 키보드가 내려가는 현상이 발생했습니다. 이 과정에서 SwiftUI의 FocusState가 키보드 표시 여부와 직결되어 있지만, 세밀한 제어가 불가능한 한계를 확인할 수 있었습니다.
2차 시도: ZStack과 opacity 활용
두 번째 시도에서는 ZStack과 opacity를 활용하여 접근했습니다. 이 방식으로 키보드가 내려가지 않고 포커스 상태를 유지할 수는 있었지만, 키보드 타입이 변경되지 않는 새로운 문제가 발생했습니다. 원인을 분석해보니 뷰 자체의 재평가가 발생하지 않아 키보드 타입 갱신이 되지 않았던 것이었습니다.
ZStack(alignment: .leading) {
SecureField(...)
.opacity(type.isSecure && isPasswordHidden ? 1 : 0)
TextField(...)
.opacity(type.isSecure && isPasswordHidden ? 0 : 1)
}
.keyboardType(type.keyboardType)

결과: 부분 성공
✅ 키보드가 내려가지 않고 포커스 상태 유지
❌ 키보드 타입이 변경되지 않는 새로운 문제 발생
3차 시도: 명시적 뷰 정체성 부여
세 번째 시도에서는 뷰에 명시적 정체성을 부여하는 방식을 시도했습니다. 이 방식으로 대부분의 키보드 타입 전환에는 성공했으나, .default ↔ .asciiCapable 간 전환만 실패하는 현상이 있었습니다.
이 과정에서 .id 수정자의 위치가 .focused 수정자보다 선행되어야 안정적으로 동작하는 것을 확인했습니다.
// MARK: - MyTextField
HStack(alignment: .center) {
ZStack(alignment: .leading) {
SecureField(...)
.opacity(type.isSecure && isPasswordHidden ? 1 : 0)
TextField(...)
.opacity(type.isSecure && isPasswordHidden ? 0 : 1)
}
}
.keyboardType(type.keyboardType)
.id("\(type)") // focused 뷰 수정자 적용 이후 id값 적용
.focused($isFocused) // focused 뷰 수정자
위와 같이 만일 .focused 뷰 수정자 위에 명시적 정체성을 부여할 경우, focused 수정자가 적용된 후, 뷰의 ID가 변경됨에 따라 SwiftUI가 뷰를 새로 생성하는 것으로 판단해 isFocused 상태와의 연결이 끊깁니다.
때문에, 아래와 같이 명시적 정체성을 부여한 후에 뷰의 focus 상태 제어 뷰 수정자가 동작하도록 순서를 지정해야, Focus 상태 추적이 가능해집니다.
// MARK: - MyTextField
HStack(alignment: .center) {
ZStack(alignment: .leading) {
SecureField(...)
.opacity(type.isSecure && isPasswordHidden ? 1 : 0)
TextField(...)
.opacity(type.isSecure && isPasswordHidden ? 0 : 1)
}
}
.keyboardType(type.keyboardType)
.focused($isFocused) // focused 뷰 수정자
.id("\(type)") // focused 뷰 수정자 적용 이후 id값 적용
결과: 부분 성공
✅ 대부분의 키보드 타입 전환 성공
❌ .default ↔ .asciiCapable 간 전환만 실패
발견: .id 수정자의 위치가 .focused 수정자보다 밑에 위치해야 안정적으로 동작
최종 해결: 컴포넌트 내부 포커스 상태 관리
최종적으로는 컴포넌트 내부에서 포커스 상태를 직접 관리하는 방식을 채택했습니다. 이를 통해 컴포넌트 내부에서 포커스 대상을 명확히 지정할 수 있었고, 키보드 타입과 포커스 상태를 독립적으로 관리할 수 있게 되었습니다.
struct MyTextField: View {
@FocusState private var focusedField: Field?
var body: some View {
ZStack(alignment: .leading) {
SecureField("", text: $value, prompt: prompt)
.focused($focusedField, equals: .secure)
.opacity(type.isSecure && isPasswordHidden ? 1 : 0)
TextField("", text: $value, prompt: prompt)
.focused($focusedField, equals: .normal)
.opacity(type.isSecure && isPasswordHidden ? 0 : 1)
}
.autocapitalization(.none)
.keyboardType(type.keyboardType)
.onChange(of: isFocused) { newValue in
if newValue {
focusedField = type.isSecure ? .secure : .normal
}
}
}
}

결과: 성공
✅ 컴포넌트 내부에서 포커스 대상을 명확히 지정
✅ 키보드 타입과 포커스 상태를 독립적으로 관리 가능
배운 점
SwiftUI의 뷰 업데이트 메커니즘
이번 구현을 통해 SwiftUI의 뷰 업데이트 메커니즘에 대해 깊이 이해할 수 있었습니다. 단순한 opacity 변경만으로는 실질적인 뷰 재평가가 발생하지 않으며, 뷰의 정체성(id)을 변경함으로써 강제로 뷰 업데이트를 유도할 수 있다는 점을 발견했습니다.
뷰 수정자의 순서 중요성
SwiftUI에서 뷰 수정자의 순서가 실행 결과에 큰 영향을 미친다는 점도 배웠습니다. 특히 .id 수정자는 .focused 수정자보다 먼저 적용되어야 안정적으로 동작한다는 것을 확인했으며, 이를 통해 SwiftUI의 뷰 수정자가 선언 순서에 따라 적용된다는 중요한 특성을 다시 체감하게 되었습니다.
포커스 상태 관리 전략
마지막으로, 복잡한 입력 폼에서의 포커스 상태 관리 전략에 대해 배웠습니다. 컴포넌트 내부에서 포커스 상태를 관리하면 더 세밀한 제어가 가능하며, 외부 상태 변화를 감지하여 내부 상태를 적절히 조정하는 패턴이 효과적이라는 것을 확인했습니다.
결론
SwiftUI에서 복잡한 입력 폼을 구현할 때는 프레임워크의 특성을 이해하고 이를 고려한 설계가 필요합니다. 특히 포커스 상태와 키보드 타입 관리와 같은 세부적인 UX 요소들은 프레임워크의 제약 사항을 파악하고, 이를 우회하거나 보완하는 전략이 중요하다는 것을 이번 구현을 통해 배울 수 있었습니다.
'개발지식 정리 > Swift' 카테고리의 다른 글
| [FCM] iOS에서 Firebase 푸시알림 구현하기 (0) | 2025.06.24 |
|---|---|
| [SwiftUI] 화면별 제스처를 통한 뒤로가기 활성화 여부 제어하기 (1) | 2025.06.17 |
| [SwiftUI] Navigation Router 패턴: 복잡한 네비게이션 플로우 관리하기 (1) | 2025.06.11 |
| [SwiftUI] 바텀시트 외부영역의 어두워짐 효과 제거하기 (0) | 2025.06.04 |
| Swift Closure 정리 (0) | 2025.04.07 |