투케이2K

60. (TWOK/UTIL) [Ios/Objc] S_DeviceID - 단말기 고유값 지정 실시 (키 체인 , UUID) 본문

투케이2K 유틸파일

60. (TWOK/UTIL) [Ios/Objc] S_DeviceID - 단말기 고유값 지정 실시 (키 체인 , UUID)

투케이2K 2022. 6. 24. 08:43

[설 명]

프로그램 : Ios / Objective-C

설 명 : S_DeviceID - 단말기 고유값 지정 실시 (키 체인 , UUID)

 
 
 

[소스 코드]

import Foundation
import Security
import UIKit

@objc class S_DeviceID: NSObject {
    
    
    // MARK: - [클래스 설명]
    /*
    // -----------------------------------------
    1. 디바이스 고유값 저장 클래스
    // -----------------------------------------
    2. 디바이스 정보 저장 : [[S_DeviceID shared] createDeviceID];
    // -----------------------------------------
    3. 디바이스 정보 조회 : [[S_DeviceID shared] getDeviceID];
    // -----------------------------------------
    4. 디바이스 정보 삭제 : [[S_DeviceID shared] deleteDeviceID];
    // -----------------------------------------
    5. 호출 방법 :
     NSString *deviceId = [[S_DeviceID shared] getDeviceID];
     
     if ([[C_Util shared] stringNotNullWithStr:deviceId] == true){
         // [저장된 디바이스 고유값 확인 실시]
     }
     else {
         // [디바이스 고유값 저장 실시]
         [[S_DeviceID shared] createDeviceID];
     }
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [키 체인 설명]
    /*
    // -----------------------------------------
    1. Keychain 은 디바이스 안에 암호화된 데이터 저장 공간입니다
    // -----------------------------------------
    2. Keychain 은 보안 기능이 뛰어나 사용자 정보 및 결제 정보 등 민감한 정보를 저장할 수 있습니다
    // -----------------------------------------
    3. Keychain 은 앱 종료 및 앱 삭제 후 재설치를 진행해도 동일하게 저장된 값을 사용할 수 있습니다
       - 영구적인 데이터
       - 사용자가 삭제하지 않는 이상 유지
       - Keychain 은 사용자가 데이터를 삭제하거나, 휴대폰 공장 초기화를 진행 시 삭제됩니다
    // -----------------------------------------
    4. 설정 패키지 : import Security
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [UUID 설명]
    /*
    // -----------------------------------------
    1. uuid 는 32개의 문자+숫자로 이루어집니다
    // -----------------------------------------
    2. iOS4까지는 iOS기기의 고유 넘버로 udid를 사용했지만, 보안 문제로 iOS5부터는 uudi(임의로 생성한 고유값)를 사용합니다
    // -----------------------------------------
    3. uuid는 앱을 삭제하면 새롭게 생성됩니다
    // -----------------------------------------
    4. uuid는 Vender 에 따라서 값이 달라집니다 (공급업체(벤더)가 같은 앱들은 모두 같은 고유 ID를 가집니다)
    // -----------------------------------------
    5. uuid 외에도 ADID (IDFA) 라는 것이 있는데 이는 광고 식별자이고 기기마다 고유한 값을 가집니다
       - 앱 스토어 등록 심사 시 필수 광고 식별자 사용을 체크하고 사유를 적어야합니다 (아니면, 앱 스토어 등록 거부될 수 있습니다)
    // -----------------------------------------
    6. 설정 패키지 : import UIKit
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [Keychain 생성 정보 정의]
    private let account = "ServiceSaveKeyTwok" // [디바이스 고유값 KEY]
    private let service = Bundle.main.bundleIdentifier // [번들 아이디]





    // MARK: - [클래스 이름 설정]
    let ACTIVITY_NAME = "S_DeviceID"
    
    
    
    
    
    // MARK: - [클래스 싱글톤 설정]
    @objc static let shared = S_DeviceID()
    
    
    
    
    
    // MARK: - [Keychain 사용해 디바이스 고유값 저장 실시]
    @objc func createDeviceID() {
        
        // -----------------------------------------
        
        guard let serviceCheck = self.service as? String
        else {
            // [프리퍼런스에 UUID 저장 : [앱 삭제 시 지워짐]]
            let uuidCreate = UUID().uuidString
            UserDefaults.standard.set(uuidCreate, forKey: self.account)
            UserDefaults.standard.synchronize() // 동기화 실시
            
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> createDeviceID() :: Service Check :: false]")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 UUID 저장 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            return
        }
        
        // -----------------------------------------
        
        // [번들 아이디 널 체크 수행 실시]
        var serviceId = ""
        if serviceCheck != nil && serviceCheck.count>0 && serviceCheck != "" {
            serviceId = serviceCheck
        }
        else {
            serviceId = "com.app." + self.account
        }
        
        // -----------------------------------------
        
        // [디바이스 고유값 생성 실시]
        let deviceID = self.setDeviceID()
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> createDeviceID() :: [사전] 디바이스 고유값 [저장] 에 필요한 정보 확인]")
        print("-------------------------------")
        print("account :: \(self.account)")
        print("-------------------------------")
        print("service :: \(serviceId)")
        print("-------------------------------")
        print("deviceID :: \(deviceID)")
        print("====================================")
        print("")
        
        // -----------------------------------------
        
        /*
        // [일반 프리퍼런스에 UUID 저장 : [앱 삭제 시 지워짐]]
        let uuidCreate = UUID().uuidString
        UserDefaults.standard.set(uuidCreate, forKey: self.account)
        UserDefaults.standard.synchronize() // 동기화 실시
        
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> createDeviceID() :: 디바이스 고유값 [저장] [성공]]")
        print("-------------------------------")
        print("로 직 :: [프리퍼런스] 에 UUID 저장 실시")
        print("-------------------------------")
        print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
        print("====================================")
        print("")
        // */
        
        // -----------------------------------------
        
        ///*
        // [키 체인 쿼리 생성]
        let query:[CFString: Any]=[kSecClass: kSecClassGenericPassword, // 보안 데이터 저장
                                   kSecAttrService: serviceId, // 키 체인에서 해당 앱을 식별하는 값 (앱만의 고유한 값)
                                   kSecAttrAccount: self.account, // 앱 내에서 데이터를 식별하기 위한 키에 해당하는 값 (사용자 계정)
                                   kSecValueData: deviceID.data(using: .utf8, allowLossyConversion: false)!] // 키값에 디바이스 고유값 저장

        
        // [keychain 에 저장을 수행한 결과 값 반환 (true / false)]
        let status: OSStatus = SecItemAdd(query as CFDictionary, nil)
        if status == errSecSuccess {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> createDeviceID() :: 디바이스 고유값 [저장] [성공]]")
            print("-------------------------------")
            print("Success Status :: \(status)")
            print("-------------------------------")
            print("type :: [키 체인] 데이터")
            print("-------------------------------")
            print("deviceID :: \(deviceID)")
            print("====================================")
            print("")
        }
        else {
            // [프리퍼런스에 UUID 저장 : [앱 삭제 시 지워짐]]
            let uuidCreate = UUID().uuidString
            UserDefaults.standard.set(uuidCreate, forKey: self.account)
            UserDefaults.standard.synchronize() // 동기화 실시

            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> createDeviceID() :: 디바이스 고유값 [저장] [실패]]")
            print("-------------------------------")
            print("Fail Status :: \(status)")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 UUID 저장 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
        }
        // */
        
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [Keychain 사용해 디바이스 고유값 호출 실시]
    @objc func getDeviceID() -> String {
        
        // -----------------------------------------
        
        guard let serviceCheck = self.service as? String
        else {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: Service Check :: false]")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 저장된 값 반환 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 반환 실시]
            return UserDefaults.standard.string(forKey: self.account) ?? ""
        }
        
        // -----------------------------------------
        
        // [번들 아이디 널 체크 수행 실시]
        var serviceId = ""
        if serviceCheck != nil && serviceCheck.count>0 && serviceCheck != "" {
            serviceId = serviceCheck
        }
        else {
            serviceId = "com.app." + self.account
        }
        
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: [사전] 저장된 디바이스 고유값 [호출] 에 필요한 정보 확인]")
        print("-------------------------------")
        print("account :: \(self.account)")
        print("-------------------------------")
        print("service :: \(serviceId)")
        print("====================================")
        print("")
        
        // -----------------------------------------
        
        // [사전 UUID 로 저장된 정보가 있는지 확인 실시]
        let checkUUid = UserDefaults.standard.string(forKey: self.account) ?? ""
        if checkUUid != nil && checkUUid.count > 0 && checkUUid != "" {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: 저장된 디바이스 고유값 [호출] [성공]]")
            print("-------------------------------")
            print("type :: [UUID] 데이터")
            print("-------------------------------")
            print("deviceID :: \(checkUUid)")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 반환 실시]
            return checkUUid
        }
        
        // -----------------------------------------
        
        // [키 체인 쿼리 정의]
        let query:[CFString: Any]=[kSecClass: kSecClassGenericPassword, // 보안 데이터 저장
                                   kSecAttrService: serviceId, // 키 체인에서 해당 앱을 식별하는 값 (앱만의 고유한 값)
                                   kSecAttrAccount : self.account, // 앱 내에서 데이터를 식별하기 위한 키에 해당하는 값 (사용자 계정)
                                   kSecReturnData : true, // kSecReturnData에 true를 리턴시켜 값을 불러옵니다
                                   kSecReturnAttributes: true, // kSecReturnAttributes에 true를 리턴시켜 값을 불러옵니다
                                   kSecMatchLimit : kSecMatchLimitOne] // 값이 일치하는 것을 찾습니다
        
        
        // [키 체인에 저장된 값을 읽어옵니다]
        var dataTypeRef : CFTypeRef?
        let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
        
        
        // [처리 결과가 성공인 경우 >> 키 체인에서 읽어온 값을 Data 타입으로 변환 >> 다시 String 타입으로 변환]
        if status == errSecSuccess {
            guard let existingItem = dataTypeRef as? [String: Any]
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: Type Check :: false]")
                print("-------------------------------")
                print("로 직 :: [프리퍼런스] 에 저장된 값 반환 실시")
                print("-------------------------------")
                print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
                print("====================================")
                print("")
                
                // [프리퍼런스에 저장된 값 반환 실시]
                return UserDefaults.standard.string(forKey: self.account) ?? ""
            }
            let deviceData = existingItem[kSecValueData as String] as? Data
            let uuidData = String(data: deviceData!, encoding: .utf8)!
            
            if uuidData != nil && uuidData.count>0 && uuidData != "" {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: 저장된 디바이스 고유값 [호출] [성공]]")
                print("-------------------------------")
                print("Success Status :: \(status)")
                print("-------------------------------")
                print("type :: [키 체인] 데이터")
                print("-------------------------------")
                print("deviceID :: \(uuidData)")
                print("====================================")
                print("")
                
                return uuidData
            }
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: 저장된 디바이스 고유값 [호출] [실패]]")
                print("-------------------------------")
                print("Fail Status :: 저장된 데이터 값이 NULL 상태")
                print("-------------------------------")
                print("로 직 :: [프리퍼런스] 에 저장된 값 반환 실시")
                print("-------------------------------")
                print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
                print("====================================")
                print("")
                
                // [프리퍼런스에 저장된 값 반환 실시]
                return UserDefaults.standard.string(forKey: self.account) ?? ""
            }
        }
        else if status == errSecItemNotFound || status == -25300 {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: 저장된 디바이스 고유값 [호출] [실패]]")
            print("-------------------------------")
            print("Fail Status :: 저장된 데이터가 없습니다")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 저장된 값 반환 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 반환 실시]
            return UserDefaults.standard.string(forKey: self.account) ?? ""
        }
        else {
            // [처리결과가 실패라면 nil을 반환]
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> getDeviceID() :: 저장된 디바이스 고유값 [호출] [실패]]")
            print("-------------------------------")
            print("Fail Status :: \(status)")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 저장된 값 반환 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 반환 실시]
            return UserDefaults.standard.string(forKey: self.account) ?? ""
        }
        
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [Keychain 에 저장된 값 삭제 수행]
    @objc func deleteDeviceID() {
        
        // -----------------------------------------
        
        guard let serviceCheck = self.service as? String
        else {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> deleteDeviceID() :: Service Check :: false]")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 저장된 값 삭제 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 삭제]
            UserDefaults.standard.removeObject(forKey: self.account)
            UserDefaults.standard.synchronize() // 동기화 실시
            return
        }
        
        // -----------------------------------------
        
        // [번들 아이디 널 체크 수행 실시]
        var serviceId = ""
        if serviceCheck != nil && serviceCheck.count>0 && serviceCheck != "" {
            serviceId = serviceCheck
        }
        else {
            serviceId = "com.app." + self.account
        }
        
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> deleteDeviceID() :: [사전] 디바이스 고유값 [삭제] 에 필요한 정보 확인]")
        print("-------------------------------")
        print("account :: \(self.account)")
        print("-------------------------------")
        print("service :: \(serviceId)")
        print("====================================")
        print("")
        
        // -----------------------------------------
        
        // [사전 UUID 로 저장된 정보가 있는지 확인 실시]
        let checkUUid = UserDefaults.standard.string(forKey: self.account) ?? ""
        if checkUUid != nil && checkUUid.count > 0 && checkUUid != "" {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> deleteDeviceID() :: 디바이스 고유값 [삭제] [성공]]")
            print("-------------------------------")
            print("type :: [UUID] 데이터")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 삭제]
            UserDefaults.standard.removeObject(forKey: self.account)
            UserDefaults.standard.synchronize() // 동기화 실시
            return
        }
        
        // -----------------------------------------
        
        // [키 체인 쿼리 정의]
        let query:[CFString: Any]=[kSecClass: kSecClassGenericPassword, // 보안 데이터 저장
                                   kSecAttrService: serviceId, // 키 체인에서 해당 앱을 식별하는 값 (앱만의 고유한 값)
                                   kSecAttrAccount : self.account] // 앱 내에서 데이터를 식별하기 위한 키에 해당하는 값 (사용자 계정)
        
        
        // [현재 저장되어 있는 값 삭제]
        let status: OSStatus = SecItemDelete(query as CFDictionary)
        if status == errSecSuccess {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> deleteDeviceID() :: 디바이스 고유값 [삭제] [성공]]")
            print("-------------------------------")
            print("Success Status :: \(status)")
            print("-------------------------------")
            print("type :: [키 체인] 데이터")
            print("====================================")
            print("")
        }
        else {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> deleteDeviceID() :: 디바이스 고유값 [삭제] [실패]]")
            print("-------------------------------")
            print("로 직 :: [프리퍼런스] 에 저장된 값 삭제 실시")
            print("-------------------------------")
            print("UUID :: \(UserDefaults.standard.string(forKey: self.account) ?? "")")
            print("====================================")
            print("")
            
            // [프리퍼런스에 저장된 값 삭제]
            UserDefaults.standard.removeObject(forKey: self.account)
            UserDefaults.standard.synchronize() // 동기화 실시
        }
        
        // -----------------------------------------
    }

    

    
    
    // MARK: - [디바이스 고유 값 추출 메소드]
    @objc func setDeviceID() -> String {
        return UIDevice.current.identifierForVendor!.uuidString
    }
    
    
} // [클래스 종료]

 

반응형
Comments