iOS ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ์ฑ ๋ด์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๋จ์ํ ์ค์ ๊ฐ์ UserDefaults๋ก ์ถฉ๋ถํ์ง๋ง,
์์ธ์ค ํ ํฐ์ด๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด์ฒ๋ผ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ๋ณด์์ด ๊ฐํ๋ Keychain์ ์ ์ฅํ๋ฉด ์ข๋ค.
์ด๋ฒ ๊ธ์์๋ ๊ธฐ์กด์ ๋ง๋ค์ด๋ UserDefaultsManager์ ์ ์ฌํ ํจํด์ผ๋ก,
Keychain์ ์กฐ๊ธ ๋ ์์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋ KeychainManager๋ฅผ ์ง์ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค :)
Keychain์ ์์คํ
์์ ์ง์ ์ํธํํ์ฌ ๊ด๋ฆฌํ๋ ์์ ํ ์ ์ฅ์์ง๋ง,
API๊ฐ ๋ค์ ๋ณต์กํ๊ณ ๋ฐ๋ณต์ ์ธ ์ฝ๋๊ฐ ํ์ํ๋ค.
๋ฐ๋ผ์ UserDefaults์ฒ๋ผ ๊ฐ๋จํ๊ฒ '์ ์ฅ, ์ฝ๊ธฐ, ์ญ์ ' ๋ฉ์๋๋ง ํธ์ถํ๋ฉด ๋๋๋ก ๋ํํ๋ฉด
์ค๋ฌด์์ ํจ์ฌ ์ฌ์ฉํ๊ธฐ ํธ๋ฆฌํ ๊ฒ ๊ฐ์๋ค.
์ด๋ฒ์ ๊ตฌํํ KeychainManager๋ Enum ํค ๊ธฐ๋ฐ์ผ๋ก
ํ์
์์ ์ฑ์ ํ๋ณดํ๋ฉด์๋, ๋ถํ์ํ ์ค๋ณต ์ฝ๋๋ฅผ ์ต์ํํ๋๋ก ๊ตฌ์ฑํ๋ค.
<KeyChainManager>
import Security
import Foundation
struct KeychainManager {
enum Keys: String {
case accessToken
case refreshToken
case userIdentifier
case email
case password
}
// ์ ์ฅ
static func save(_ value: String, for key: Keys) -> Bool {
guard let data = value.data(using: .utf8) else { return false }
// ๊ธฐ์กด ๊ฐ ์ ๊ฑฐ
delete(for: key)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecValueData as String: data
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
// ์ฝ๊ธฐ
static func load(for key: Keys) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess,
let data = item as? Data,
let value = String(data: data, encoding: .utf8) else {
return nil
}
return value
}
// ์ญ์
static func delete(for key: Keys) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
}
<์ฌ์ฉ ์์>
// ์ ์ฅ
KeychainManager.save("my_access_token", for: .accessToken)
// ๋ถ๋ฌ์ค๊ธฐ
if let token = KeychainManager.load(for: .accessToken) {
print("ํ ํฐ: \(token)")
}
// ์ญ์
KeychainManager.delete(for: .accessToken)
๊ตฌํํ ์ฝ๋๋ฅผ ๊ฐ๋ตํ๊ฒ ์ค๋ช
ํ์๋ฉด,
Keychain์ ์ ์ฅํ ํญ๋ชฉ๋ค์ Enum์ผ๋ก ์ ์ํ๋ค.
enum Keys: String {
case accessToken
case refreshToken
case userIdentifier
case email
case password
}
๊ทธ๋ฆฌ๊ณ ๋ฌธ์์ด ๊ฐ์ ์ ์ฅํ๋ ๋ฉ์๋๋ฅผ ๋ง๋ค์๋ค.
static func save(_ value: String, for key: Keys) -> Bool {
guard let data = value.data(using: .utf8) else { return false }
// ๊ธฐ์กด ๊ฐ ์ ๊ฑฐ
delete(for: key)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecValueData as String: data
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
๋ด๋ถ์ String์ Data ํ์
์ผ๋ก ๋ณํํ๊ณ , ๋์ผํ ํค๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ค๋ฉด ์ถฉ๋์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ญ์ ํ๋๋ก ๊ตฌ์ฑํ๋ค.
๊ทธ๋ฆฌ๊ณ SecItemAdd๋ฅผ ์ฌ์ฉํ์ฌ Keychain์ ํญ๋ชฉ์ ์ถ๊ฐํ๋ค. (์ฑ๊ณต ์ฌ๋ถ๋ Bool๊ฐ์ผ๋ก ๋ฐํ)
๋ค์์ผ๋ก ์ฝ๊ธฐ(์กฐํ) ๋ฉ์๋๋
static func load(for key: Keys) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess,
let data = item as? Data,
let value = String(data: data, encoding: .utf8) else {
return nil
}
return value
}
SecItemCopyMatching์ ํตํด key์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ ,
์ฐพ์ ๊ฐ์ Data์์ String์ผ๋ก ๋ณํํ์ฌ ๋ฐํํ๋ค. (๋ง์ฝ ๊ฐ์ด ์๊ฑฐ๋ ์คํจํ ๊ฒฝ์ฐ nil ๋ฐํ)
๋ง์ง๋ง์ผ๋ก ์ญ์ ๋ฉ์๋๋
static func delete(for key: Keys) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
์ญ์ ๋์(key)์ ์ง์ ํ๋ฉด, SecItemDelete๋ฅผ ํธ์ถํ์ฌ ํด๋น ๊ฐ์ ์ญ์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ฑ๊ณต ์คํจ ์ฌ๋ถ๋ฅผ Bool๊ฐ์ผ๋ก ๋ฐํํ๋ค.
์์ ๊ฐ์ด ๊ตฌ์ฑํ๊ฒ ๋๋ฉด,
ํ๋ก์ ํธ ์ ๋ฐ์์ Keychain์ ํ์ฉํ ๋ ๋จ์ํ๊ณ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
๋ฌผ๋ก ์ฌ๊ธฐ์๋ ๊ธฐ๋ณธ์ ์ธ ์ ์ฅ·์กฐํ·์ญ์ ๊ธฐ๋ฅ๋ง ๊ตฌํํ์ง๋ง,
ํ์ํ๋ค๋ฉด ์์ฒด์ธ์ฆ ์ต์
, Keychain ๊ณต์ ๊ทธ๋ฃน, ์ ๊ทผ์ฑ ์ค์ ๋ฑ์ ํ์ฅํ์ฌ ์ ์ฉํ ์๋ ์๋ค.
์ด๋ฒ์ ๊ตฌ์ฑํ ๊ตฌ์กฐ๋
Keychain์ ๋ณต์กํ API๋ฅผ ๊ฐ์ธ์ Swfit์์ ๊ฐ๊ฒฐํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ถ์ํํ ๊ตฌ์กฐ์ด๋ค.
'๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] Keychain (0) | 2025.09.14 |
---|