안녕하세요! 피피아노입니다 🎵
이번 포스팅에서는 제가 공부용으로 진행하고 있는 일기앱 프로젝트에서 Face ID 잠금 기능을 도입해봤는데 도입 과정을 정리해보려고 합니다.
그럼 바로 시작하겠습니다!
Face ID 기능 설계 개요
앱 실행 시 사용자의 얼굴 인증을 통해 잠금을 해제하고, 인증 성공 시에만 홈 화면(HomeView)으로 진입할 수 있도록 구현하였습니다.
구조는 아래처럼 구현하였습니다.
앱 실행 → 잠금화면(Face ID 요청) → 인증 성공 → HomeView 진입
↘ 인증 실패 시 에러 표시
이 기능을 구현하기 위해서 크게 3가지를 구현했습니다.
- Face ID 인증 로직을 담은 AuthViewModel
- 인증 인터페이스를 담당하는 LockScreenView
- 앱 진입 지점인 JournalApp.swift에서 인증 상태에 따른 뷰 전환 처리
LocalAuthentication 프레임워크
iOS 앱에서 Face ID를 구현하려면 LocalAuthentication 프레임워크를 사용해야 합니다. 이 프레임워크는 Face ID 및 Touch ID를 사용하여 사용자의 생체 정보를 인증하는 기능을 제공하는 프레임워크입니다.
https://developer.apple.com/documentation/localauthentication
Local Authentication | Apple Developer Documentation
Authenticate users biometrically or with a passphrase they already know.
developer.apple.com
LocalAuthentication 프레임워크의 주요 클래스는 LAContext이고 핵심 메서드는 canEvaluatePolicy(_:error:) 와 evaluatePolicy(_:localizedReason:reply:)입니다.
- canEvaluatePolicy(_:error:): 생체 인증 사용 가능 여부 확인
- evaluatePolicy(_:localizedReason:reply:): Face ID 인증 요청
LAContext는 생체 인증 및 기기 패스코드를 통한 사용자 인증을 위한 컨텍스트 객체입니다.
evaluatePolicy(_:localizedReason:reply:) 메서드는 시스템 인증 창을 띄워 사용자 인증을 수행하며, 인증 성공 여부는 클로저의 Bool 값으로 전달된다. 인증 UI는 시스템이 제공하며, 사용자가 명확하게 인증 사유를 알 수 있도록 localizedReason 설명을 반드시 제공해야 합니다.
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Face ID를 사용해 앱을 잠금 해제합니다.") { success, error in
if success {
// 인증 성공
} else {
// 인증 실패
}
}
}
iOS 11 이상부터 Face ID를, iOS 8 이상부터 Touch ID를 사용할 수 있습니다.
AuthViewModel - Face ID 인증 로직
Face ID 인증은 LocalAuthentication 프레임워크의 LAContext를 활용해서 구현하였습니다.
import Foundation
import LocalAuthentication
class AuthViewModel: ObservableObject {
@Published var isUnlocked = false
@Published var authError: String?
func authenticate() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "앱 잠금 해제를 위해 Face ID를 사용합니다.") { success, error in
DispatchQueue.main.async {
self.isUnlocked = success
self.authError = success ? nil : error?.localizedDescription
}
}
} else {
DispatchQueue.main.async {
self.authError = "Face ID를 사용할 수 없습니다."
}
}
}
}
이 코드에서 isUnlocked는 인증 성공 여부를 나타내는 상태 값이고 authError는 인증 실패 메시지를 UI에 표시하기 위한 문자열입니다.
LockScreenView - Face ID UI 구성
struct LockScreenView: View {
@ObservedObject var authVM: AuthViewModel
var body: some View {
VStack(spacing: 20) {
Image(systemName: "faceid")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.blue)
Text("Face ID로 잠금 해제")
.font(.title2)
if let error = authVM.authError {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
Button("잠금 해제 시도") {
authVM.authenticate()
}
.buttonStyle(.borderedProminent)
}
.onAppear {
authVM.authenticate()
}
}
}
앱 실행 시 자동으로 Face ID 인증을 시도하고, 실패 시 버튼을 눌러서 재시도할 수 있도록 UI를 구성하였습니다.
JournalApp.swift – 인증 상태에 따른 화면 전환
struct JournalApp: App {
@StateObject private var authVM = AuthViewModel()
var sharedModelContainer: ModelContainer = {
let schema = Schema([
JournalEntry.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
if authVM.isUnlocked {
HomeView()
} else {
LockScreenView(authVM: authVM)
}
}
.modelContainer(sharedModelContainer)
}
}
앱의 진입점에서 authVM.isUnlocked 상태를 기준으로 HomeView() 또는 LockScreenView()를 표시하도록 구성하였습니다.
Info.plist 설정 추가
Face ID를 사용하기 위해 다음 항목을 Info.plist에 추가해야 합니다.
<key>NSFaceIDUsageDescription</key>
<string>앱 잠금 해제를 위해 Face ID를 사용합니다.</string>
코드를 제대로 다 구현해도 해당 항목이 없으면 앱이 인증을 요청할 때 크래시가 발생할 수 있으니 꼭 추가를 해줘야 합니다!
마무리
SwiftUI와 LocalAuthentication을 활용하면 비교적 간단한 구조로 Face ID 인증을 구현할 수 있었습니다.
하지만 단순히 기능이 된다고 끝내기보다, 실패 시 재시도 UX나, 설정에서 잠금 기능을 끌 수 있도록 하는 유연한 확장성까지 고려하고 싶다는 생각이 들었습니다.
이번 경험은 기능 하나를 구현하는 데 그치지 않고,
앱의 신뢰도를 높이고 사용자 입장에서 어떤 인증 흐름이 좋은지 고민해보는 계기가 되었습니다.
예를 들어 앱을 열자마자 인증이 자동으로 이뤄지는 흐름이 좋은지, 실패했을 때 다시 시도할 수 있는 여지를 어떻게 줄지, 또는 인증 자체를 사용자가 원하지 않을 수도 있다는 점까지 고려하면서 보안과 사용성 사이의 균형을 생각해볼 수 있었습니다.
앞으로는 백그라운드 복귀 시 재인증, 패스코드 대체, 설정 토글 등 사용자 중심으로 보안 기능을 세분화하는 방향으로 발전시켜볼 계획입니다!
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'Apple > SwiftUI' 카테고리의 다른 글
[SwiftUI] SwiftUI 상태 동기화 트러블슈팅 (1) | 2025.06.25 |
---|---|
[SwiftUI] CADisplayLink를 활용한 부드러운 ProgressView 애니메이션 구현 트러블슈팅 (6) | 2025.06.06 |
[SwiftUI] NavigationStack 사용 시 화면 전환 안 되는 문제와 title 깨짐 문제 트러블슈팅 (0) | 2025.06.03 |
[SwiftUI] SwiftUI로 카메라 기능 구현하기 (2) | 2025.03.01 |
[SwiftUI] ProgressView 생성하기 (15) | 2025.01.15 |