ํฐ์คํ ๋ฆฌ ๋ทฐ
Clean Architecture ๊ธฐ๋ฐํ์ฌ
๋คํธ์ํฌ๋ฅผ ์ถ์ํ, ๋ชจ๋ํํ๋ ์ฝ๋๋ฅผ ์ ๋ฆฌํ๋ ค๊ณ ํฉ๋๋ค
Clean Architecture์์ ๋คํธ์ํฌ ๊ณ์ธต์ ๋ค์๊ณผ ๊ฐ์ ๋ ์ด์ด๋ก ๋๋ ์ ์์ต๋๋ค
- Domain Layer: ์ฑ์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๊ท์น์ ์ ์ํฉ๋๋ค
- Data Layer: ์ธ๋ถ ๋ฐ์ดํฐ ์์ค(๋คํธ์ํฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ)์์ ์ํธ์์ฉ์ ์ฒ๋ฆฌํฉ๋๋ค
- Presentation Layer: UI์ ์ํธ์์ฉํ๋ฉฐ, ์ฌ์ฉ์์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋๋ค
์ด์ค์์ network ๋ก์ง์ Data Layer์ ํฌํจ๋ฉ๋๋ค.
๋จผ์
๋คํธ์ํฌ ์ธ์ ์ ๋์ ๊ตฌํํ๋ URLSessionWrapper๋ฅผ ์์ฑํฉ๋๋ค.
import Foundation
public protocol SessionProtocol {
func performRequest(_ url: URLRequest) async throws -> (Data, URLResponse)
}
public class URLSessionWrapper: SessionProtocol {
private let session: URLSession
public init(session: URLSession = .shared) {
self.session = session
}
public func performRequest(_ url: URLRequest) async throws -> (Data, URLResponse) {
return try await session.data(for: url)
}
}
SessionProtocol๋ฅผ ์ฑํํ๋ URLSessionWrapper๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ
์ถํ์ SessionProtocol๋ก ๋ง๋ MockSession์ผ๋ก
URLSessionWrapper๋์ ์ฌ์ฉํ์ฌ ํ ์คํธ ๊ฐ๋ฅํ๋๋ก ํฉ๋๋ค
๊ทธ ๋ค์ ์ค์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ NetworkManger ์ ๋๋ค.
import Foundation
public enum NetworkError: Error {
case invalidURL
case requestFailed(String)
case dataNil
case invalidResponse
case decodingFailed(String)
case serverError(Int)
}
public protocol NetworkManagerProtocol {
func executeRequest<T: Decodable>(url: String,
method: String,
parameters: [String: Any]?) async -> Result<T, NetworkError>
}
public class NetworkManager: NetworkManagerProtocol {
private let session: SessionProtocol
public init(session: SessionProtocol) {
self.session = session
}
public func executeRequest<T: Decodable>(url: String,
method: String,
parameters: [String: Any]?) async -> Result<T, NetworkError> {
guard let url = URL(string: url) else {
return .failure(.invalidURL)
}
var request = URLRequest(url: url)
request.httpMethod = method
if let parameters = parameters {
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: [])
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
do {
let (data, response) = try await session.performRequest(request)
guard let httpResponse = response as? HTTPURLResponse else {
return .failure(.invalidResponse)
}
if (200..<300).contains(httpResponse.statusCode) {
let decodedData = try JSONDecoder().decode(T.self, from: data)
return .success(decodedData)
} else {
return .failure(.serverError(httpResponse.statusCode))
}
} catch {
return .failure(.requestFailed(error.localizedDescription))
}
}
}
SessionProtocol์ ์์กด์ฑ์ผ๋ก ์ฃผ์ ๋ฐ์์
์ค์ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ๋ NetworkManager ํด๋์ค๋ฅผ ๋ง๋ค์ด์ค๋๋ค
NetworkManagerProtocol์ ํตํด ์ถ์ํ๋์ด ์์ด Mock ๊ฐ์ฒด๋ก ๋์ฒด๊ฐ ๊ฐ๋ฅํฉ๋๋ค
๋ Generic์ ์ฌ์ฉํ์ฌ ๋ค์ํ return ๊ฐ์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค
๊ทธ๋ฆฌ๊ณ
์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋คํธ์ํฌ ์์ฒญ ๋ก์ง UserNetwork๋ฅผ ์ ์ํฉ๋๋ค.
import Foundation
public final class UserNetwork {
private let manager: NetworkManagerProtocol
public init(manager: NetworkManagerProtocol) {
self.manager = manager
}
public func fetchUsers(query: String, page: Int) async -> Result<[User], NetworkError> {
let url = "https://api.github.com/search/users?q=\(query)&page=\(page)"
return await manager.executeRequest(url: url, method: "GET", parameters: nil)
}
}
public struct User: Decodable {
let id: Int
let login: String
}
์ค์ ํธ์ถ์์๋
URLSession์ ๊ตฌํํ๋ URLSessionWrapper() ์ ์ธํ๊ณ
๊ทธ๋ฆฌ๊ณ session์ ์ค์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ NetworkManager์ session๊ฐ์ผ๋ก ๋ฃ์ด์ค๋๋ค
๋ง์ง๋ง์ผ๋ก ํธ์ถ url๋ฅผ ๋ด๊ณ ์๋ UserNetwork ํด๋์ค๋ฅผ ์ด๊ธฐํํด์ค๋๋ค
let session = URLSessionWrapper() // URLSession์ ์ฌ์ฉํ๋ ์ธ์
๊ตฌํ์ฒด
let networkManager = NetworkManager(session: session) // NetworkManager๋ฅผ ๊ตฌ์ฑ
let userNetwork = UserNetwork(manager: networkManager) // UserNetwork ์ด๊ธฐํ
๊ทธ๋ฆฌ๊ณ userNetwork ์ fetchUsers ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ api๋ฅผ ํธ์ถํฉ๋๋ค.
Task {
let query = "john"
let page = 1
let result = await userNetwork.fetchUsers(query: query, page: page)
switch result {
case .success(let users):
print("๊ฒ์๋ ์ฌ์ฉ์ ๋ชฉ๋ก: \(users)")
case .failure(let error):
print("์๋ฌ ๋ฐ์: \(error)")
}
}
๊ทธ ๋ค์์ ํ ์คํธ ์ฝ๋์ ๋๋ค.
Session Protocol์ ์ฑํํ๋ MockSession์ ์์ฑํด์ฃผ๊ณ
import Foundation
public class MockSession: SessionProtocol {
private let mockData: Data
private let mockResponse: URLResponse
public init(mockData: Data, mockResponse: URLResponse) {
self.mockData = mockData
self.mockResponse = mockResponse
}
public func performRequest(_ url: URLRequest) async throws -> (Data, URLResponse) {
return (mockData, mockResponse)
}
}
mockData, mockResponse๋ฅผ returnํ๋ performRequest๋ฅผ ์์ฑํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ํ ์คํธ์์ MockSession์ ์ฌ์ฉํด ๋คํธ์ํฌ ์์ฒญ์ ์๋ฎฌ๋ ์ด์ ํฉ๋๋ค.
import XCTest
final class UserNetworkTests: XCTestCase {
func testFetchUsersSuccess() async {
// Mock ๋ฐ์ดํฐ ์ค์
let mockUserData = """
[
{"id": 1, "login": "user1"},
{"id": 2, "login": "user2"}
]
""".data(using: .utf8)!
let mockResponse = HTTPURLResponse(url: URL(string: "https://api.github.com")!,
statusCode: 200,
httpVersion: nil,
headerFields: nil)!
// MockSession ์์ฑ
let mockSession = MockSession(mockData: mockUserData, mockResponse: mockResponse)
let networkManager = NetworkManager(session: mockSession)
let userNetwork = UserNetwork(manager: networkManager)
// ํ
์คํธ ์คํ
let result = await userNetwork.fetchUsers(query: "test", page: 1)
switch result {
case .success(let users):
XCTAssertEqual(users.count, 2)
XCTAssertEqual(users.first?.login, "user1")
case .failure(let error):
XCTFail("Test failed with error: \(error)")
}
}
func testFetchUsersFailure() async {
// Mock ๋ฐ์ดํฐ ์ค์
let mockResponse = HTTPURLResponse(url: URL(string: "https://api.github.com")!,
statusCode: 500,
httpVersion: nil,
headerFields: nil)!
// MockSession ์์ฑ
let mockSession = MockSession(mockData: Data(), mockResponse: mockResponse)
let networkManager = NetworkManager(session: mockSession)
let userNetwork = UserNetwork(manager: networkManager)
// ํ
์คํธ ์คํ
let result = await userNetwork.fetchUsers(query: "test", page: 1)
switch result {
case .success:
XCTFail("Test should have failed")
case .failure(let error):
XCTAssertEqual(error, .serverError(500))
}
}
}
Clean Architecture ๊ธฐ๋ฐํ์ฌ ๋คํธ์ํฌ ์ถ์ํ ๊ทธ๋ฆฌ๊ณ ํ ์คํธ ๊น์ง ์์ฑํ์์ต๋๋ค.
๊ฐ์ฌํฉ๋๋ค:)
- Total
- Today
- Yesterday
- swift ์์ ์ฝ๊ธฐ
- ๋ ๋์ธ์ด
- filemanager excel read
- swift urlsession network module
- swift filemanager excel
- focus timer ์ดํ
- llm pdf rag
- swift network module
- ๊ณต๋ถ ํ์ด๋จธ ์ดํ
- swift urlsession refactoring
- chatgpt rag llm
- swift network refactoring
- swift network ๊ณตํตํ
- swift excel read
- swift queryitem encode
- rag ๊ธฐ๋ฐ llm ์ฑ๋ด
- swift urlsession ๊ณตํตํ
- swift filemanager get excel
- swift ์๊ฐ
- swift get excel
- ๋ ๋์ธ์ด ์ดํ
- rag llm pdf
- rag ๊ธฐ๋ฐ llm
- swift ๋คํธ์ํฌ ๋ชจ๋ํ
- swift urlcomponent encode
- llm csv
- readysay
- swift urlsession module
- swift ์์ ๊ฐ์ ธ์ค๊ธฐ
- ์๋์ํํธ ๋ ์ด์ธ์ด
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |