본문 바로가기

SwiftUI 앱에서 Face ID / Touch ID 인증 구현하기

@Prof.SSong2025. 5. 14. 21:15
728x90

프로젝트 맥락

 

본 글은 SwiftUI로 Apple Developer Academy_Challenge 2에서 개발 중인 ‘Re:ToU(오늘의 너)’ 앱을 기반으로 작성되었습니다.

‘Re:ToU’는 하루에 하나씩 감정과 회고를 기록하는 앱으로,

개인적인 기록이 포함되는 만큼 앱을 열 때 간단한 생체인증(Face ID / Touch ID)을 요구하도록 설계했습니다.

 

이 기능은 앱을 껐다 켤 때마다 자동으로 인증 절차가 실행되며, 다른 사람의 접근을 방지하기 위한 보안 UX의 핵심 역할을 합니다.

 


 

기능 설명

 

앱 실행 시 다음과 같은 흐름으로 동작합니다.

 

  1. 앱이 실행되면 인증 뷰를 먼저 띄움
  2. Face ID / Touch ID 인증을 시도
  3. 성공 시 메인 화면으로 진입
  4. 실패 시 다시 인증 시도 또는 앱 종료 유도

 

이 기능은 LocalAuthentication 프레임워크를 사용하며, SwiftUI와 연동하기 위해 별도 인증 뷰 모델을 구성했습니다.

 


 

 

시도 동기

 

초기에는 앱 실행 후 바로 메인 화면으로 진입하는 구조였고,

기록이 아무나 볼 수 있다는 점에서 보안적으로 문제가 있었습니다.

 

그래서 앱 실행 시 생체인증을 도입하려고 했지만,

SwiftUI에서 LocalAuthentication을 사용하는 예제가 적고,

앱의 Scene 단위 진입 흐름과 어떻게 연동할지 명확하지 않았습니다.

 


 

 

문제 발견

 

Face ID / Touch ID를 연동하면서 다음과 같은 문제를 마주했습니다.

 

  • SwiftUI에서는 AppDelegate 없이 인증 시점 지정이 어려움
  • 앱이 백그라운드 → 포그라운드로 돌아올 때 재인증 처리 누락
  • 인증 실패 시 재시도 로직 누락으로 UX 불안정

 


 

 

문제 해결

 

 

1. LocalAuthentication 인증 함수 정의

import LocalAuthentication

class AuthViewModel: ObservableObject {
    @Published var isAuthenticated = false

    func authenticate() {
        let context = LAContext()
        var error: NSError?

        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            let reason = "기록을 열기 위해 인증이 필요합니다."

            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, _ in
                DispatchQueue.main.async {
                    self.isAuthenticated = success
                }
            }
        } else {
            // Face ID 미지원 기기 대응
            DispatchQueue.main.async {
                self.isAuthenticated = true
            }
        }
    }
}

 

 


 

 

2. 인증 전용 View 구성

struct AuthenticationView: View {
    @StateObject private var authViewModel = AuthViewModel()

    var body: some View {
        Group {
            if authViewModel.isAuthenticated {
                MainView() // 인증 완료 시 진입
            } else {
                VStack {
                    Text("Face ID 인증 중...")
                    ProgressView()
                }
                .onAppear {
                    authViewModel.authenticate()
                }
            }
        }
    }
}

 

 


 

 

3. 앱 진입 시 AuthenticationView 사용

@main
struct ReToUApp: App {
    var body: some Scene {
        WindowGroup {
            AuthenticationView()
        }
    }
}

이 구조로 앱을 열면 항상 인증 → 메인 진입 순서가 유지됩니다.

 


 

 

비슷한 인증 방식 비교

방식 설명과 단점

Face ID / Touch ID (현재) 생체 인증 빠르고 편리, UX 자연스러움 기기 지원 필요, 실패 처리 필요
앱 비밀번호 입력 사용자 지정 암호 모든 기기 지원 관리 복잡, 유출 위험
비인증 상태 유지 인증 없음 구현 쉬움 보안 없음, 개인 정보 노출

 

 


 

 

느낀 점

 

생체인증은 생각보다 구현 자체는 간단하지만,

SwiftUI와 결합할 때 앱 흐름을 어떻게 설계할지가 훨씬 중요하다는 걸 느꼈습니다.

 

특히 인증 성공 여부에 따라 어떤 화면을 보여줄지 분기 처리하는 구조를

MVVM 기반 ViewModel을 통해 깔끔하게 분리해두는 것이

나중에 테스트, 확장, UI 변경 시에도 큰 도움이 될 거라 확신했습니다.

 

728x90
목차