Programing/iOS

[iOS] Apollo를 이용해 Graph QL 서버와 연동해보기 - 2

5주년 2020. 8. 7. 17:34

안녕하세요~!🙋🙋🙋 5anniversary 입니다~!~!~!~!

 

이번 포스팅에서는 저번 apollo 활용 첫번째 포스팅에 이어서 서버 데이터를 받아오는 시간을 가져볼거에요~!~!

 

우선 GraphQL이 어떻게 사용되는지 간단하게 알아볼까요???

 

GraphQL은 거의 공통적으로 query를 클라이언트 레벨에서 request 해주면 server에서 해당 query를 승인 해준뒤 알맞는 데이터를 보내주는 구조에요!! 이런 구조를 글로만 설명하면 이해가 좀... 힘들겠죠??? 보러가시죠~! 

 

사이트에 들어가시죠!! 

 

사이트에 들어가면 이런 화면이 나올거에요!!

 

 

오른쪽의 Docs를 누르면 이 서버에서 제공하는 GraphQL의 query리스트가 있구요!!

schema를 누르면 이 서버에서 제공하는 GraphQL의 shema를 보여줍니다.

 

여기서 데이터가 어떤 방식으로 전달되는지 볼게요!!

 

왼쪽 창에 

query {
  launches{
    cursor
    hasMore
  }
}

와 같은 쿼리를 넣어주세요!!

 

그럼 이렇게 데이터가 나오게 돼요!!

 

여기서 원하는 데이터들을 알맞게 받아올수있는데요!!

 

docs를 보고 원하는 데이터들을 쿼리 리스트에 넣어주세요!!

 

query {
  launches{
  	cursor
    hasMore
    launches{
      id
    }
  }
}

 

이런 쿼리를 넣어주면 이렇게 나온답니다~!

 

이제 GraphQL이 어떤 방식으로 구현되는지 알아봤으니 구현을 해볼까여~??

 

empty 파일으로 LaunchList.graphql 라는 이름의 파일을 만들어 볼게요~! 여기서도 조건이 있는데요

 

1. Target이 설정되어있으면 안되구요

2. schema.json 파일과 같은 레벨에 위치해 있어야합니다.

 

그리고 이제 알맞는 쿼리를 넣어줄까요??

 

query LaunchList($cursor:String) {
  launches(after:$cursor) {
    hasMore
    cursor
    launches {
      id
      site
      mission {
        name
        missionPatch(size: SMALL)
      }
    }
  }
}

여기서는 cursor를 페이징처리를 위해 받아서 넘겨주는 방식으로 처리합니다.

 

그리고~!

 

네트워크 관련 코드를 작성해야겠죠????

 

import Apollo

class Network {
  static let shared = Network()
    
    private(set) lazy var apollo: ApolloClient = {
        let httpNetworkTransport =
        HTTPNetworkTransport(url: URL(string: "https://apollo-fullstack-tutorial.herokuapp.com/")!)
        httpNetworkTransport.delegate = self
        return ApolloClient(networkTransport: httpNetworkTransport)
    }()
}

extension Network: HTTPNetworkTransportPreflightDelegate {
    func networkTransport(_ networkTransport: HTTPNetworkTransport,
                          shouldSend request: URLRequest) -> Bool {
        return true
    }
    
    func networkTransport(_ networkTransport: HTTPNetworkTransport,
                          willSend request: inout URLRequest) {
    }
}

를 만들고 작성해주세요~!

 

그 다음 service코드를 작성할건데요 

 

import Apollo

public final class LaunchListQuery: GraphQLQuery {
  /// The raw GraphQL definition of this operation.
  public let operationDefinition: String =
    """
    query LaunchList($cursor: String) {
      launches(after: $cursor) {
        __typename
        hasMore
        cursor
        launches {
          __typename
          id
          site
          mission {
            __typename
            name
            missionPatch(size: SMALL)
          }
        }
      }
    }
    """

  public let operationName: String = "LaunchList"

  public var cursor: String?

  public init(cursor: String? = nil) {
    self.cursor = cursor
  }

  public var variables: GraphQLMap? {
    return ["cursor": cursor]
  }

  public struct Data: GraphQLSelectionSet {
    public static let possibleTypes: [String] = ["Query"]

    public static let selections: [GraphQLSelection] = [
      GraphQLField("launches", arguments: ["after": GraphQLVariable("cursor")], type: .nonNull(.object(Launch.selections))),
    ]

    public private(set) var resultMap: ResultMap

    public init(unsafeResultMap: ResultMap) {
      self.resultMap = unsafeResultMap
    }

    public init(launches: Launch) {
      self.init(unsafeResultMap: ["__typename": "Query", "launches": launches.resultMap])
    }

    public var launches: Launch {
      get {
        return Launch(unsafeResultMap: resultMap["launches"]! as! ResultMap)
      }
      set {
        resultMap.updateValue(newValue.resultMap, forKey: "launches")
      }
    }

    public struct Launch: GraphQLSelectionSet {
      public static let possibleTypes: [String] = ["LaunchConnection"]

      public static let selections: [GraphQLSelection] = [
        GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
        GraphQLField("hasMore", type: .nonNull(.scalar(Bool.self))),
        GraphQLField("cursor", type: .nonNull(.scalar(String.self))),
        GraphQLField("launches", type: .nonNull(.list(.object(Launch.selections)))),
      ]

      public private(set) var resultMap: ResultMap

      public init(unsafeResultMap: ResultMap) {
        self.resultMap = unsafeResultMap
      }

      public init(hasMore: Bool, cursor: String, launches: [Launch?]) {
        self.init(unsafeResultMap: ["__typename": "LaunchConnection", "hasMore": hasMore, "cursor": cursor, "launches": launches.map { (value: Launch?) -> ResultMap? in value.flatMap { (value: Launch) -> ResultMap in value.resultMap } }])
      }

      public var __typename: String {
        get {
          return resultMap["__typename"]! as! String
        }
        set {
          resultMap.updateValue(newValue, forKey: "__typename")
        }
      }

      public var hasMore: Bool {
        get {
          return resultMap["hasMore"]! as! Bool
        }
        set {
          resultMap.updateValue(newValue, forKey: "hasMore")
        }
      }

      public var cursor: String {
        get {
          return resultMap["cursor"]! as! String
        }
        set {
          resultMap.updateValue(newValue, forKey: "cursor")
        }
      }

      public var launches: [Launch?] {
        get {
          return (resultMap["launches"] as! [ResultMap?]).map { (value: ResultMap?) -> Launch? in value.flatMap { (value: ResultMap) -> Launch in Launch(unsafeResultMap: value) } }
        }
        set {
          resultMap.updateValue(newValue.map { (value: Launch?) -> ResultMap? in value.flatMap { (value: Launch) -> ResultMap in value.resultMap } }, forKey: "launches")
        }
      }

      public struct Launch: GraphQLSelectionSet {
        public static let possibleTypes: [String] = ["Launch"]

        public static let selections: [GraphQLSelection] = [
          GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
          GraphQLField("id", type: .nonNull(.scalar(GraphQLID.self))),
          GraphQLField("site", type: .scalar(String.self)),
          GraphQLField("mission", type: .object(Mission.selections)),
        ]

        public private(set) var resultMap: ResultMap

        public init(unsafeResultMap: ResultMap) {
          self.resultMap = unsafeResultMap
        }

        public init(id: GraphQLID, site: String? = nil, mission: Mission? = nil) {
          self.init(unsafeResultMap: ["__typename": "Launch", "id": id, "site": site, "mission": mission.flatMap { (value: Mission) -> ResultMap in value.resultMap }])
        }

        public var __typename: String {
          get {
            return resultMap["__typename"]! as! String
          }
          set {
            resultMap.updateValue(newValue, forKey: "__typename")
          }
        }

        public var id: GraphQLID {
          get {
            return resultMap["id"]! as! GraphQLID
          }
          set {
            resultMap.updateValue(newValue, forKey: "id")
          }
        }

        public var site: String? {
          get {
            return resultMap["site"] as? String
          }
          set {
            resultMap.updateValue(newValue, forKey: "site")
          }
        }

        public var mission: Mission? {
          get {
            return (resultMap["mission"] as? ResultMap).flatMap { Mission(unsafeResultMap: $0) }
          }
          set {
            resultMap.updateValue(newValue?.resultMap, forKey: "mission")
          }
        }

        public struct Mission: GraphQLSelectionSet {
          public static let possibleTypes: [String] = ["Mission"]

          public static let selections: [GraphQLSelection] = [
            GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
            GraphQLField("name", type: .scalar(String.self)),
            GraphQLField("missionPatch", arguments: ["size": "SMALL"], type: .scalar(String.self)),
          ]

          public private(set) var resultMap: ResultMap

          public init(unsafeResultMap: ResultMap) {
            self.resultMap = unsafeResultMap
          }

          public init(name: String? = nil, missionPatch: String? = nil) {
            self.init(unsafeResultMap: ["__typename": "Mission", "name": name, "missionPatch": missionPatch])
          }

          public var __typename: String {
            get {
              return resultMap["__typename"]! as! String
            }
            set {
              resultMap.updateValue(newValue, forKey: "__typename")
            }
          }

          public var name: String? {
            get {
              return resultMap["name"] as? String
            }
            set {
              resultMap.updateValue(newValue, forKey: "name")
            }
          }

          public var missionPatch: String? {
            get {
              return resultMap["missionPatch"] as? String
            }
            set {
              resultMap.updateValue(newValue, forKey: "missionPatch")
            }
          }
        }
      }
    }
  }
}

예시로 나와있는 코드는 이렇게 되는데요 우선 이번 시간에는 실행 해보고~~

 

다음 포스팅에서 자세히 알아보도록 할게요~~~

 

그리고 이 서버 코드를 실행시키는 코드도 있어야겠죠??

 

예시에서는 어플을 실행시키자마자 서버 연동이 되는지 확인 하기 위해서 AppDelegate에 넣어줬는데요

 

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        Network.shared.apollo.fetch(query: LaunchListQuery()) { result in
          switch result {
          case .success(let graphQLResult):
            print(graphQLResult)
          case .failure(let error):
            print("Failure! Error: \(error)")
          }
        }

        return true
    }

 

이렇게 작성한 뒤 빌드를 해볼까요???

 

자 이렇게 서버에서 받아오는 데이터를 볼수가 있죠???🎉 🎉 🎉 🎉 

 

이제 다음 포스팅으로 넘어가서 하나씩 이 코드가 어떻게 작동하는지 알아보도록 하죠!!!

 

수고하셨습니다.