1️⃣ URL extension활용하기
- URL extension을 활용하여 필요한 URL마다 추가하는 방법
- 파라미터가 필요하다면 함수로 작성
- 💡 이 방법은 편리하지만 enum을 활용하면 좀 더 Endpoint들을 한눈에 파악하기 편리합니다.
extension URL {
static var recommendations: URL {
makeForEndpoint("recommendations")
}
static func article(withID id: Article.ID) -> URL {
makeForEndpoint("articles/\\(id)")
}
}
private extension URL {
static func makeForEndpoint(_ endpoint: String) -> URL {
URL(string: "<https://api.myapp.com/\\(endpoint)>")!
}
}
2️⃣ Endpoint (enum활용방법)
- enum의 연관값을 활용하여 파라미터를 추가할 수 있습니다.
- 💡 enum을 활용하는 방법은 모든 endpoint를 한 곳에서 정의해야 하므로 실용적이지 않을 수 있습니다. (예를들어, 네트워킹 코드를 별도의 모듈로 추출하고 싶은 경우)
- 즉 case가 추가될 때 마다 enum에서 case를 추가해야 합니다.
enum Endpoint {
case recommendations
case article(id: Article.ID)
case search(query: String, maxResultCount: Int = 100)
}
extension Endpoint {
var url: URL {
switch self {
case .recommendations:
return .makeForEndpoint("recommendations")
case .article(let id):
return .makeForEndpoint("articles/\\(id)")
case .search(let query, let count):
return .makeForEndpoint("search/\\(query)?count=\\(count)")
}
}
}
private extension URL {
static let baseURL = "<https://api.myapp.com>"
static func makeForEndpoint(_ endpoint: String) -> URL {
URL(string: baseURL + endpoint)!
}
}
3️⃣ Endpoint (Struct 활용방법)
- 💡 enum으로 작성할 때 case 추가를 하는것은 명세화 측면이나 휴먼에러 측면에서 파악하기 쉬운 장점이 있습니다.
struct Endpoint {
var path: String
var queryItems: [URLQueryItem] = []
}
extension Endpoint {
var url: URL {
var components = URLComponents()
components.scheme = "https"
components.host = "api.myapp.com"
components.path = "/" + path
components.queryItems = queryItems
guard let url = components.url else {
preconditionFailure(
"Invalid URL components: \\(components)"
)
}
return url
}
}
extension Endpoint {
static var recommendations: Self {
Endpoint(path: "recommendations")
}
static func article(withID id: Article.ID) -> Self {
Endpoint(path: "articles/\\(id)")
}
static func search(for query: String,
maxResultCount: Int = 100) -> Self {
Endpoint(
path: "search/\\(query)",
queryItems: [URLQueryItem(
name: "count",
value: String(maxResultCount)
)]
)
}
}
4️⃣ Endpoint (Protocol 활용방법)
- Path프로토콜을 활용하여 CustomURL프로토콜을 만들면 추후에 TDD를 수행할 때 sampleData와 url을 불러와서 사용하기 편리할 것 같다.
enum Endpoint {
case recommendations
case article(id: Article.ID)
case search(query: String, maxResultCount: Int = 100)
}
protocol Path {
var path : String { get }
}
extension Endpoint : Path {
var path: String {
switch self {
case .recommendations: return "/recommendations"
case .article(let id): return "/articles/\\(id)"
case .search(let query, let count): return "search/\\(query)?count=\\(count)"
}
}
}
protocol CustomURL: Path {
var baseURL: URL { get }
var sampleData: String { get }
}
extension Endpoint: CustomURL {
var baseURL: URL { return URL(string: "<https://api.myapp.com>")! }
var sampleData: String {
switch self {
case .recommendations: return "recommendations Sample Data"
case .article(let id): return "{id: \\"\\(id)\\"}"
case .search(let query, let count): return "{query: \\"\\(query)\\", count: \\"\\(count)\\"}"
}
}
}
func url(_ route: CustomURL) -> URL {
return route.baseURL.appendingPathComponent(route.path)
}
let sample: Endpoint = .recommendations
print(url(sample))
🤔 고민한 부분
Endpoint를 관리하는 방법이 여러방법이 있는데 각각의 장단점은 무엇일까?
- url extension
- 장점 : 가장 간편하게 사용할 수 있다.
- 단점 : extension에 작성되어 있어 한눈에 파악하기 어려울 것 같다.
- enum
- 장점 : 명세화 측면이나 휴먼에러측면에서 case를 통해 한눈에 파악하기 쉬울 것 같다. , 연관값이 생기면서 쿼리를 추가하기도 용이해졌다.
- 단점 : 만약 네트워킹 코드를 별도의 모듈로 추출하는 경우 모든 endpoint를 한곳에서 정의해야 하므로 불편함이 있을 것 같다.
- struct
- 장점 : enum의 단점을 해결할 수 있다. let temp = Endpoint(path: "temp") 식으로 어디서든 추가 가능
- 단점 : enum의 장점, 명세화측면에서 불편할 수도 있을 것 같다. Endpoint 구조체인스턴스를 enum으로 묶으면 해결될 것 같다.
endpoint와 url만 관리할 때는 특별한 경우가 아니라면 enum을 사용할 것같다. moya같은 라이브러리도 비슷한 방식으로 관리한다고 합니다.
🔗 참고링크
- Managing URLs and endpoints
- https://www.swiftbysundell.com/clips/4/
- https://chris.eidhof.nl/post/typesafe-url-routes-in-swift/
- [iOS - swift] 네트워크(network) - testable한 URLSession 설계 (Endpoint, Provider) - https://ios-development.tistory.com/719
- API Key 보안관리
- 보안 관리하기 - https://nshipster.co.kr/secrets/
'iOS > iOS (응용)' 카테고리의 다른 글
[iOS] SwiftPM - PackageCollection (0) | 2024.08.21 |
---|---|
[iOS] SwiftPM - SwiftPackage (0) | 2024.08.21 |
[iOS] 앱 배포 자동화: Slack 명령어로 TestFlight 업로드 (Flask, Bitbucket Pipelines, Fastlane 활용) (2) | 2024.06.03 |