Router ๊ฐ ๋ญํ๋ ์น๊ตฌ?
์ด๋ฒ์๋ Router ๊ฐ ๋ญํ๋ ์น๊ตฌ์ธ์ง ์์๋ณด๊ฒ์์ ๐
์ด Router ๋ ๋คํธ์ํฌ ์์ฒญ์ ์ค๋ํฌ์ธํธ, ๋ฉ์๋, ํ๋ผ๋ฏธํฐ ๋ฑ์ ์ ์ํด์ URLRequest ๋ก ๋ณํํ๋ ์ญํ ์ ํฉ๋๋ค.
๊ทธ๋ผ ์ URLRequest ๊ฐ ํ์ํ ๊น์ ?
์ฐ๋ฆฌ๊ฐ ์๋ฒ์ ํต์ ์ ํ๊ธฐ ์ํด์ ,
์ฐ๋ฆฌ๊ฐ ์ง๋๋ฒ์ ์ฌ์ฉํ .dataTaskPublisher(for: request) ์ ํ์ํ url ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ฃผ๊ธฐ ์ํจ์ ๋๋ค.
์ฐ๋ฆฌ๊ฐ ํ๋ก์ ํธ์์ ํ์ํ ๋ค์ํ ๋คํธ์ํฌ ์์ฒญ๋ค์ด ์์ํ ๋ฐ,
user ์ ๊ด๋ จ๋ ๋คํธ์ํฌ ์์ฒญ๋ง ๋ด๋
์ ์ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ, ํ์ฌ ๋ก๊ทธ์ธ๋ ์ ์ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ, ์ต์ด ๋ก๊ทธ์ธ์ ํ์๊ฐ์ ํ ์ ์ ์ ๋ณด ์ ์ฅํ๊ธฐ, ์ ์ ์ ๋ณด ๋ณ๊ฒฝํ๊ธฐ ๋ฑ…
์ด๋ ๊ฒ ๋ค์ํ ์์ฒญ๋ค์ด ์์๊ฒ๋๋ค.! ์ด ๋ชจ๋ ์์ฒญ๋ค์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผํ ๊น์?
how?
์ด๋ฐ ๋ค์ํ ์ผ์ด์ค๋ค์ UserRouter ์์
ํ์ํ ๋คํธ์ํฌ ์์ฒญ๋ค์ ๊ฐ๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ์ผ์ด์ค ๋ณ๋ก ์ ๋ฆฌํ๊ธฐ ์ํด, (GET, POST, PATCH, DELETE)
enum ์ผ๋ก ์ ์ ํ์ฌ ๋คํธ์ํฌ ์์ฒญ ๊ด๋ จ ์ฝ๋๋ฅผ ์ ๋ฆฌํด ๋์์ต๋๋ค.
Router ๋ ๋คํธ์ํฌ ์์ฒญ์ ์ค๋ํฌ์ธํธ, ํค๋, ๋ฐ๋, ํ๋ผ๋ฏธํฐ๋ฑ์ ์ ์ํด์ URLRequest ๋ก ๋ณํํ๋ ์ญํ ์ ํฉ๋๋ค.
๊ทธ๋ผ ์ด๋ป๊ฒ URLRequest๋ฅผ ๋ง๋๋์ง ํ๋ฒ ๋ณผ๊น์?
๊ทธ๋ผ UserRouter ํ์ผ์ ํ๋ฒ ์ดํด๋ณผ๊ฒ์!
- ๋จผ์ ์ด๋ค ๋คํธ์ํฌ ์์ฒญ์ธ์ง ์ผ์ด์ค๋ฅผ ์ ์ํด์ฃผ์ด์ผํฉ๋๋ค.
case get(nextPageToken: String)
case post(myFilm: String, bookmarkedMagazineID: String,email: String, myCamera: String, postedCommunityID: String, postedMagazineID: String, likedMagazineId: String, lastSearched: String, bookmarkedCommunityID: String, recentSearch: String, id: String, following: String, myLens : String, profileImage: String, name: String, follower: String, nickName: String, introduce: String, fcmToken : String, blocking: String, blocked: String)
case patchProfile(profileImage: String, nickName: String, introduce: String, docID: String)
case delete(docID: String)
- ๊ณตํต์ผ๋ก ์ฌ์ฉ๋๋ baseURL๊ณผ, ํ์ฌ firestore ์ user ์ปฌ๋ ์ ์ ์ ๊ทผํ๊ธฐ ์ํด info ๋ฆฌ์คํธ์ UuidUser ๊ฐ์ ๊ฐ์ ธ์ Path๋ฅผ ์ ์ฅํฉ๋๋ค.
private var baseURL: URL {
var baseUrlString : String = "https://"
if let infolist = Bundle.main.infoDictionary {
if let url = infolist["FireStore"] as? String {
baseUrlString += url
}
}
return URL(string: baseUrlString) ?? URL(string: "")!
}
// firestore ์ collection ์ ์ ๊ทผํ๊ธฐ ์ํ path
private var queryItemString: String {
var userString : String = ""
// ๋ณด์์ฒ๋ฆฌ๋ฅผ ์ํด path๋ฅผ infolist ์ ์ ์ฅ.
if let infolist = Bundle.main.infoDictionary {
if let str = infolist["UuidUser"] as? String {
userString = str
}
}
return userString
}
- ๊ทธ๋ค์ GET, POST, PATCH, DELETE ์ ๊ฐ์ ๋ค์ํ HTTP ๋ฉ์๋๋ฅผ ๋ํ๋ด๋ ์ด๊ฑฐํ์ ๋ง๋ค์ด์ค๋๋ค.
- ์ด ์ด๊ฑฐํ์ ๊ฐ๊ฐ์ ์ผ์ด์ค์ ๋ฐ๋ผ value ์ ์ ๊ทผํด ๋ฌธ์์ด์ ๋ฆฌํดํ ์ ์์ต๋๋ค.
private enum HTTPMethod {
case get
case post
case patchProfile
case delete
var value: String {
switch self {
case .get: return "GET"
case .post: return "POST"
case .patchProfile: return "PATCH"
case .delete: return "DELETE"
}
}
}
- ์ผ์ด์ค์ ๋ฐ๋ผ ๋ฌธ์์ด ์กฐํฉ์ผ๋ก URL ์ EndPoint ๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
- ex ) https://firestore/user/1234 → ํ์ด์ด์คํ ์ด์ ์ ์ ์ปฌ๋ ์ ์ documentID ๊ฐ 1234 ์ธ ์ ์ ์ ๋ณด
private var endPoint: String {
switch self {
case let .patchProfile(_,_,_, docID):
return "/" + "\(queryItemString)" + "/" + "\(docID)"
case let .delete(docID: docID):
return "/" + "\(queryItemString)" + "/" + "\(docID)"
default:
return "/" + "\(queryItemString)"
}
}
- URLQueryItem์ ์ฌ์ฉํด์ ๊ฐ๊ฐ์ URLRequest ์ ํ์ํ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ฅํฉ๋๋ค.
- URLQueryItem ์ URL ์ฟผ๋ฆฌ ๋ฌธ์์ด์์ ํน์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ํ๋ด๋ ํด๋์ค์ ๋๋ค. URL์ฟผ๋ฆฌ ๋ฌธ์์ด์ URL ์ ์ผ๋ถ์ด๊ณ , “?” ๋ค์ ํค - ๊ฐ ์์ผ๋ก ๊ตฌ์ฑ๋๋ ๋ฐ์ดํฐ์ ๋๋ค.
- ํ๋ผ๋ฏธํฐ๊ฐ ์ฌ๋ฌ๊ฐ ํ์ํ ์ ์๊ธฐ๋๋ฌธ์ URLQueryItem ๋ฐฐ์ด๋ก ์ ์ธํด ์ค๋๋ค.
var parameters: [URLQueryItem]? {
switch self {
case let .get(nextPageToken):
let params: [URLQueryItem] = [URLQueryItem(name: "pageToken", value: nextPageToken)]
return params
case let .post(myFilm, bookmarkedMagazineID, email, myCamera, postedCommunityID, postedMagazineID, likedMagazineId, lastSearched, bookmarkedCommunityID, recentSearch, id, following, myLens , profileImage, name, follower, nickName, introduce , fcmToken, blocking, blocked):
var params: [URLQueryItem] = [URLQueryItem(name: "documentId", value: id)]
return params
case let .patchProfile(profileImage, _, _, _):
let param1 = URLQueryItem(name: "updateMask.fieldPaths", value: "profileImage")
let param2 = URLQueryItem(name: "updateMask.fieldPaths", value: "nickName")
let param3 = URLQueryItem(name: "updateMask.fieldPaths", value: "introduce")
var params: [URLQueryItem] = []
if profileImage == "" {
params = [param2, param3]
} else {
params = [param1, param2, param3]
}
return params
default :
let params: [URLQueryItem]? = nil
return params
}
}
http:// kkkk?uid=1234 ์ด๋ฐ์์ด์ฃ !
ex) ์ด๋ค ์ ์ ๊ฐ ์ ์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ค๊ณ ํ๋ฉด, ์ด๋ค ์ ์ ์ธ์ง id path๋ฅผ parameter ๋ก ์ ๋ฌํด์ฃผ์ด์ผ ํฉ๋๋ค.
- method ํ๋กํผํฐ๋ ๊ฐ case์ ๋ฐ๋ผ HTTP ๋ฉ์๋๋ฅผ ๋ฐํํฉ๋๋ค.
private var method: HTTPMethod {
switch self {
case .get :
return .get
case .post :
return .post
case .delete:
return .delete
default:
return .patchString
}
}
- data ํ๋กํผํฐ๋ ๊ฐ case ์ ๋ฐ๋ผ API ์์ฒญ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํฉ๋๋ค.
- ์ด๋ UserQuery์์ insertUserQuery์ ๊ฐ์ ๋ฉ์๋๋ ์ ์ ํ์ด์ด๋ฒ ์ด์ค ๋์ ๊ธฐ๋ ๋ง๋ค์ด์ค StructuredQuery ๋ฅผ Data ํ์ ์ผ๋ก ๋ณํํ์ฌ ๋ฆฌํดํ๋ ๋ฉ์๋์ ๋๋ค.
private var data: Data? {
switch self {
case let .post(myFilm, bookmarkedMagazineID, email, myCamera, postedCommunityID,postedMagazineID: postedMagazineID,likedMagazineId,lastSearched,bookmarkedCommunityID, recentSearch, id, following,myLens,profileImage,name,follower,nickName, introduce, fcmToken, blocking, blocked):
return UserQuery.insertUserQuery(myFilm: myFilm,bookmarkedMagazineID: bookmarkedMagazineID,email: email,myCamera: myCamera,postedCommunityID: postedCommunityID, postedMagazineID: postedMagazineID, likedMagazineId: likedMagazineId, lastSearched: lastSearched, bookmarkedCommunityID: bookmarkedCommunityID, recentSearch: recentSearch, id: id, following: following, myLens: myLens, profileImage: profileImage, name: name, follower: follower, nickName: nickName, introduce: introduce, fcmToken: fcmToken, blocking: blocking, blocked: blocked)
case let .patchProfile(profileImage, nickName, introduce, _):
return UserQuery.updateUserProfile(profileImage: profileImage, nickName: nickName, introduce: introduce)
default:
return nil
}
}
asURLRequest () **
- ์ ๋ง์ง๋ง์ผ๋ก , , ! ! asURLRequest ํจ์๋ฅผ ์ฌ์ฉํด์ URLRequest ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด ๋ฆฌํดํด์ค๊ฒ๋๋ค.!
func asURLRequest() throws -> URLRequest {
let url = baseURL.appendingPathComponent(endPoint)
var component = URLComponents(url: url, resolvingAgainstBaseURL: false)!
if let param = parameters {
component.queryItems = param
}
var request = URLRequest(url: component.url!)
request.httpMethod = method.value
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
if let data = data {
request.httpBody = data
}
return request
}
ํ๋ ํ๋ ์ดํด๋ณผ๊ฒ์! ๐
1. baseURL ์ endPoint ๋ฅผ ์ถ๊ฐํด์ URL ์ธ์คํด์ค๋ฅผ ์์ฑํฉ๋๋ค.
let url = baseURL.appendingPathComponent(endPoint)
2. URL ์ ๊ธฐ๋ฐ์ผ๋ก URLComponents ์ธ์คํด์ค๋ฅผ ์์ฑํฉ๋๋ค.
var component = URLComponents(url: url, resolvingAgainstBaseURL: false)!
3. parameter ๊ฐ nil ์ด ์๋๊ฒฝ์ฐ(get ๋ฉ์๋์ผ๊ฒฝ์ฐ์ ํ๋ผ๋ฏธํฐ๊ฐ ์์) , URLComponent ์ queryItems ์์ฑ์ ํ๋ผ๋ฏธํฐ๋ฅผ ํ ๋นํฉ๋๋ค.
if let param = parameters {
component.queryItems = param
}
4. URLRequest ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ . HTTP ๋ฉ์๋์, ํค๋ ํ๋๋ฅผ ์ค์ ํด์ค๋๋ค.
var request = URLRequest(url: component.url!)
request.httpMethod = method.value
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
5. data (query) ๊ฐ nil ์ด ์๋ ๊ฒฝ์ฐ์๋, HTTP Body ์ ๋ฐ์ดํฐ๋ฅผ ํ ๋นํฉ๋๋ค.
if let data = data {
request.httpBody = data
}
6. ์์ฑ๋ URLRequest ์ธ์คํด์ค๋ฅผ ๋ฐํํฉ๋๋ค.
return request
์ด๋ ๊ฒ ํด์ ๋ชจ๋ ๋คํธ์ํฌ ์์ ๋ค์ ํ์ํ URLRequest ๋ฅผ ์์ฑํด์ค ์ ์๊ฒ ๋๋๊ฒ๋๋ค!
Why Router?
๊ทธ๋ฐ๋ฐ ์ ์ด๋ ๊ฒ Router ๋ฅผ ๋ฐ๋ก ๊ด๋ฆฌํ๋ ๊ฑธ๊น์ ?
๋ง์ฝ Router์ URL ๊ด๋ จ ์ฝ๋๋ค์ ์ ๋ฆฌํด๋์ง ์์๋ค๋ฉด ์ด๋ ์๊น์ ?
// UserService
static func getUser(nextPageToken: String) -> AnyPublisher<UserResponse, Error> {
do {
// ๋ฐ๋ก request ์ด๋ถ๋ถ
let request = try UserRouter.get(nextPageToken: nextPageToken).asURLRequest()
return URLSession
.shared
.dataTaskPublisher(for: request)
.map{ $0.data}
.decode(type: UserResponse.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
} catch {
return Fail(error: HTTPError.requestError).eraseToAnyPublisher()
}
}
๋ฐ๋ก ์๋น์ค ํ์ผ๋ด์ dataTaskPublisher์ ํ์ํ request(URLRequest) ๋ฅผ ๊ฐ ๋ฉ์๋๋ง๋ค ์ ์ธํด์ฃผ์ด์ผ ํ๊ฒ ์ฃ !
์ด ์งง์ ์ฝ๋ ๋์
let request = try UserRouter.get(nextPageToken: nextPageToken).asURLRequest()
์ด๋ ๊ฒ ๊ธด ์ฝ๋๋ฅผ ๋ชจ๋ ๋ฉ์๋์ ์ ์ธํด์ฃผ์ด์ผํฉ๋๋ค! ๐ณ
let baseURL: = "https://"
let endPoint = "Users"
let url = baseURL.appendingPathComponent(endPoint)
let component = URLComponents(url: url, resolvingAgainstBaseURL: false)!
let request = URLRequest(url: component.url!)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
Router ๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด ์ฃผ์ง ์์์๋์ ๋์ฐธ์ฌ…
์ด๋ค ์ด๋?
๊ทธ๋์ Router ๋ฅผ ๋ฐ๋ก ๋นผ์ฃผ์์๋ ์ ๊ฐ ๋๋ ์ฅ์ ๋ค์ ๋๋ค ๐คฉ
- ์ฝ๋์ ๊ฐ๋ ์ฑ: Router๋ฅผ ์ฌ์ฉํ๋ฉด ๋คํธ์ํฌ ์์ฒญ ๊ด๋ จ ์ฝ๋๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ์ ๋ฆฌ๋์ด ๊ฐ๋ ์ฑ์ด ๋์์ง๋๋ค. ๊ฐ ์์ฒญ์ด ์ผ์ด์ค๋ก ๋ช ํํ๊ฒ ์ ์๋์ด ์์ด ์ฝ๊ฒ ํ์ ํ ์ ์์ฃ !
- ์ฌ์ฌ์ฉ์ฑ: Router๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ผํ ์๋ํฌ์ธํธ์ ๋ฉ์๋๋ฅผ ๊ฐ์ง ๋ค๋ฅธ ๋ถ๋ถ์์๋ ๋์ผํ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ก์ด request๋ฅผ ์ถ๊ฐํ ๋๋ ์๋ก์ด ์ผ์ด์ค๋ฅผ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋๋ฏ๋ก ํจ์จ์ ์ ๋๋ค!
- ์ค๋ฅ ๋ฐฉ์ง: ๋น์ทํ ์์ ์ ๋ฐ๋ณตํ๋๋ผ๋, URL์ ํ ์จํ๋ ํ๋ฆฌ๋ฉด ๋ค ์๋๊ธฐ ๋๋ฌธ์ ,,,, Router๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ํฌ์ธํธ, ๋ฉ์๋, ๋งค๊ฐ๋ณ์ ๋ฑ์ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ์ ์ํ๊ธฐ ๋๋ฌธ์ ์ค์๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๋๋์
์ฌ์ค ์์ ๋์ฐธ์ฌ ์ฝ๋๋ ์ฒ์ ์ ์ฝ๋์์ต๋๋ค.. ๊ทธ๋ฌ๋ค ์์์ฐจ๋ ธ์ฃ ์ด๊ฑด์ข ,,
URL ๊ด๋ จ ์ฝ๋๋ค์ ํ๊ตฐ๋ฐ์ ์์๊ฒ! ์! ๊ด๋ฆฌํ๊ณ ์ถ์์ต๋๋ค..
๋ ์ด๋ ๊ฒ ํ๋ค๋ณด๋ฉด ์์ค์๋ค๋ ๋ง์์ง ๊ฒ ๊ฐ๊ณ , ์ฑ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด์ ํ๋์ ์์๋ณด๊ธฐ๋ ์ฐธ ๋ณ๋ก์ผ๊ฒ ๊ฐ์์ฃ !
๊ทธ๋์ ๋ง์ ์๋ฃ๋ค์ ๊ฒ์ํด๋ณด๊ณ ๊ณต๋ถํด์ Router ๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด์
URLRequest ๋ฅผ ๋ง๋ค์ด ์ค ์ ์๋ ๋ฐฉ๋ฒ์ ๊ณต๋ถํด๋ณด๊ณ ์ ์ฉ์์ผ๋ณด์์ต๋๋ค: ) ๋ฟ์ฐ๋ฏ
๊ทธ๋ ์ชผ๊ธ ๊ณ ํต์ด์์ง๋ง ์ง๊ธ ๋์๋ณด๋ฉด
๋คํธ์ํฌ ์์ฒญ ์ผ์ด์ค๋ง ํ ๋ฐฑ๊ฐ์ฏค๋๋ ์ฐ๋ฆฌํ์ ๊ฐ๋ฐ ์๊ฐ์ ๊ทธ๋๋ 20 ์๊ฐ์ ๋จ์ถ ์ํค์ง ์์์๊น์ ? ์ด๊ฑด ์ข ์ค๋ฐ๊ฐ… ์จ๋ !
์ฌ๊ธฐ๊น์ง URLRequest๋ฅผ ๋ง๋๋ ๊ณผ์ ๊ณผ, Router ํ์ผ์ ๋ํ ๊ณ ์ฐฐ,,? ๊ณ ๋ฏผ? ๋ญ ์ํผ ์์๋ณด์์ต๋๋ค.
ํด .. ๋ณ๊ฑฐ์๋๋ค์ ^^ ๐
์ค๋๋ ํด๊ฒฐ์~ ๐ฅณ
'ํ๋ก์ ํธ ๐ ๏ธ > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ์ฑ์ด ์งฑ์ปค์ ์ ๋ฐ์ดํธ ํ ๊ฑฐ ๋ฐฑ๋ง๊ฐ! (0) | 2023.06.10 |
|---|---|
| URLSession + Combine ์กฐํฉ์ผ๋ก ๋คํธ์ํฌ ๋ ์ด์ด ๋ง๋ค๊ธฐ (0) | 2023.06.10 |
| Combine(3): Operator (0) | 2023.06.10 |
| Combine(2): Subject (0) | 2023.06.10 |
| Combine(1): Publisher, Subscriber! (0) | 2023.06.10 |