๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป Programming ๊ฐœ๋ฐœ/๐ŸŽ iOS ๊ฐœ๋ฐœ, Swift

Moya ๊ฐ„๋‹จ ์‚ฌ์šฉ๋ฒ• - Request sample๋ถ€ํ„ฐ ์‹ค์ œ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ๊นŒ์ง€

by kimdee 2023. 2. 23.
๋ฐ˜์‘ํ˜•

 

 

 

์ €๋Š” ์ตœ๊ทผ 2์›”์— ์ˆ˜๋ฃŒํ•œ ์—ฐํ•ฉ๋™์•„๋ฆฌ UMC 3๊ธฐ์—์„œ ํ˜‘์—… ํ”„๋กœ์ ํŠธ ์™€์šฐ๋ฉ”์ดํŠธ์—์„œ iOS ํŒŒํŠธ ๋ฆฌ๋“œ๋ฅผ ๋งก์•˜์Šต๋‹ˆ๋‹ค.

2์›” 16์ผ UMC 3๊ธฐ ๋ฐ๋ชจ๋ฐ์ด ํ˜„์žฅ

 

 

์ œ๊ฐ€ ๋‹ด๋‹นํ•œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ 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 ์—๋„ ๋ฐœํ–‰๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. 

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€