-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from Student-Center/feat/network
[WEAV-17] Alamofire 네트워크 레이어 구축
- Loading branch information
Showing
12 changed files
with
470 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// | ||
// AuthStatus.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/25/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public class AuthState: ObservableObject { | ||
|
||
public enum State { | ||
case loggedIn | ||
case loggedOut | ||
} | ||
|
||
public static let shared = AuthState() | ||
|
||
private init() {} | ||
|
||
@Published public private(set) var current: State = .loggedOut | ||
|
||
public func updateAuthState(to status: State) { | ||
self.current = status | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
Projects/Core/NetworkKit/Sources/AuthService/AuthEndpoint.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// | ||
// AuthEndpoint.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/25/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Alamofire | ||
|
||
public enum AuthEndpoint { | ||
case refreshToken | ||
} | ||
|
||
extension AuthEndpoint: EndpointType { | ||
|
||
public var method: HTTPMethod { | ||
switch self { | ||
case .refreshToken: return .post | ||
} | ||
} | ||
|
||
public var path: String { | ||
switch self { | ||
case .refreshToken: return "auth/refresh-token" | ||
} | ||
} | ||
|
||
public var parameters: Encodable? { | ||
switch self { | ||
case .refreshToken: | ||
if let refreshToken = TokenManager.refreshToken { | ||
return ["refreshToken": refreshToken] | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
public var body: Encodable? { | ||
return nil | ||
} | ||
} | ||
|
||
struct TokenResponse: Decodable { | ||
let accessToken: String | ||
let refreshToken: String | ||
} |
38 changes: 38 additions & 0 deletions
38
Projects/Core/NetworkKit/Sources/ExampleService/ExampleEndpoint.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// | ||
// ExampleEndpoint.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/24/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Alamofire | ||
|
||
public enum ExampleEndpoint { | ||
case example | ||
} | ||
|
||
extension ExampleEndpoint: EndpointType { | ||
|
||
|
||
public var method: HTTPMethod { | ||
switch self { | ||
case .example: return .get | ||
} | ||
} | ||
|
||
public var path: String { | ||
switch self { | ||
case .example: return "api/" | ||
} | ||
} | ||
|
||
public var parameters: Encodable? { | ||
return nil | ||
} | ||
|
||
public var body: Encodable? { | ||
return nil | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
Projects/Core/NetworkKit/Sources/NetworkCore/EndpointType.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// EndpointType.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/24/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Alamofire | ||
|
||
public protocol EndpointType { | ||
var method: HTTPMethod { get } | ||
var path: String { get } | ||
var parameters: Encodable? { get } | ||
var body: Encodable? { get } | ||
func request<T: Decodable>(_ type: T.Type) async throws -> T | ||
func request() async throws | ||
} |
70 changes: 70 additions & 0 deletions
70
Projects/Core/NetworkKit/Sources/NetworkCore/Interceptor.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// | ||
// Interceptor.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/25/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import CoreKit | ||
import Alamofire | ||
|
||
class AuthInterceptor: RequestInterceptor { | ||
|
||
private let retryLimit = 3 | ||
private let retryDelay: TimeInterval = 1.5 | ||
|
||
func adapt( | ||
_ urlRequest: URLRequest, | ||
for session: Session, | ||
completion: @escaping (Result<URLRequest, Error>) -> Void | ||
) { | ||
var urlRequest = urlRequest | ||
|
||
// AccessToken 추가 | ||
if let token = TokenManager.accessToken { | ||
urlRequest.setValue( | ||
"Bearer \(token)", | ||
forHTTPHeaderField: "Authorization" | ||
) | ||
} | ||
|
||
completion(.success(urlRequest)) | ||
} | ||
|
||
func retry( | ||
_ request: Request, | ||
for session: Session, | ||
dueTo error: Error, | ||
completion: @escaping (RetryResult) -> Void | ||
) { | ||
guard let response = request.task?.response as? HTTPURLResponse, | ||
response.statusCode == 401, | ||
request.retryCount < retryLimit else { | ||
completion(.doNotRetry) | ||
return | ||
} | ||
|
||
// refresh 토큰 갱신 후 retry | ||
Task { | ||
do { | ||
try await refreshAccessToken() | ||
completion(.retryWithDelay(retryDelay)) | ||
} catch { | ||
await MainActor.run { | ||
AuthState.shared.updateAuthState(to: .loggedOut) | ||
} | ||
completion(.doNotRetry) | ||
} | ||
} | ||
} | ||
|
||
private func refreshAccessToken() async throws { | ||
let endpoint = AuthEndpoint.refreshToken | ||
let response = try await endpoint.request(TokenResponse.self) | ||
|
||
TokenManager.accessToken = response.accessToken | ||
TokenManager.refreshToken = response.refreshToken | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// | ||
// Logger.swift | ||
// NetworkKit | ||
// | ||
// Created by 김지수 on 8/25/24. | ||
// Copyright © 2024 com.studentcenter. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Alamofire | ||
|
||
class NetworkLogger: EventMonitor { | ||
var queue: DispatchQueue = DispatchQueue(label: "myNetworkLogger") | ||
|
||
func requestDidFinish(_ request: Request) { | ||
print("") | ||
print("======================== 👉 Network Request Log 👈 ==========================") | ||
debugPrint("✅ [URL] : \(request.request?.url?.absoluteString ?? "")") | ||
debugPrint("✅ [Method] : \(request.request?.httpMethod ?? "")") | ||
debugPrint("✅ [Headers] : \(request.request?.allHTTPHeaderFields ?? [:])") | ||
|
||
if let body = request.request?.httpBody?.toPrettyPrintedString { | ||
debugPrint("✅ [Body] : \(body)") | ||
} else { | ||
debugPrint("✅ [Body] : body 없음") | ||
} | ||
print("==============================================================================") | ||
print("") | ||
} | ||
|
||
func request<Value>( | ||
_ request: DataRequest, | ||
didParseResponse response: DataResponse<Value, AFError> | ||
) { | ||
print("") | ||
print("======================== 👉 Network Response Log 👈 ========================") | ||
|
||
switch response.result { | ||
case .success: | ||
debugPrint("✅ [StatusCode] : \(response.response?.statusCode ?? 0)") | ||
case .failure: | ||
debugPrint("🚨 요청 실패") | ||
} | ||
|
||
|
||
if let statusCode = response.response?.statusCode { | ||
switch statusCode { | ||
case 400..<500: | ||
debugPrint("🚨 클라이언트 오류") | ||
case 500..<600: | ||
debugPrint("🚨 서버 오류") | ||
default: | ||
break | ||
} | ||
} | ||
|
||
if let response = response.data?.toPrettyPrintedString { | ||
debugPrint("✅ [Response] : \(response)") | ||
} | ||
|
||
print("============================================================================") | ||
print("") | ||
} | ||
} | ||
|
||
|
||
fileprivate extension Data { | ||
var toPrettyPrintedString: String? { | ||
guard let object = try? JSONSerialization.jsonObject(with: self, options: []), | ||
let data = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted]), | ||
let prettyPrintedString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) | ||
else { | ||
return nil | ||
} | ||
return prettyPrintedString as String | ||
} | ||
} |
Oops, something went wrong.