Moya ๊ฐ๋จ ์ฌ์ฉ๋ฒ - Request sample๋ถํฐ ์ค์ ๋ทฐ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉ๊น์ง
์ ๋ ์ต๊ทผ 2์์ ์๋ฃํ ์ฐํฉ๋์๋ฆฌ UMC 3๊ธฐ์์ ํ์ ํ๋ก์ ํธ ์์ฐ๋ฉ์ดํธ์์ iOS ํํธ ๋ฆฌ๋๋ฅผ ๋งก์์ต๋๋ค.
์ ๊ฐ ๋ด๋นํ ํ๋ก์ ํธ์์๋ ๋คํธ์ํฌ ํต์ ์ Moya ๋ฅผ ์ด์ฉํด์ ๋คํธ์ํฌ ํต์ ์ ๋ณด๋ฅผ ์ถ์ํํ๊ณ ์๋๋ฐ์.
UMC 3๊ธฐ์ ํ๊ธฐ์ค ์์ ์์๋ Alamofire ๋ง ์ฃผ๋ก ๋ค๋ค๊ธฐ ๋๋ฌธ์, ๊ฐ๋จํ ์ฌ์ฉ๋ฒ์ ์ ๋ฆฌํด๋ ๊ฒ์ ๊ณต์ ํด๋ด ๋๋ค.
Moya๋ก ๋ ํธํ๊ฒ ๊ด๋ฆฌํ ์ ์์๊ฑฐ๋ผ๊ณ ์๊ฐํด์ ์์ ์ ํด๋์๋๋ฐ ์ฒ์ ํด๋ณด์ ๋ค๋ฉด ๋ง์ด ํท๊ฐ๋ฆด ๊ฒ ๊ฐ์์. ํ ๋ฒ ๋ณด๊ณ ํด๋ณด์๋ฉด ์์ํ์ง ์์๊น ์ถ์ด ์ ๋ฆฌํด๋ด ๋๋ค.
1. Codable ๊ตฌ์กฐ์ฒด๋ฅผ ์ ์ธํ๊ธฐ
์๋ฒ ์ธก์์ ์ ๋ฌ๋ฐ์ API ๋ช ์ธ์ ๋ณด๋ฉด JSON ์์๊ฐ ์์ต๋๋ค. ๊ฒ์๊ธ์ ๋ฑ๋กํ๋ API๋ฅผ ์๋ก ๋ค์ด๋ด ์๋ค.
{
"postTitle":"1๋ฒ์งธ ๊ฒ์๊ธ",
"categoryName": "์ด๋",
"postMember": 0,
"tag1": "umc",
"tag2": "wowmate",
"tag3": null,
"tag4": null,
"tag5": null,
"postContext": "์ฒซ๋ฒ์งธ ๊ฒ์๊ธ ๋ฑ๋ก ์๋ฃ"
}
์ด๊ฑธ ๊ทธ๋๋ก ๋ณต์ฌํด์ quicktype.io ์ ๊ฐ์ ธ๊ฐ๋๋ค.
ํตํ์ ์ JSON ์์์ ์ธ์ด์ ๋ง๊ฒ ๋ชจ๋ธ์ ์๋์ผ๋ก ์์ฑํด์ค๋๋ค.null ๊ฐ์ด ์์ ๊ฒฝ์ฐ Nullable ์ฒ๋ฆฌ๊ฐ ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. Swift์์ Optional ์ฒ๋ฆฌ๋ type-safety๋ฅผ ์ํด์ ๊ฐ์ ํด์ ๋ณด๋ค๋ guard-let๋ฑ์ ์ด์ฉํ ์ธ๋ํ์ด๋ nil-coalescing ์ ํด์ผ๋๋๋ฐ ์ด ๊ฒฝ์ฐ ์ฒ๋ฆฌ๊ฐ ๋ณต์กํด์ง๋ฏ๋ก ์ฌ๋งํ๋ฉด null์ด ์๋ ๊ฐ์ผ๋ก ๋ฐ์ ์ ์๋๊ฒ ์ข์ ๊ฒ ๊ฐ์์.
์ด๋ ๊ฒ ๋ง๋ค์ด์ง ๊ตฌ์กฐ์ฒด๋ ์๋ก์ด ํ์ผ์ ๋ง๋ค์ด ๋ณต์ฌํด์ค๋๋ค. ์ ๋ ๊ฒ์๊ธ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ธ์ ํ ๊ณณ์ ๋ชจ์๋ณด๋๊ฒ ์ข์ ๊ฒ ๊ฐ์์ Post.swift ํ์ผ์ ๋ชจ์๋๋ ค๊ณ ์.
// Post.swift
import Foundation
// MARK: - PostRegister
struct PostRegister: Codable {
let postTitle, categoryName: String
let postMember: Int
let tag1, tag2, tag3, tag4: String
let tag5, postContext: String
}
2. Moya์ TargetType์ผ๋ก ๋คํธ์ํฌ ํต์ ํ ๋ด์ฉ์ ์ ์ธํด๋ก๋๋ค.
์ด๊ฑธ ์ ํ๋๋ฉด์. ์ ํฌ๊ฐ ์ด์ ์ ๋ฐฐ์ ๋ Alamofire๋ฅผ ์ด์ฉํด AF.request ํ๊ณ ์ฌ๊ธฐ์ ๋ฉ์๋์ ํค๋์ ํ๋ผ๋ฏธํฐ ๋ฃ๊ณ ๋ฑ๋ฑ์ ํ๊ฒ ๋ ๊ฒฝ์ฐ ๋ทฐ์ปจํธ๋กค๋ฌ ์ฝ๋์์ ๋๋ฌด ๋ง์ ์์กด์ฑ์ด ์๊ธฐ๊ณ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ๊ฐ ์ด๋ ต๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ค์ ์์ ํด์ผ๋๋ค๋ฉด ์ผ์ผํ ์ฐพ์ ๊ณ ์น๊ธฐ๊ฐ ๋ฒ๊ฑฐ๋กญ๊ฒ ๋๊ฒ ์ฃ . ์ฝ๋์์ ๊ด์ฌ์ฌ๋ฅผ ์๋ฏธ๋จ์๋ก ์ต๋ํ ๋๋๋ ๊ฒ์ ์ดํ ์ฌ์ฌ์ฉ์ฑ ๋ฐ ์ ์ง๋ณด์๋ฅผ ์ํด์ ๊ผญ ํ์ํ ์์
์
๋๋ค.
์ผ์ ์ ์ ๊ฐ Data/Model ํด๋ ์์ ์ ๋ฆฌํด๋ ๋ด์ฉ์ ํ ๋ฒ ๋ค์ ์ดํด๋ด ์๋ค.
Endpoint.swift ์๋ ๋ฒ ์ด์ค url์ด ๋ค์ด๊ฐ๋๋ค. ์ ํฌ๊ฐ notion.so/wowmatte ๊ฐ ์๋ค๋ฉด ์ฌ๊ธฐ์ notion.so ๊ฐ ๋ฒ ์ด์ค์ ํด๋นํฉ๋๋ค. ์ด๋ฏธ ๋ฃ์ด๋์์ผ๋ ์ ๊ฒฝ์ฐ์ง ์์ผ์ ๋ ๋ฉ๋๋ค.
```swift
// Endpoint.swift
import Foundation
enum ServiceAPI {
static let baseURL = "<http://ec2-13-209-29-182.ap-northeast-2.compute.amazonaws.com:8080>"
}
```
Post.swift ์ ์๊น ๋ง๋ ๋ชจ๋ธ์ ๋ฃ์ด์ค๋๋ค.
```swift
/* ๊ฒ์๊ธ ๋ฑ๋ก (post - posts) */
// MARK: - PostRegister
struct PostRegister: Codable {
let postTitle, categoryName: String
let postMember: Int
let tag1, tag2, tag3, tag4, tag5, postContext: String
}
```
PostAPI์์ ํต์ ํํ์ ๋ง์ถฐ ์ ๋ฆฌํด์ค๋๋ค. ์ ํฌ๋ ๊ฒ์๊ธ ๋ฑ๋ก์ด๊ณ , ๋ชจ๋ธ๋ช ๋ ๊ทธ์ ๋ง์ถฐ ๋ฐ๊ฟจ์ผ๋ ์ด๊ฑฐํ์ case๋ฅผ ์ด๋ฆ๋ง์ถฐ ์ถ๊ฐํด์ค๋๋ค.
๋ด์ฉ์ ๋ฐ์์ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ฏ๋ก ์ด๊ฑฐํ์ ์ฐ๊ด๊ฐ(Associated Value) ์ ์ด์ฉํด ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฃ์ด์ค๋๋ค. ์ถ๊ฐํ ๋ด์ฉ๊ณผ ์ฐธ๊ณ ํ ๋ด์ฉ์ ์๋ ์ฝ๋์ ์ฃผ์์ผ๋ก ๋ฌ์๋์์ต๋๋ค.
//PostAPI.swift
enum PostAPI {
case posts
case mockPosts
case postRegister(param: PostRegister) // <- New! ์ฐ๊ด๊ฐ์ผ๋ก ๊ฐ์ ๋ฐ์์ค๋๋ค.
}
์ ํฌ๋ TargetType์ผ๋ก extensionํ ์ฝ๋์์ ๋๋จธ์ง ๋ถ๋ถ์ ์ฑ์ธ ๊ฒ์ ๋๋ค.
extension PostAPI: TargetType {
var baseURL: URL {
switch self {
case .mockPosts:
return URL(string: "<https://63ba608856043ab3c79a44ce.mockapi.io/api/v1>")!
default:
return URL(string: ServiceAPI.baseURL)! // ์ด๋ฏธ ์ด๊ฑฐํ์ ๋ฒ ์ด์ค๋ฅผ ์ ์ธํ์ผ๋ฏ๋ก
}
}
var path: String {
switch self {
case .posts, .postRegister: // path ๋๋ค ๋์ผํฉ๋๋ค.
return "/posts"
case .mockPosts:
return "/posts"
}
}
var method: Moya.Method {
switch self {
case .posts:
return .get
case .postRegister: // ๊ฒ์๊ธ๋ฑ๋กํ๋ ๊ฒฝ์ฐ .post ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
return .post
case .mockPosts:
return .get
}
}
var task: Moya.Task {
switch self {
case .posts:
return .requestPlain
case .postRegister(let param):
return .requestJSONEncodable(param) // ๋ฐ์๋ ๊ฐ์ JSON ํํ๋ก ๋ณด๋ด๊ธฐ
case .mockPosts:
return .requestPlain
}
}
var headers: [String : String]? {
switch self {
default:
return ["Content-Type": "application/json"] // ํ ํฐ์ด ๋ค์ด๊ฐ ๊ฒฝ์ฐ ์ฌ๊ธฐ์ ๋ค์ด๊ฐ.
}
}
์ ํฌ๊ฐ ๋ก๊ทธ์ธ์ ํ๊ฑฐ๋ ์์ ์ ํ๊ฑฐ๋ ๊ฒ์๊ธ์ ์์ฑํ๋ ๋ฑ ๋ง์ ๋ถ๋ถ๋ค์ด ํค๋์ชฝ์์ ์ธ์ฆ์ด ์ด๋ฃจ์ด์ ธ์ผ ํ๋๋ฐ์. ๊ทธ ๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ช ์ธ๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ์ถ๊ฐํ๊ฒ ์ต๋๋ค. ๋ณดํต์ content-type ์ ๋๋ JSON ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์๋ฒ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ ๊ฐ์์. ์ ์๋ nil ์ด์๋๋ฐ ์ผ๋จ ์ถ๊ฐํด๋ด ๋๋ค. ์ ํค๋ ๋ถ๋ถ์ HTTPHeader? ํํ๋ก๋ ์ ์๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
3. ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํ ๋คํธ์ํฌ ๋งค๋์ ํ์ผ์ ๋ฉ์๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์ด๋ฏธ ๋ง๋ค์ด๋ PostManager.swift๋ก ๋์ด๊ฐ๋๋ค. ๋ช ์ธ์์ response sample์ ๋ณด๋ฉด ์ด๋ ๊ฒ ๋์์๋๋ฐ์.
"isSuccess": true, "code": 200, "message": "SUCCESS!", "data1": { "postId": 2 } }
๋ฉ์์ง์ ์ฑ๊ณต ๋ฐ ์คํจ์ฌ์ ๊ฐ ์์ด์ ๋ฉ์์ง๋ง ๋ณด๋ด๋ ๋ ๊ฑฐ ๊ฐ์ต๋๋ค.
์๋์ฒ๋ผ ํ์ถํด๋ก์ ๋ฅผ ์ด์ฉํด์ ๋ฃ์ด๋์์ต๋๋ค.
import Foundation
import Moya
class PostManager {
private init() {}
static let shared = PostManager()
let provider = MoyaProvider<PostAPI>() // ๊ฒ์๊ธ ๋ฑ๋ก
func registerPost(post: PostRegister, completion: @escaping (Result<String, Error>) -> Void ) {
provider.request(.postRegister(param: post)) { result in
switch result {
case .success(let data):
if let json = try? JSONSerialization.jsonObject(with: data.data,options: []) as? [String : Any] {
if let message = json["message"] as? String {
completion(.success(message))
}
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
4. ์ค์ ๋ทฐ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉํด๋ณด๊ธฐ
์ฌ์ฉ์์๊ฒ ๋ฐ์ ๋ด์ฉ์ PostRegister ๊ตฌ์กฐ์ฒด๋ก ์ ์ฅํ ํ ํด๋น ๋ด์ฉ์ ์ด๋ ๊ฒ ๋ฃ์ด๋ก๋๋ค. ๋คํธ์ํฌ ํต์ ์ฑ๊ณต, ์คํจ ์ฌ๋ถ์ ๋ฐ๋ผ ํ์ํ ์ก์ ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
let postByUser = PostRegister(postTitle: "์ ๋ชฉ", categoryName: "์นดํ
๊ณ ๋ฆฌ", postMember: 2, tag1: "ํ๊ทธ1", tag2: "ํ๊ทธ2", tag3: "ํ๊ทธ3", tag4: "", tag5: "", postContext: "๋ด์ฉ")
PostManager.shared.registerPost(post: postByUser) { result in
switch result {
case .success(let success):
print(success)
case .failure(let failure):
print(failure)
}
}
์ค๋๋ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
๊ถ๊ธํ๊ฑฐ๋ ๋๋๊ณ ์ถ์ ์๊ธฐ๊ฐ ์์ผ์๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!
์ฌ๋ฐ๊ฒ ์ฝ์ผ์ จ๋ค๋ฉด ๊ณต๊ฐ๊ณผ ๊ตฌ๋ ์ ํฐ ํ์ด ๋ฉ๋๋ค.
ํญ์ ๊ฐ์ฌํฉ๋๋ค.
์ด ๊ธ์ doy.oopy.io ์๋ ๋ฐํ๋์ด ์์ต๋๋ค.