๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS/iOS

[iOS] ํ‚ค์ฒด์ธ(Keychain)์„ ์ด์šฉํ•œ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ

์•ˆ๋…•ํ•˜์„ธ์š” ์ œ์ธ์ž…๋‹ˆ๋‹ค :)

์ตœ๊ทผ์— ํ”„๋กœ์ ํŠธ ๊ฐœ๋ฐœ์„ ํ•˜๋ฉฐ ํ† ํฐ๊ณผ ๊ฐ™์€ ์ค‘์š”ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ข€ ๋” ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ณ ์ž ํ‚ค์ฒด์ธ์„ ์ด์šฉํ•ด๋ณด์•˜๋Š”๋ฐ์š”,

ํ‚ค์ฒด์ธ์— ๋Œ€ํ•œ ๋‚ด์šฉ๋„ ์ •๋ฆฌํ•  ๊ฒธ, ํ‚ค์ฒด์ธ์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ๊ณต์œ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค !

Keychain Service

ํ‚ค์ฒด์ธ ์„œ๋น„์Šค๋Š” Apple์ด ์ œ๊ณตํ•˜๋Š” ๋ณด์•ˆ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

๊ณต์‹๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, ํ‚ค์ฒด์ธ ์„œ๋น„์Šค API๋Š” ์•ฑ์— ํ‚ค์ฒด์ธ์ด๋ผ๋Š” ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ž‘์€ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ๋น„ํŠธ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜์—ฌ ์•”ํ˜ธ์™€ ๊ฐ™์ด ๋ณด์•ˆ์— ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.

์ฆ‰, ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ Keychain์— ์ •๋ณด๋ฅผ ์ €์žฅํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

ํ‚ค์ฒด์ธ์—์„œ ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€ ๋ณดํ˜ธ

ํ‚ค์ฒด์ธ์€ ์•”ํ˜ธ์—๋งŒ ๊ตญํ•œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹ ์šฉ ์นด๋“œ ์ •๋ณด๋‚˜ ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ๊ด€์‹ฌ์„ ๊ฐ–๋Š” ๋‹ค๋ฅธ ๋น„๋ฐ€์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ํ•„์š”๋กœ ํ•˜์ง€๋งŒ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋Š” ํ•ญ๋ชฉ์„ ์ €์žฅํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•”ํ˜ธํ™” ํ‚ค ๋ฐ ์ธ์ฆ์„œ๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ๋ณด์•ˆ ํ†ต์‹ ์— ์ฐธ์—ฌํ•˜๊ณ  ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๋ฐ ์žฅ์น˜์™€ ์‹ ๋ขฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Keychain์˜ ํŠน์ง•

  • iOS์—์„œ ์•ฑ์€ ๋‹จ์ผ ํ‚ค์ฒด์ธ(๋…ผ๋ฆฌ์ ์œผ๋กœ iCloud ํ‚ค์ฒด์ธ์„ ํฌํ•จํ•จ)์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • iOS์—์„œ ํ‚ค์ฒด์ธ์˜ ์œ„์น˜๋Š” ์ƒŒ๋“œ๋ฐ•์Šค ์™ธ๋ถ€์ด๋ฏ€๋กœ, ์•ฑ์„ ์‚ญ์ œํ•ด๋„ ํ‚ค์ฒด์ธ์— ์ €์žฅ๋œ ์ •๋ณด๋Š” ์‚ญ์ œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ‚ค์ฒด์ธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์žฅ์น˜์˜ ์ž ๊ธˆ์„ ํ•ด์ œํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ž ๊ธˆ ํ•ด์ œ๋˜๊ณ  ์žฅ์น˜๊ฐ€ ์ž ๊ธฐ๋ฉด ์ž ๊น๋‹ˆ๋‹ค. 
  • ์•ฑ์€ ์ž์‹ ์˜ ํ‚ค์ฒด์ธ ํ•ญ๋ชฉ ๋˜๋Š” ์•ฑ์ด ์†ํ•œ ๊ทธ๋ฃน๊ณผ ๊ณต์œ ๋œ ํ•ญ๋ชฉ์—๋งŒ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
  • ํ‚ค์ฒด์ธ ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Keychain Items

ํ‚ค์ฒด์ธ์— ์ €์žฅ๋˜๋Š” ๊ธฐ๋ฐ€ ์ •๋ณด๋Š” item์œผ๋กœ ํŒจํ‚ค์ง•ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ‚ค์ฒด์ธ์— ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜๋Š” ๋‹จ์œ„๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ‚ค์ฒด์ธ์€ ํ•˜๋‚˜ ์ด์ƒ์˜ Keychain Item์„ ๊ฐ–์Šต๋‹ˆ๋‹ค. data ์ž์ฒด์™€ ํ•จ๊ป˜ ๊ณต๊ฐœ์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š” attribute๋ฅผ ์ œ๊ณตํ•˜์—ฌ item์˜ ์ ‘๊ทผ์„ฑ์„ ์ œ์–ดํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. 

 

์œ„์˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ํ‚ค์ฒด์ธ ์„œ๋น„์Šค๋Š” ๋””์Šคํฌ์— ์ €์žฅ๋œ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ ํ‚ค์ฒด์ธ์—์„œ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™” ๋ฐ ์ €์žฅ(attributes ํฌํ•จ)์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ๋œ ํ”„๋กœ์„ธ์Šค๋Š” ํ‚ค์ฒด์ธ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ญ๋ชฉ์„ ์ฐพ๊ณ  ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด๋…ํ•ฉ๋‹ˆ๋‹ค.

Keychain Item Class

Keychain item์€ ์•”ํ˜ธ, ์•”ํ˜ธ ํ‚ค ๋ฐ ์ธ์ฆ์„œ์™€ ๊ฐ™์ด ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ํด๋ž˜์Šค๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. 

item์˜ ํด๋ž˜์Šค๋Š” ์ ์šฉ๋˜๋Š” attribute๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ  ์‹œ์Šคํ…œ์ด ๋””์Šคํฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ๋ฅผ ๋“ค์–ด, ์•”ํ˜ธ๋Š” ์•”ํ˜ธํ™”๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ ์ธ์ฆ์„œ๋Š” ๋น„๋ฐ€์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

key(kSecClass)-value ์Œ(Dictionary ํƒ€์ž…)์„ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด item์˜ ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•˜๊ณ , item์„ ์กฐํšŒ(์—…๋ฐ์ดํŠธ or ์‚ญ์ œ ์‹œ)ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์ด์™€ ๋™์ผํ•œ ์Œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

Keychain์— ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ข…๋ฅ˜(value๊ฐ’์— ํ•ด๋‹น)

  • kSecClassGenericPassword : ์ผ๋ฐ˜ ์•”ํ˜ธ ํ•ญ๋ชฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’
  • kSecClassInternetPassword : ์ธํ„ฐ๋„ท ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ญ๋ชฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’
  • kSecClassCertificate : ์ธ์ฆ์„œ ํ•ญ๋ชฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’
  • kSecClassIdentity: ID ํ•ญ๋ชฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’
  • kSecClassKey : ์•”ํ˜ธํ™” ํ‚ค ํ•ญ๋ชฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’

Keychain์„ ์ด์šฉํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ด€๋ฆฌ

ํ‚ค์ฒด์ธ์— ์ธํ„ฐ๋„ท ๋น„๋ฐ€๋ฒˆํ˜ธ ์ €์žฅ ์˜ˆ์‹œ

1. ์•ฑ์„ ์ฒ˜์Œ ์‹คํ–‰ํ•œ ๊ฒฝ์šฐ(์ธ์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ)

์•ฑ์„ ์ฒ˜์Œ ์‹คํ–‰ํ–ˆ๋‹ค๋ฉด Keychain์— ์–ด๋– ํ•œ ์ •๋ณด๋„ ์ €์žฅ๋˜์–ด์žˆ์ง€ ์•Š์€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

SecItemCopyMatcing(_:_:) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ๊ฒ€์ƒ‰์„ ํ•˜๋ฉด ๋งž๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ๋‹ค์ด์–ด๊ทธ๋žจ์˜ ์˜ค๋ฅธ์ชฝ ํ”Œ๋กœ์šฐ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ธ์ฆํ•˜๋Š” credential์„ ์ œ๊ณตํ•˜๋ฉด SecItemAdd(_:_:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์•ฑ์€ ์ผ๋ฐ˜ ๋„คํŠธ์›Œํฌ ์•ก์„ธ์Šค๋ฅผ ๊ณ„์†ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ์„œ๋ฒ„๊ฐ€ ์žฌ์ธ์ฆ์„ ์š”๊ตฌํ•  ๋•Œ ์•ฑ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ธ์ฆ์„ ์š”๊ตฌํ•˜๋Š” ๋Œ€์‹  ํ‚ค์ฒด์ธ์—์„œ credential์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

2. ์ธ์ฆ์— ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ

์•ฑ์„ ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ค‘๊ฐ„ ํ”Œ๋กœ์šฐ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ๋Š” ์‚ฌ์šฉ์ž ์ƒํ˜ธ ์ž‘์šฉ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

SecItemCopyMatching(_:_:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค์ฒด์ธ์—์„œ ์•”ํ˜ธ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ์•”ํ˜ธ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์–ด ์•ฑ์ด ์ธ์ฆ์— ์„ฑ๊ณตํ•œ๋‹ค๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€์—ฌํ•˜์ง€ ์•Š๊ณ  ๊ณ„์† ์ž‘์—…์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

3. ์ธ์ฆ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ

๋•Œ๋•Œ๋กœ ์‚ฌ์šฉ์ž๋Š” ์•ฑ์˜ ๋ฒ”์œ„ ๋ฐ–์—์„œ credential์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•ฑ์„ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ ํ•ด๋‹น ์„œ๋น„์Šค์˜ ์›น์‚ฌ์ดํŠธ์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ฐ”๋€ ์ดํ›„์—๋Š” Keychain์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ๋”๋ผ๋„ ์ธ์ฆ์—์„œ ์‹คํŒจํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์™ผ์ชฝ ํ”Œ๋กœ์šฐ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”Œ๋กœ์šฐ์—์„œ๋Š” SecItemUpdate(_:_:) ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ์ €์žฅ๋œ value๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ธ์ฆํ•œ credential์˜ value๋กœ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

4. Keychain ๋ฐ์ดํ„ฐ ์‚ญ์ œ

SecItemDelete(_:) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด ํ‚ค์ฒด์ธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์˜ˆ์ œ

ํ‚ค์ฒด์ธ ํด๋ž˜์Šค ์ •์˜

import Foundation
import Security

final class Keychain {
    
    // Create
    static func create(key: String, data: String) {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key,
            kSecValueData: data.data(using: .utf8, allowLossyConversion: false) as Any
        ]
        SecItemDelete(query) // Keychain์€ Key๊ฐ’์— ์ค‘๋ณต์ด ์ƒ๊ธฐ๋ฉด ์ €์žฅํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋จผ์ € Delete
        
        let status = SecItemAdd(query, nil)
        assert(status == noErr, "Failed to save Token")
    }
    
    // Read
    static func read(key: String) -> String? {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key,
            kSecReturnData: kCFBooleanTrue as Any,
            kSecMatchLimit: kSecMatchLimitOne
        ]
        
        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(query, &dataTypeRef)
        
        if status == errSecSuccess {
            if let retrievedData: Data = dataTypeRef as? Data {
                let value = String(data: retrievedData, encoding: String.Encoding.utf8)
                return value
            } else { return nil }
        } else {
            #if DEBUG
            print("Failed to loading, status code = \(status)")
            #endif
            return nil
        }
    }
    
    // Delete
    static func delete(key: String) {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key
        ]
        let status = SecItemDelete(query)
        assert(status == noErr, "Failed to delete the value, status code = \(status)")
    }
}

ํ‚ค์ฒด์ธ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

// AuthAPIService
func login() -> Single<Void> {
    return oauthService
        .authorize()
        .do { oauthAuthentication in
            #if DEBUG
            print("โœจ OAuth ์ธ์ฆ ์„ฑ๊ณต) \(oauthAuthentication)")
            #endif
                
            let authType = oauthAuthentication.oauthType.rawValue
            Keychain.create(key: Keychain.Keys.socialType, data: authType)
        }
        .map { $0.toSignInRequestDTO() }
        .flatMap(signIn)
        .do { dto in
            if let dto {
                UserDefaults.userId = dto.id
                // ํ‚ค์ฒด์ธ์— ํ† ํฐ ์ €์žฅ
                Keychain.create(key: Keychain.Keys.accessToken, data: dto.accessToken)
                Keychain.create(key: Keychain.Keys.refreshToken, data: dto.refreshToken)
            } else {
                #if DEBUG
                print("ํฌ๊ทธํฌ๊ทธ ์„œ๋ฒ„ ์ธ์ฆ ์‹คํŒจ")
                #endif
                // TODO: ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ํŒ์—… ๋„์›Œ์ฃผ๊ธฐ
            }
        }
        .map { _ in () }
}

// DefaultAppCoordinator
func start() {
        
    // ์•ฑ์˜ ์‹œ์ž‘์  ์„ค์ •(keychain์˜ accessToken ์ €์žฅ ์—ฌ๋ถ€ ํ™•์ธ)
    if Keychain.read(key: Keychain.Keys.accessToken) != nil {
        showMapFlow()
    } else {
        showLoginFlow()
    }
}

 

Keychain ํด๋ž˜์Šค์— ํ‚ค์ฒด์ธ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฉ”์„œ๋“œ๋“ค์„ ์ •์˜ํ•ด๋‘๊ณ  ํ•„์š”ํ•œ ๊ณณ์—์„œ ์‚ฌ์šฉํ•ด์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ํ† ํฐ ์ €์žฅ ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ์š”, ๋” ์ด์ƒ ์ƒ์†๋˜์ง€ ์•Š์„ ํด๋ž˜์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— final ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ฃผ์—ˆ๊ณ , ํ‚ค์ฒด์ธ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋“ค๋„ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์žฌ์ •์˜ํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— static func์œผ๋กœ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ‚ค์ฒด์ธ ๋ฉ”์„œ๋“œ ํ™œ์šฉ๋ฐฉ๋ฒ•์€ ๊ณต์‹๋ฌธ์„œ์— ์ž์„ธํžˆ ๋‚˜์™€์žˆ์œผ๋‹ˆ ๊ณต์‹๋ฌธ์„œ๋งŒ ์ฐธ๊ณ ํ•ด๋„ ์ดํ•ดํ•˜๊ณ  ํ‚ค์ฒด์ธ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค! 

ex) Adding a Password to the Keychain

 

Adding a Password to the Keychain | Apple Developer Documentation

Add network credentials to the keychain on behalf of the user.

developer.apple.com


[์ฐธ๊ณ  ์ž๋ฃŒ]

 

Keychain Services | Apple Developer Documentation

Securely store small chunks of data on behalf of the user.

developer.apple.com

 

[iOS] Keychain์„ ์‚ฌ์šฉํ•ด๋ณด์ž ๐Ÿ”

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๊ตฌํ˜„ํ–ˆ๋˜ ๊ฒƒ๋“ค ์ค‘ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋“ค์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๋งŒํ•œ ์œ ์šฉํ•œ ๊ฒƒ๋“ค์„ ์ •๋ฆฌํ•˜๋ คํ•œ๋‹ค. ๊ทธ ์ฒซ๋ฒˆ์งธ๋กœ Apple์—์„œ ์ œ๊ณตํ•˜๋Š” Keychain์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด๋ณด์ž. ๊ธฐ์กด UserDefault

velog.io

 

[iOS] ํ† ํฐ ๋ฐ์ดํ„ฐ ์ €์žฅ ๊ณต๊ฐ„์„ Keychain์œผ๋กœ ๋ฐ”๊ฟ”๋ณด์ž

๋ณธ๊ฒฉ ๋–ก๋ฐฅ ํšŒ์ˆ˜ ํ”„๋กœ์ ํŠธ(?) [iOS] Access Token๊ณผ Refresh Token, ๊ทธ๋ฆฌ๊ณ  Auto Login๊นŒ์ง€ ์ด๋ฒˆ ๋‚˜๋‹ค NADA ์–ดํ”Œ ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ์ค€๋น„ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ๋งŽ์ด ๊ณต๋ถ€ํ•œ ๋ถ€๋ถ„์ด "๋กœ๊ทธ์ธ"๊ณผ ๊ด€๋ จ๋œ ๋ถ€๋ถ„์ผ ๊ฑฐ๋‹ค. ์ฒ˜์Œ ์•„์š”๋ผ๋ฆฌ ๋‹ด

mini-min-dev.tistory.com

 

'๐ŸŽ iOS > iOS' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[iOS] TDD์™€ Unit Test  (3) 2023.12.14
[iOS] iOS ํŒŒ์ผ ์‹œ์Šคํ…œ  (1) 2023.11.28
[iOS/Architecture] Coordinator Pattern  (2) 2022.12.11
[iOS] ReactorKit์ด๋ž€?  (4) 2022.08.03
[iOS] UserDefaults๋ž€?  (3) 2022.01.06