# 키체인 <문제점> 1. **kSecClassInternetPassword Keychain에 저장된 인터넷 비밀번호를 찾으려고 시도하기때문에 문제가 생김** 2. add해주기전에 삭제하지않으면 Keychain에 같은 키를 사용하여 두 개 이상의 비밀번호가 저장되는 경우가 발생할 수 있음. 새 비밀번호 생성 전에 삭제 3. 기존 코드의 로그인 버튼 클릭 시, kSecAttrAccount를 사용하여 계정 정보를 가져오려고 시도했는데. 이 경우에는 계정 정보가 필요하지 않아서.수정된 코드에서는 kSecAttrService를 사용하여 저장된 비밀번호를 찾도록 변경! → 이렇게 하면 동일한 서비스를 사용하는 모든 계정의 비밀번호를 검색할 수 있음!, 그리고 우리 코드에서는 account가 필요없음 ! 수정 전 코드 ```swift guard let existingItem = item as? [String : Any], let passwordData = existingItem[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: String.Encoding.utf8), let account = existingItem[kSecAttrAccount as String] as? String else { return } ``` 수정 후 코드 ```swift guard let existingItem = item as? [String : Any], let passwordData = existingItem[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: String.Encoding.utf8) else { return } ``` ### 전체코드 ```swift class LogInViewController: UIViewController { @IBOutlet weak var pwTextField: UITextField! var diaryViewController: DiaryViewController? override func viewDidLoad() { super.viewDidLoad() diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController } @IBAction func tapLogInButton(_ sender: Any) { guard let diaryViewController = diaryViewController else { return } let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "passwordKey", kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnAttributes as String: true, kSecReturnData as String: true] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) guard status != errSecItemNotFound else { return } guard status == errSecSuccess else { return } guard let existingItem = item as? [String : Any], let passwordData = existingItem[kSecValueData as String] as? Data, let password = String(data: passwordData, encoding: String.Encoding.utf8) else { return } guard pwTextField.text == password else { let alert = UIAlertController(title: "비밀번호가 불일치", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다.", preferredStyle: .alert) let confirm = UIAlertAction(title: "돌아가기", style: .default) alert.addAction(confirm) present(alert, animated: true, completion: nil) return } diaryViewController.modalPresentationStyle = .fullScreen present(diaryViewController, animated: true) } @IBAction func addNewPassword(_ sender: Any) { let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}" let validPw = pwTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil if validPw { guard let newPassword = pwTextField.text, !newPassword.isEmpty else { return } let passwordData = newPassword.data(using: String.Encoding.utf8)! // 기존 비밀번호를 삭제하고 추가해줘야항 let deleteQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "passwordKey"] SecItemDelete(deleteQuery as CFDictionary) // 추가 let addQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "passwordKey", kSecValueData as String: passwordData] let addStatus = SecItemAdd(addQuery as CFDictionary, nil) guard addStatus == errSecSuccess else { return } let alert = UIAlertController(title: "성공", message: "비밀번호가 성공적으로 등록되었습니다.", preferredStyle: .alert) let confirm = UIAlertAction(title: "확인", style: .default) alert.addAction(confirm) present(alert, animated: true, completion: nil) } else { let alert = UIAlertController(title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.", preferredStyle: .alert) let confirm = UIAlertAction(title: "확인", style: .default) alert.addAction(confirm) present(alert, animated: true, completion: nil) } } } ```