Skip to content

Commit

Permalink
๐ŸŽจ :: [#48] Add Sigin ViewModel error code ์˜ˆ์™ธ์ฒ˜๋ฆฌ
Browse files Browse the repository at this point in the history
  • Loading branch information
Xixn2 committed Nov 10, 2024
1 parent b4e9da0 commit f14b7b2
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 21 deletions.
65 changes: 55 additions & 10 deletions Projects/App/Sources/Extension/EXPOToken.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,45 @@ import Domain
import Security
import Foundation

// ํ† ํฐ๊ณผ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ํฌํ•จํ•˜๋Š” ๊ตฌ์กฐ์ฒด
struct TokenData: Codable {
let token: String
let expirationDate: Date
}

public class KeyChain {
public static let shared = KeyChain()

// ํ† ํฐ ์ €์žฅํ•˜๊ธฐ (๋งŒ๋ฃŒ ๊ธฐ๊ฐ„๊ณผ ํ•จ๊ป˜)
func create(key: String, token: String) {
let query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: token.data(using: .utf8, allowLossyConversion: false) as Any
]
SecItemDelete(query)
let status = SecItemAdd(query, nil)
assert(status == noErr, "failed to save Token")
// ๋จผ์ € ํ† ํฐ์„ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ์„ค์ •
if let tokenData = token.data(using: .utf8) {
let query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: tokenData
]
SecItemDelete(query) // ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์‚ญ์ œ
let status = SecItemAdd(query, nil)
assert(status == noErr, "failed to save Token")
}
}

// ํ† ํฐ๊ณผ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ JSON์œผ๋กœ ์ €์žฅํ•˜๊ธฐ
func saveTokenWithExpiration(key: String, token: String, expiresIn: TimeInterval) {
let expirationDate = Date().addingTimeInterval(expiresIn) // ๋งŒ๋ฃŒ์ผ ๊ณ„์‚ฐ
let tokenData = TokenData(token: token, expirationDate: expirationDate)

if let encodedData = try? JSONEncoder().encode(tokenData),
let tokenString = String(data: encodedData, encoding: .utf8) {
create(key: key, token: tokenString)
print("Token ์ €์žฅ ์™„๋ฃŒ: \(tokenString)")
} else {
print("TokenData ์ธ์ฝ”๋”ฉ ์‹คํŒจ")
}
}

// ์ €์žฅ๋œ ํ† ํฐ ์ฝ๊ธฐ (๋งŒ๋ฃŒ ๊ธฐ๊ฐ„ ํฌํ•จ)
public func read(key: String) -> String? {
let query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
Expand All @@ -38,7 +63,7 @@ public class KeyChain {

if status == errSecSuccess {
if let retrievedData: Data = dataTypeRef as? Data {
let value = String(data: retrievedData, encoding: String.Encoding.utf8)
let value = String(data: retrievedData, encoding: .utf8)
return value
} else { return nil }
} else {
Expand All @@ -47,9 +72,27 @@ public class KeyChain {
}
}

// ํ† ํฐ๊ณผ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ํ•จ๊ป˜ ์ฝ๊ธฐ
func loadTokenWithExpiration(key: String) -> TokenData? {
if let tokenString = read(key: key),
let tokenData = tokenString.data(using: .utf8) {
return try? JSONDecoder().decode(TokenData.self, from: tokenData)
}
return nil
}

// ๋งŒ๋ฃŒ ์—ฌ๋ถ€ ํ™•์ธ
func isTokenExpired(key: String) -> Bool {
if let tokenData = loadTokenWithExpiration(key: key) {
return tokenData.expirationDate < Date() // ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ ๋งŒ๋ฃŒ์ผ ๋น„๊ต
}
return true // ํ† ํฐ์ด ์—†์œผ๋ฉด ๋งŒ๋ฃŒ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ
}

// ํ† ํฐ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ
func updateItem(token: Any, key: Any) -> Bool {
let prevQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key]
kSecAttrAccount: key]
let updateQuery: [CFString: Any] = [kSecValueData: (token as AnyObject).data(using: String.Encoding.utf8.rawValue) as Any]

let result: Bool = {
Expand All @@ -63,6 +106,7 @@ public class KeyChain {
return result
}

// ํ† ํฐ ์‚ญ์ œ
func delete(key: String) {
let query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
Expand All @@ -83,3 +127,4 @@ public struct Const {
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,88 @@ public final class AuthViewModel: ObservableObject {
let param = SigninRequest(nickname: email, password: password)
authProvider.request(.signIn(param: param)) { [weak self] response in
guard let self = self else { return }

switch response {
case .success(let result):
let statusCode = result.statusCode
do {
switch statusCode {
case 200:
print(self.password) // ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ถœ๋ ฅ (ํ…Œ์ŠคํŠธ ์šฉ๋„๋ผ๋ฉด ๋‚˜์ค‘์— ์‚ญ์ œ)

// ์„œ๋ฒ„ ์‘๋‹ต์„ SigninResponse ํƒ€์ž…์œผ๋กœ ๋งคํ•‘
let signInResponse = try result.map(SigninResponse.self)

// KeyChain์— ํ† ํฐ ์ €์žฅ
KeyChain.shared.create(key: Const.KeyChainKey.accessToken, token: signInResponse.accessToken)
KeyChain.shared.create(key: Const.KeyChainKey.refreshToken, token: signInResponse.refreshToken)
KeyChain.shared.saveTokenWithExpiration(
key: Const.KeyChainKey.accessToken,
token: signInResponse.accessToken,
expiresIn: Double(signInResponse.accessTokenExpiresIn) ?? 3600
)

// ํ† ํฐ ์ถœ๋ ฅ
print("Access Token: \(signInResponse.accessToken)")
print("Refresh Token: \(signInResponse.refreshToken)")
KeyChain.shared.saveTokenWithExpiration(
key: Const.KeyChainKey.refreshToken,
token: signInResponse.refreshToken,
expiresIn: Double(signInResponse.refreshTokenExpiresIn) ?? 2592000
)

// ์„ฑ๊ณต ์‹œ ํ† ํฐ๊ณผ ํ•จ๊ป˜ completion ํ˜ธ์ถœ
DispatchQueue.main.async {
completion(statusCode, signInResponse.accessToken, signInResponse.refreshToken)
}

if let savedAccessToken = KeyChain.shared.read(key: Const.KeyChainKey.accessToken),
let savedRefreshToken = KeyChain.shared.read(key: Const.KeyChainKey.refreshToken) {
print("Access Token ์ €์žฅ๋จ: \(savedAccessToken)")
print("Refresh Token ์ €์žฅ๋จ: \(savedRefreshToken)")

if let accessTokenExpiresIn = Double(signInResponse.accessTokenExpiresIn),
let refreshTokenExpiresIn = Double(signInResponse.refreshTokenExpiresIn) {

let accessTokenExpirationDate = Date(timeIntervalSinceNow: accessTokenExpiresIn)
let refreshTokenExpirationDate = Date(timeIntervalSinceNow: refreshTokenExpiresIn)

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.current

print("Access Token ๋งŒ๋ฃŒ ๋‚ ์งœ: \(dateFormatter.string(from: accessTokenExpirationDate))")
print("Refresh Token ๋งŒ๋ฃŒ ๋‚ ์งœ: \(dateFormatter.string(from: refreshTokenExpirationDate))")
} else {
print("๋งŒ๋ฃŒ ์‹œ๊ฐ„ ๋ณ€ํ™˜ ์˜ค๋ฅ˜: accessTokenExpiresIn ๋˜๋Š” refreshTokenExpiresIn์ด Double๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š์Œ.")
print("accessTokenExpiresIn: \(signInResponse.accessTokenExpiresIn), refreshTokenExpiresIn: \(signInResponse.refreshTokenExpiresIn)")

let defaultDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.current
print("๊ธฐ๋ณธ ๋งŒ๋ฃŒ ๋‚ ์งœ (ํ˜„์žฌ ์‹œ๊ฐ„): \(dateFormatter.string(from: defaultDate))")
}
} else {
print("ํ† ํฐ ์ €์žฅ ์‹คํŒจ: Keychain์—์„œ ๊ฐ’์„ ์ฝ์–ด์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
}

case 400:
print("400ใ…ฃ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
DispatchQueue.main.async {
completion(statusCode, nil, nil)
}

case 403:
print("403ใ…ฃ์•„์ง ๋ณด๋ฅ˜์ค‘์ž…๋‹ˆ๋‹ค.")
DispatchQueue.main.async {
completion(statusCode, nil, nil)
}

case 404:
print("404ใ…ฃ๊ณ„์ •์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
DispatchQueue.main.async {
completion(statusCode, nil, nil)
}

case 500..<600:
print("\(statusCode)ใ…ฃ์„œ๋ฒ„์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.")
DispatchQueue.main.async {
completion(statusCode, nil, nil)
}

default:
print("Received unexpected status code: \(statusCode)")
print("Unhandled status code: \(statusCode)")
DispatchQueue.main.async {
completion(statusCode, nil, nil)
}
Expand All @@ -130,6 +186,7 @@ public final class AuthViewModel: ObservableObject {
}
}


// MARK: - Sign Up
func signUp(completion: @escaping (Bool) -> Void) {
let params = SignupRequest(name: name, nickname: nickname, email: email, password: password, phoneNumber: phoneNumber)
Expand Down

0 comments on commit f14b7b2

Please sign in to comment.