[iOS] 생체인식 인증 (Face ID / Touch ID) 구현해보기

iOS에서 생체인식 인증을 구현하는 방법에 대해서 다루어봅니다.
2022.05.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

안녕하세요! 클래스메소드 CX사업본부 모바일사업부의 정하은입니다?

iOS 엔지니어가 된 지 벌써 1년이 되었지만 이런저런 핑계로 블로그 쓰는 걸 미루다 드디어 오늘 공개를 하네요. (어제까지 푹 쉬다 와서 그런지 글이 잘 써지는 것 같은 착각이...?)

이번 글은 스마트폰을 사용하는 사람이라면 너무나도 익숙한 생체인식 인증을 앱에서 실행시킬 수 있도록 구현하는 방법에 대해서 다루어보려고 합니다.

생체인식 인증 (Biometrics Authentication) 기능 추가하기

앞서 머리말에서 이야기했던 것처럼 생체인식 인증 기능 자체는 코드 3줄 정도로 추가가 가능한데요.

let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "인증이 필요합니다") {
   [weak self] (res, err) in
       DispatchQueue.main.async {
            self?.loggedIn = res
       }
}

와~ 정말 간단하죠? ?

...라고 마치기에는 글이 너무 불친절해지기 때문에 간략하게나마 위 코드에 대해 알아보도록 할게요.

LAContext 클래스에 대하여

위의 코드에서 LAContext 클래스의 인스턴스를 생성하고 있는데 우선 LA라는 약자가 무엇을 의미하는 지 이해해야합니다. (당연한 말이지만 미국 LA를 의미하는 건 아니라는 점..!!)

LA = Local Authentication

Local Authentication은 사용자가 기기내에서 인증하는 부분을 다루는 프레임워크인데요. 생체인식 인증과 암호 인증 구현이 이에 해당됩니다.

인증 데이터는 시스템의 나머지 부분과 격리된 하드웨어 기반 보안 프로세서인 Secure Enclave가 이 데이터를 운영 체제의 손마저 닿지 않는 곳에서 관리합니다. 대신에 특정 policy를 지정하고, 사용자가 인증을 해야할 때 사용자에게 인증을 원하는 이유를 알려주는 메시지를 표시합니다. 그런 다음 Secure Enclave 와 조정하여 작업을 수행하고, 성공과 실패를 나타내는 Boolean 결과를 얻게 됩니다.

결론적으로 LAContext라는 클래스는 위의 인증과 관련된 기능들을 다루는 셈이 되구요.

evaluatePolicy(_: localizedReason: reply:) 의 역할

evaluatePolicy(_: localizedReason: reply:) 는 Face ID/Touch ID (생체인식 인증) 또는 Passcode (암호 인증) 를 사용한 인증을 실행해주는 코드입니다. 인증 후 결과는 함수 내의 인수로 콜백하는 형태로 사용합니다.

괄호 안 첫 번째 요소는 사용 인증 방식에 대해 지정하는 정수를 입력하게 되는데요. 아래 두 가지로 나뉩니다.

  • deviceOwnerAuthenticationWithBiometrics : 생체인식 인증을 실행. 그러나, iOS 9부터 이 정수를 사용할 때, Fallback 버튼 (인증이 실패했을 때 나오는 암호 입력 버튼) 을 눌러도 아무 화면이 나오지 않음. Fallback 버튼이 나오지 않도록 코드를 수정하거나, 아래의 deviceOwnerAuthentication 으로 대체해야함 (아니면 커스텀으로 입력화면을 따로 만들어서 띄우게 하는 것도 가능할 지도?)

  • deviceOwnerAuthentication : 생체인식 인증이 등록되어 있는 경우, 생체인식 인증을 실행. 생체인식 인증이 등록되어 있지 않거나 실패할 경우, 암호 인증을 실행.

괄호 안 두 번째 요소 localizedReason 에는 인증 시에 알림 창에 표시할 메시지를 지정합니다.

마지막으로 reply는 클로져를 작성하여 인증이 성공 또는 실패할 경우의 처리 등을 작성합니다.

예시

실제로 위의 코드를 앱으로 구현할 경우에 아래와 같이 동작하게 됩니다.

(인증이 성공했을 시, 버튼 아래에 인증 성공 문구를 출력. 실패 시, 문구를 표시하지 않음)

Face ID Touch ID

데이터가 일치하지 않아 생체인식 인증이 실패할 경우, 아래처럼 암호 인증 화면이 나옵니다. (Touch ID인 경우, 알림창에 있는 암호 입력 버튼을 누른 후에 나옵니다)

전체코드

저는 화면 구성에 SwiftUI를 사용했지만, UIKit으로 위 화면과 유사하게 만들어서 활용하는 것도 가능합니다.

View

import SwiftUI

struct ContentView: View {
    
    @EnvironmentObject
    var viewModel: ViewModel
    
    var body: some View {
        VStack {
            Spacer()
            Text("Biometric Auth")
                .font(.title)
            Spacer()
                .frame(height: 20)
            Button(action: {
                self.viewModel.auth()
            }) {
                Text("인증하기")
            }
            Spacer()
            if viewModel.loggedIn {
                Text("인증 성공!")
            }
            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(ViewModel())
    }
}

ViewModel

import Foundation
import LocalAuthentication

class ViewModel: ObservableObject {
    
    @Published
    var loggedIn: Bool = false
    
    func auth() {
        let context = LAContext()
        context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "인증이 필요합니다") {
            [weak self] (res, err) in
            DispatchQueue.main.async {
                self?.loggedIn = res
            }
        }
    }
}

(선택) Info.plist 수정

앱에서 생체인식 인증 기능을 처음으로 사용할 시에 생체인식 인증을 통해 인증 하는 것을 허가하도록 요청하는 알림창이 표시가 되는데요.

이 때 표시되는 문구가 마음에 들지 않은 경우, Info.plist 에 아래 항목을 추가하여 메시지 내용을 변경할 수 있습니다.

마무리하며

이번 글에서는 생체인식 인증 기능 구현에 대해서 알아보았는데요. 이 기능을 매우 간단하게 추가했지만 위 코드만으로는 보안적인 허점이 존재합니다.

다음 글에서는 이 부분에 대해서 다루어 볼 예정입니다. 아래의 링크를 클릭하시면 바로 다음 글로 넘어가니 관심 있으시다면 같이 읽어봐주시면 감사드립니다?‍♀️

참고