iOS์์ ํํ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ ์ ์ฅ์๋ก๋ UserDefaults, CoreData, Keychain ๋ฑ์ด ์๋ค.
ํ์ฐฝ ๊ณต๋ถํ ๋, ์ด๋ฐ ์ด์ผ๊ธฐ๋ฅผ ์์ฃผ ๋ค์๋ค.
"๋น๋ฐ๋ฒํธ ํน์ ํ ํฐ๊ณผ ๊ฐ์ด ์ค์ํ ์ ๋ณด๋ค์ ํค์ฒด์ธ์ ์ ์ฅํด์ผํด. UserDefaults๋ ๋ณด์์ ์ข์ง ์์."
"๊ฒฐ๊ตญ ๋ฐ์ง๊ณ ๋ณด๋ฉด UserDefaults์ ์ ์ฅํ๋ ํค์ฒด์ธ์ ์ ์ฅํ๋ ๋น์ทํด."
ํ์ง๋ง ์ ์ ์ด๋ค ์ํฉ์์ ์ด๋ค ์ ์ฅ์๋ฅผ ์ ํํด์ผ ํ๋์ง ๋ช
ํํ ๊ธฐ์ค์ ์ฐพ์ง ๋ชปํ๊ณ ,
๋จ์ํ ๊ตฌํ์ด ๊ฐํธํ๋ค๋ ์ด์ ๋ก UserDefaults๋ฅผ ์์ฃผ ์ฌ์ฉํด ์๋ค.
์ด๋ฒ ๊ธ์์๋ iOS Keychain์ ์ข ๋ ๋ฉด๋ฐํ ์ดํด๋ณด๊ณ ,
์์ผ๋ก๋ ๊ฐ๋ฐ ๊ณผ์ ์์ ๋ฐ์ดํฐ์ ์ฑ๊ฒฉ๊ณผ ๋ณด์ ์๊ตฌ ์ฌํญ์ ๋ง์ถ์ด ์ ์ ํ ์ ์ฅ์๋ฅผ ์ ํํ๋ ๊ธฐ์ค์ ์ธ์๋ณด๋ ค๊ณ ํ๋ค :)
Keychain์ด๋?
- iOS, macOS ๋ฑ ์ ํ ํ๋ซํผ์์ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ์ ์ฅํ๊ธฐ ์ํด ์ ๊ณตํ๋ ์์คํ
์ ์ฅ์์
๋จ์ํ ํ์ผ ์ํธ๊ฐ ์๋๋ผ, OS ์ฐจ์์ ๋ณด์ ๊ด๋ฆฌ๊ฐ ์ ์ฉ๋๋ฉฐ, ๊ธฐ๊ธฐ ์ ๊ธ์ด๋ ์์ฒด ์ธ์ฆ ๊ฐ์ ํ๋์จ์ด ๋ณด์ ์์์๋ ์ฐ๊ณํ ์ ์์
๋ํ์ ์ผ๋ก ๋น๋ฐ๋ฒํธ, ์์ธ์ค ํ ํฐ, ์ธ์ฆ์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ๋ณด๊ดํ ๋ ์ฌ์ฉํจ
UserDefaults, CoreData์์ ๋น๊ต
๋ง์ ๊ฐ๋ฐ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋ UserDafaults๋ CoreData๋ฅผ ๋จผ์ ๋ ์ฌ๋ฆผ
๊ทธ๋ฌ๋ ๋ณด์ ์ธก๋ฉด์์ Keychain์ ๋ช
ํํ ์ฅ์ ์ด ์์
์ ์ฅ์ | ์ฉ๋ | ๋ณด์์ฑ |
UserDefaults | ๋จ์ ์ค์ ๊ฐ, ํ๋๊ทธ ์ ์ฅ | ์ํธํ ์์ |
CoreData | ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ, ๋ชจ๋ธ ์ ์ฅ | ์ง์ ์ํธํ ํ์ |
Keychain | ๋น๋ฐ๋ฒํธ, ํ ํฐ, ์ธ์ฆ์ ๋ฑ | OS ๋ณด์ ๋ด์ฅ, ํด๋์จ์ด ์ธ์ฆ ์ฐ๊ณ |
๐๐ปโ๏ธ ๋ณด์์ด ํ์ํ ๋ฐ์ดํฐ๋ ๋ฐ๋์ Keychain์ ์ ์ฅํด์ผ ํ๋ค๋ ์ ์ด Apple์ ๊ฐ์ด๋๋ผ์ธ์ ์ ํ์์
Keychain์ ์ฃผ์ ๊ธฐ๋ฅ
- ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ์ ์ฅ: ๋น๋ฐ๋ฒํธ, API ํ ํฐ, ์ธ์ฆ์ ๋ฑ
- ๋ฐ์ดํฐ ์กฐํ/์ญ์ /์
๋ฐ์ดํธ: Keychain API๋ฅผ ํตํด CRUD ์ง์
- ์์ฒด ์ธ์ฆ ์ฐ๊ณ: FaceID, TouchID์ ํจ๊ป ์ฌ์ฉํ ์ ์์
- iCloud Keychian ๋๊ธฐํ: ๋์ผ Apple ID๋ก ๋ก๊ทธ์ธ๋ ๊ธฐ๊ธฐ ๊ฐ ์๋ ๋๊ธฐํ
- ์ฑ ๊ฐ ๊ณต์ : Access Group์ ํตํด ์ฌ๋ฌ ์ฑ์์ Keychain ๋ฐ์ดํฐ ๊ณต์ ๊ฐ๋ฅ
Keychain API?
Keychain Services API
- ์๋์ ๋น๋ฐ ๋ฐ์ดํฐ(์ํธ, ํ ํฐ, ํค ๋ฑ)๋ฅผ OS ๋ณด์/๋ฐ์ดํฐ ๋ณดํธ(ํด๋์ค ํค, ํค๋ฐฑ) ๊ธฐ๋ฐ ์ํธํ ์ ์ฅ์์ ์์ ํ๊ฒ CRUD๋ก ๊ด๋ฆฌํ๋ ํ์ค ์ธํฐํ์ด์ค
- ํต์ฌ ํจ์: SecItemAdd / SecItemCopyMatching / SecItemUpdate / SecItemDelete
ํญ๋ชฉ(Items)
- Keychain์ '์์ดํ
' ๋จ์๋ก ๋์ํจ
• kSecClassGenericPassword: ์ผ๋ฐ ๋น๋ฐ๋ฒํธ/ํ ํฐ ๋ฑ
• kSecClassInternetPassword: ์๋ฒ·๋๋ฉ์ธ·ํ๋กํ ์ฝ ์ ๋ณด์ ํจ๊ป ์ฐ๋ ์ธํฐ๋ท ๋น๋ฐ๋ฒํธ
• kSecClassKey: ๋์นญ/๋น๋์นญ ํค
• kSecClassCertificate: ์ธ์ฆ์
ํน์ง
- ๊ฐํ ๋ฐ์ดํฐ ๋ณดํธ:
• iOS ๋ฐ์ดํฐ ๋ณดํธ ์ฒด๊ณ/ํด๋์ค ํค์ ์ํด ์ ์ฅ ์ ์ํธํ๋๊ณ , ํ๋์จ์ด ๋ณด์ ์์์ ์ฐ๊ณ๋จ
- ์ ๊ทผ ์์ ์ ์ด: kSecAttrAcessible
• ์ธ์ ์ ๊ทผ ๊ฐ๋ฅํ์ง ์ง์ ๊ฐ๋ฅ(ex. ์ ๊ธ ํด์ ์, ์ต์ด ์ ๊ธ ํด์ ์ดํ, ...)
- ์ถ๊ฐ ์ธ์ฆ ์๊ตฌ: SecAccessControl + LAContext
• FaceID/TouchID·๋๋ฐ์ด์ค ์ํธ ๋ฑ์ ๊ฐ์ ํ ์ ์์
- ์ฑ/ํ์ฅ ๊ฐ ๊ณต์ : Access Group
• Xcode์ Keychain Sharing์ ์ผ๊ณ ๊ณตํต access group์ ์ค์ ํ ํ, kSecAttrAccessGroup์ ์ง์
- iCloud ๋๊ธฐํ
• kSecAttrSynchronizable: true๋ก ์ค์
Swfit์์์ Keychain ์ ๊ทผ ๋ฐฉ๋ฒ
1) Security ํ๋ ์์ํฌ ์ง์ ์ฌ์ฉ
import Security
// ์ ์ฅ
func savePassword(_ password: String, for account: String) {
let data = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: data
]
SecItemAdd(query as CFDictionary, nil)
}
// ์กฐํ
func loadPassword(for account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: AnyObject?
SecItemCopyMatching(query as CFDictionary, &item)
if let data = item as? Data {
return String(data: data, encoding: .utf8)
}
return nil
}
2) Keychain ๋ํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ(๋งํฌ)
import KeychainAccess
let keychain = Keychain(service: "com.example.yegr")
try? keychain.set("yegrPassword123", key: "yegrPassword")
let password = try? keychain.get("yegrPassword")
์ฃผ์์ฌํญ ๋ฐ ์ ์ฉ ์ฌ๋ก
- ๊ผญ ํ์ํ ๋ฏผ๊ฐ ๋ฐ์ดํฐ๋ง ์ ์ฅํ ๊ฒ(๋ถํ์ํ ๋ฐ์ดํฐ๋ UserDefaults)
- ๋น๋ฐ๋ฒํธ ๋์ ์ธ์
ํ ํฐ์ ์ ์ฅํ๋ ๊ฒ์ด ๊ถ์ฅ๋จ
- ์์ฒด ์ธ์ฆ ์คํจ ์ fallback ์ ๋ต ํ์ํจ(ex. ์ฑ ์์ฒด ๋น๋ฐ๋ฒํธ)
- ์ฑ ์ญ์ ํ์๋ Keychain ๋ฐ์ดํฐ๊ฐ ๋จ์ ์ ์์ผ๋ฏ๋ก, ๋ก๊ทธ์์ ์ ๋ฐ๋์ ์ญ์ ์ฒ๋ฆฌ ํ์
- iCloud Keychain์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ๋๊ธฐํ ์ ์ฑ
์ ๋ช
ํํ ์ค๊ณํ ๊ฒ
๊ฒฐ๋ก
Keychain์ iOS์์ ๊ฐ์ฅ ์์ ํ ๋ฐ์ดํฐ ์ ์ฅ์์
๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ณต๊ฐ์ด ์๋๋ผ, ๋ณด์ ์ ์ฑ
๊ณผ ์ธ์ฆ ์ฒด๊ณ๊ฐ ํตํฉ๋ ์ธํ๋ผ๋ผ๊ณ ๋ณผ ์ ์์
๋ฐ๋ผ์ ํ ํฐ, ์ธ์ฆ ์ ๋ณด ๋ฑ ๋ณด์์ด ์ค์ํ ๋ฐ์ดํฐ๋ ๋ฐ๋์ Keychain์ ์ ์ฅํ๊ณ ,
์ผ๋ฐ ์ค์ ๊ฐ์ UserDefaults ํน์ CoreData๋ก ๋๋์ด ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์
๊ฐ์ธ์ ์ธ ์๊ฒฌ
Keychain์ ์ ์ฅ/์กฐํ ์ธก๋ฉด์์๋ UserDefaults์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ณ๋์ ์ฌ์ฉ์ ํ๋กฌํํธ ์์ด ๋ฐ๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
๋ค๋ง UserDefaults๋ณด๋ค ๊ตฌํ์ด ๋ณต์กํ ๋์ , ๋ณด์ ๋จ๊ณ๋ฅผ ์ ํ์ ์ผ๋ก ์ถ๊ฐํ ์ ์๋ค๋ ์ ์ด ์ฅ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์๋ฅผ ๋ค์ด Access Token์ฒ๋ผ ์ ํจ ๊ธฐ๊ฐ์ด ์งง์ ๋ฐ์ดํฐ๋ผ๋ฉด, ๊ตณ์ด FaceID์ ๊ฐ์ ์ถ๊ฐ ์ธ์ฆ์ ๋ถ์ผ ํ์ ์์ด
'kSecAttrAccessibleWhenUnlocked' ์ ๋๋ก '๊ธฐ๊ธฐ ์ ๊ธ ํด์ ์ํ์์๋ง ์ ๊ทผ ๊ฐ๋ฅ'ํ๊ฒ ์ค์ ํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ๋ค๊ณ ์๊ฐํ๋ค.
์ถ๊ฐ์ ์ผ๋ก UserDefaults๋ ์ฑ์ ์ญ์ ํ๋ฉด ๋ฐ์ดํฐ๋ ํจ๊ป ์ฌ๋ผ์ง์ง๋ง, Keychain์ ์ฑ์ ์ง์๋ ๋ฐ์ดํฐ๊ฐ ๋จ์ ์ ์๋ค.
๋ฐ๋ผ์ ํ์์ ๋ฐ๋ผ ์ฑ ์ญ์ ๋ ๋ก๊ทธ์์ ์์ ์ Keychain ๋ฐ์ดํฐ๋ฅผ ๋ช
์์ ์ผ๋ก ์ ๋ฆฌํด์ฃผ๋ ๊ฒ๋ ์ค์ํด๋ณด์ธ๋ค.
์ด ํน์ฑ์ด ์๋ ์ ๊ฒ์ด ๋ ์ ์๋ค๊ณ ์๊ฐํ๋ค.
๋ณด์ ์ธก๋ฉด์์๋ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ์์ ์๋์ ์๊ด์์ด ๊ธฐ๊ธฐ์ ๋จ์ ์๋ค๋ ์ ์ด ๋จ์ ์ด ๋ ์ ์๋ค.
๋ฐ๋ฉด์ ์ฌ์ฉ์ ๊ฒฝํ ์ธก๋ฉด์์๋ ์ฅ์ ์ผ๋ก ์์ฉ๋ ์ ์๋ค๊ณ ์๊ฐํ๋ค.
์๋ฅผ ๋ค์ด ๊ณผ๊ฑฐ์ ์ฌ์ฉ์๊ฐ ์นด์นด์ค/๋ค์ด๋ฒ์ ๊ฐ์ ํน์ ์์
๋ก๊ทธ์ธ์ผ๋ก ๊ฐ์
ํ ์ ์ด ์๋์ง ํ์
ํ ์ ์์ด, ์ฌ๊ฐ์
ํน์ ์ฌ๋ก๊ทธ์ธ ๊ณผ์ ์์ ํด๋น ์ต์
์ ์ฐ์ ์ถ์ฒํ๋ UI๋ฅผ ์ ๊ณตํ ์ ์์ ๊ฒ ๊ฐ๋ค.
๋ค์ ๊ณํ
๊ธฐ์กด์ ์์ฃผ ์ฌ์ฉํ๋ UserDefaultsManager์ฒ๋ผ KeychainManager๋ฅผ ๊ตฌ์ฑํด๋ณผ ์์