ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iOS [Swift] - Moya๋ฅผ ํ†ตํ•œ ๋„คํŠธ์›Œํฌ ํ†ต์‹ 
    iOS Develop 2023. 2. 14. 15:08
    ๋ฐ˜์‘ํ˜•
    ๐Ÿ“„

    iOS์—์„œ ๋„คํŠธ์›Œํฌ ์ž‘์—…์ด๋ผ๊ณ  ํ•˜๋ฉด ๊ฐ€์žฅ ๋– ์˜ค๋ฅด๋Š” URLSession, Alamofire๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค. URLSession์€ ํ”„๋กœ์ ํŠธ์˜ ๋ณต์žก๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ€๊ณ  ๊ฐ€๋…์„ฑ์ด ์•ˆ์ข‹์€ ๋ฐ˜๋ฉด, URLSession ๊ธฐ๋ฐ˜์˜ Alamofire๋Š” ๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค. ํ•˜์ง€๋งŒ, Alamofire๋Š” ์œ ์ง€๋ณด์ˆ˜ & ํ…Œ์ŠคํŠธ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ํ™•์–ธํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋Š˜์˜ ์ฃผ์ œ Moya๋ฅผ ์‚ดํŽด๋ณผ ๊ฒƒ์ด๋‹ค.


    ๊ฐœ๋…

    • URLSession์„ ์ถ”์ƒํ™” ์‹œํ‚จ Alamofire๋ฅผ ํ•œ ๋ฒˆ ๋” ์ถ”์ƒํ™”์‹œํ‚จ Networking Library์ด๋‹ค.
    • Enum(์—ด๊ฑฐํ˜•)์„ ์‚ฌ์šฉํ•ด์„œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ type-safeํ•œ ๋ฐฉ์‹์œผ๋กœ ์บก์Аํ™”ํ•œ๋‹ค. (์•ˆ์ „ & ๊น”๋”)
      • type-safe
        • ํ•ด๋‹น Type์—๋Š” ์ •ํ•ด์ง„ Type๋งŒ ์ €์žฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋ผ๋Š” ์˜๋ฏธ (๋‹ค๋ฅธ Type ์•ˆ๋ผ!! โŒ)
        • ์ฆ‰, ๋ณ€์ˆ˜์˜ Type์ด ๋ช…ํ™•ํ•ด์ง„๋‹ค.


    ํŠน์ง•

    • Alamofire์—์„œ ๋„คํŠธ์›Œํฌ ๊ณ„์ธต์„ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.
    • Alamofire Library๋ฅผ Enum ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด ๋” ์‚ฌ์šฉ์„ฑ๊ณผ ํ…Œ์ŠคํŠธ์„ฑ์„ ์šฉ์ดํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.
      • Enum์ด๊ธฐ ๋•Œ๋ฌธ์— Type ์ง€์ •์„ ํ•ด์ฃผ๊ธฐ ํŽธํ•˜๋‹ค.
    • ๊ธฐ์กด ๋„คํŠธ์›Œํ‚น ๋ฐฉ์‹ VS Moya
      • ๊ธฐ์กด ๋„คํŠธ์›Œํ‚น ์‚ฌ์šฉ๋ฒ•์€ ์™ผ์ชฝ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ Network Layer๋ฅผ ํ†ตํ•ด ์ž์œ ๋กญ๊ฒŒ ๊ตฌํ˜„๋˜๊ณ  ์žˆ๋‹ค.
      • ๋”ฐ๋ผ์„œ, ๊ธฐ์กด ๋„คํŠธ์›Œํ‚น ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ๋‹ค.
        • ์ƒˆ App์„ ์ž‘์„ฑํ•˜๊ธฐ ์–ด๋ ต๋‹ค.(์–ด๋””์„œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์•ผํ•˜๋Š”๊ฐ€?)
        • ๊ธฐ์กด App์„ ์œ ์ง€ํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค.(์•„๋†”, ๋„ˆ๋ฌด ์—‰๋ง ์ง„์ฐฝ์ด๋„ค..)
        • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค.(์ด ์ž‘์—…์„ ๋‹ค์‹œ ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•ด?)
      • Moya๋Š” ์˜ค๋ฅธ์ชฝ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋ณด๋‹ค ๊น”๋”ํ•œ Layer๋ฅผ ํ†ตํ•ด ์ž์ฒด์ ์œผ๋กœ ๋„คํŠธ์›Œํ‚น์„ํ•˜์ง€ ์•Š๊ณ  Alamofire๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„๋œ๋‹ค.
      • ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, Network Layer๋ฅผ Templateํ™” ์‹œ์ผœ์„œ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

    • Moya์˜ ์žฅ์ 
      • ์˜ฌ๋ฐ”๋ฅธ API ์—”๋“œํฌ์ธํŠธ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•œ ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ๊ฒ€์‚ฌ.
      • ์—ฐ๊ฒฐ๋œ ์—ด๊ฑฐํ˜• ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์—”๋“œํฌ์ธํŠธ์˜ ๋ช…ํ™•ํ•œ ์‚ฌ์šฉ๋ฒ•์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ํ…Œ์ŠคํŠธ ์Šคํ…์„ ์ผ๊ธ‰ ์‹œ๋ฏผ์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋ฏ€๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ๋งค์šฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.


    Flow (์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ๋งŒ ์‚ฌ์šฉ๋ฒ• ์ตํžˆ๊ธฐ)

    1. Pod ์„ค์น˜
      1. pod init
      1. pod install
      pod 'Moya', '~> 15.0'
    1. API ํ†ต์‹ ์„ ์œ„ํ•œ Moya > TargetType์„ ์ •์˜
    import Foundation
    import Moya
    
    enum APIClient {
      case users, todos, comments
    }
    
    extension APIClient: TargetType {
      var baseURL: URL {
        switch self {
        case .users, .todos, .comments:
          return URL(string: "https://jsonplaceholder.typicode.com/")!
        }
      }
      
      var path: String {
        switch self {
        case .users:
          return "users"
        case .todos:
          return "todos"
        case .comments:
          return "comments"
        }
      }
      
      var method: Moya.Method {
        switch self {
        case .users, .todos, .comments:
          return .get
        }
      }
      
      var task: Moya.Task {
        switch self {
        case .users:
          return .requestPlain
        case .todos:
          return .requestPlain
        case .comments:
          return .requestPlain
        }
      }
      
      var headers: [String : String]? {
        return ["Content-type": "application/json"]
      }
    }
    1. Json Model Struct ์ž‘์„ฑ
    struct Todos: Codable {
      var userId: Int
      var id: Int
      var title: String
      var completed: Bool
    }
    1. API ํ†ต์‹ 
    func getDataFromJsonPlaceHolder() {
        let moyaProvider = MoyaProvider<APIClient>()
        
        moyaProvider.request(.todos) { result in
          switch result {
          case .success(let response):
    //        guard let todoTitle = try? response.mapJSON() else { return }
            guard let todo = try? response.map([Todos].self) else { return }
            self.labelText.text = todo[Int.random(in: 1..<10)].title
          case .failure(let moyaError):
            print(moyaError.errorDescription ?? "Moya API Request Error")
          }
        }
      }


    ๋งˆ๋ฌด๋ฆฌ

    • URLSession & Alamofire VS Moya ์ฐจ์ด์ 
      • ๊ธฐ์กด์— ์‚ฌ์šฉํ•ด๋ดค๋˜ URLSession๊ณผ Alamofire๋Š” request/response๋ฅผ ํ•˜๋ฉด์„œ ๋™์‹œ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€(url, method ๋“ฑ)๋ฅผ ์‹ ๊ฒฝ ์จ์•ผํ–ˆ๋‹ค. (์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ˜๋ณต ์ž‘์„ฑ)
      • Moya๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์‚ฌ์šฉํ•ด๋ณด๋ฉด์„œ ํ•˜๋‚˜์˜ ํƒ€์ž…์„ ๋‹ค ์ •์˜ ํ•ด์ค˜์•ผํ•œ๋‹ค๋Š” ์ž‘์—…์ด ์กฐ๊ธˆ(?)์€ ๊ท€์ฐฎ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ™์€ API๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š” ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค๊ณ  ์ƒ๊ฐ๋๋‹ค.
      • ์•„๋ฌด๋ž˜๋„ ๊ฐ™์€ API์— ๋Œ€ํ•ด์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์—์„œ ์–ด์ฉŒ๋ฉด ์ง‘์ค‘๋„๋ฅผ ๋–จ์–ด๋œจ๋ฆฌ๋Š” ์š”์ธ์ด ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
      • Moya์˜ TargetType์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ•ญ๋ชฉ๋“ค์„ ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•ด๋†“๊ณ , ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๋ถ„์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ณด๋‹ค ์ง‘์ค‘๋„ ์žˆ๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
    • ์˜คํžˆ๋ ค ๋‚ด๊ฐ€ ์ง€๊ธˆ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋งํ•˜๊ณ  ์žˆ๊ธดํ•˜์ง€๋งŒ ใ…‹ใ…‹ ์•„๋ฌดํŠผ๊ฐ„!! Moya๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋งž์ถฐ ํ•„์š”ํ•œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  Moya๋ฅผ ์ข€ ๋” ํšจ์œจ์ ์œผ๋กœ ์“ฐ๊ธฐ ์œ„ํ•ด์„  CommonMoyaProvider๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ชจ๋“  API ์ฝ”๋“œ์— ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
      • ex) ๋ชจ๋“  api response์— ์ค‘๋ณต ๋กœ๊ทธ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐ˜๋ณต์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด Custom ํ•ด์ฃผ๋Š” ๊ฒƒ.
      • CommonMoyaProvider์—๋Š” Moya TargetType์„ ๊ฐ€์ง„ API๋“ค์„ ๋ชจ๋‘ ๋‹ด์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Generic ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. CommonMoyaProvider<T: TargetType>
    • ๋ง‰ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์œ„ํ•ด์„œ Moya๋Š” ์“ฐ๋Š”๊ฒŒ ์ง„์งœ ์ข‹์•„!! ์ด์ •๋„์˜ ๋ฉ”๋ฆฌํŠธ๋Š” ์•„๋‹Œ ๋А๋‚Œ์ด ๋“ค์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ถ„๋ช…ํžˆ ์“ฐ์ž„์ƒˆ๊ฐ€ ์ข‹๊ณ  ํ™œ์šฉ๋„๊ฐ€ ๋†’์€๊ฑด ํ™•์‹ค!ํ•˜๋‹ค. ์ฝ”๋“œ๋„ ๊ฐ„๊ฒฐํ•ด์ง€๊ณ ..?
    • ์ด๋ฒˆ ํฌ์ŠคํŒ… ๋!

    References

    iOS Networking and Testing | ์šฐ์•„ํ•œํ˜•์ œ๋“ค ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ
    Networking์€ ์š”์ฆ˜ ์•ฑ์—์„œ ๊ฑฐ์˜ ํ•„์ˆ˜์ ์ธ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ์„ค์น˜๋˜์–ด ์žˆ๋Š” ์•ฑ๋“ค ์ค‘์— ๋„คํŠธ์›Œํ‚น์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์•ฑ์€ ๊ฑฐ์˜ ์—†์„ ๊ฒ๋‹ˆ๋‹ค. API ์ถ”๊ฐ€๊ฐ€ ์‰ฝ๊ณ  ๋ณ€๊ฒฝ์ด ์šฉ์ดํ•œ ๋„คํŠธ์›Œํ‚น ๋ชจ๋“ˆ์„ ๊ฐœ๋ฐœํ•˜๋Š”๊ฒƒ์ด ์ค‘์š”ํ•œ ์ด์œ ์ฃ . ๋ฏธ๋“œ ๋ณด์…จ๋‚˜์š”? ์‰˜๋“  ์ฟ ํผ ๋ผ๋Š” ์ฃผ์ธ๊ณต์€ 16์‚ด์— ๋ฐ•์‚ฌํ•™์œ„๋ฅผ ์ทจ๋“ํ•  ๋งŒํผ ์ฒœ์žฌ์ด๋ฉด์„œ ๋™์‹œ์— ๋Œ€๋‹จํžˆ ํŠน์ดํ•œ ์‚ฌ๋žŒ์ž…๋‹ˆ๋‹ค. ์ฃผ๋ณ€์—์„œ "Are you crazy?" ๋ผ๋Š” ๋ง์„ ์ž์ฃผ ๋“ฃ์Šต๋‹ˆ๋‹ค.
    https://techblog.woowahan.com/2704/

    Uploaded by N2T

Designed by Tistory.