[ swift ] Moya와 OpenWeatherMap One Call API 연동하기

sonny·2025년 1월 8일
2

TIL

목록 보기
96/133

모야는 참 편하다.

TargetType를 채택하려면 저 다섯가지를 꼭 작성해야한다고 친히 Fix가 뜨고 나는 안에 값만 잘 넣어주면 된다.

OpenWeatherMap의 One Call API를 사용해 날씨 정보를 받아오려면 먼저 네트워크 요청을 어떻게 구성해야 할지 고민을 했다.

난 Moya 라이브러리를 이용해서 구조적으로 API를 관리하는 방법을 선택했는데,

TargetType 프로토콜을 구현해서 API 엔드포인트를 정의하는 과정을 해보면서 각 프로퍼티가 어떤 역할을 하는지 자세히 공부해봤다.

작성한 WeatherAPI


import Foundation
import Moya

enum WeatherAPI {
    case oneCall(lat: Double,      // 위도
                 lon: Double,      // 경도
                 exclude: String,  // 제외할 항목
                 units: String)    // 단위 (metric = 온도 섭씨 (°C))
}

extension WeatherAPI: TargetType {
    var baseURL: URL {
        return URL(string: "https://api.openweathermap.org/data/3.0")!
    }
    
    var path: String {
        switch self {
        case .oneCall:
            return "/onecall"
        }
    }
    
    var method: Moya.Method {
        return .get
    }
    
    var task: Moya.Task {
        switch self {
        case let .oneCall(lat, lon, exclude, units):
            let parameters: [String: Any] = [
                "lat": lat,
                "lon": lon,
                "exclude": exclude,
                "units": units,
                "appid": "8ef68fb1d27fb92f55802750b90d54f7"
            ]
            return .requestParameters(parameters: parameters, encoding: URLEncoding.default)
        }
    }
    
    var headers: [String : String]? {
        ["Content-Type": "application/json"]
    }
}

enum WeatherAPI의 역할

enum은 여러 가지 상태나 케이스를 타입 안전하게 표현하기에 적합한데, 내 코드에서는 One Call API만 사용하기 때문에 oneCall(lat:lon:exclude:units:)라는 하나의 케이스만 정의해봤다.

  • lat: 위도
  • lon: 경도
  • exclude: 응답에서 제외할 항목
  • units: 온도 및 풍속 등의 단위

exclude 파라미터

응답에서 특정 기상 정보를 제외하고 싶을 때 사용하는 건데, 예를 들어 "minutely"를 제외하면 1분 단위 예보 데이터가 응답에서 빠진다고 한다.
exclude="minutely,hourly"처럼 쉼표로 구분하면 여러 항목을 동시에 뺄 수 있기도 한데 이렇게 불필요한 데이터를 제거해야 트래픽 절감파싱 비용 감소에 도움이 된다고 한다.

units 파라미터

온도, 풍속 등의 단위를 결정하는 것.
"metric"이면 섭씨(°C)와 m/s, "imperial"이면 화씨(°F)와 mph 단위를 사용하는데 만약 기본값(standard)으로 할 경우에는 켈빈 단위를 사용한다고 한다.

이렇게 열거형의 연관값을 통해 이 API가 필요한 매개변수를 선언해줬는데 이 덕분에 잘못된 타입을 넘겨주는 실수를 컴파일 단계에서 막을 수가 있다.
.
.

TargetType 프로토콜 구현

그리고 Moya를 사용하려면 TargetType 프로토콜을 준수해야 하는데, 주요 프로퍼티로는 baseURL, path, method, task, headers 등이 있다.

baseURL

  • 실제로 요청을 보낼 기본 URL을 정의 하는 것.
  • 나는https://api.openweathermap.org/data/3.0을 사용했고 URL(string:)이 옵셔널을 반환하기 때문에 강제 언래핑을 사용했다.

path

  • API 엔드포인트 즉 경로를 나타낸다.
  • One Call API를 호출하기 위해 "/onecall"로 지정을 했고
  • 만약 여기서 다른 API가 추가된다면 열거형에 새 케이스를 추가하고 여기서도 분기처리를 해줄 수 있다.

method

  • HTTP 메서드를 지정한다.
  • One Call API는 GET 방식을 사용하니 .get을 반환했다.
  • POST, PUT, DELETE 등이 필요한 경우에는 케이스별로 달라질 수 있다.

task

  • 네트워크 요청에 담길 파라미터인코딩 방식을 결정한다.
  • 스위치문을 통해 .oneCall(lat, lon, exclude, units) 케이스를 분기해주고 [String: Any] 딕셔너리를 만들어 lat, lon, exclude, units, appid(API Key) 등을 넣어준다.
  • 그리고 .requestParameters(parameters:encoding:)로 반환해주면, GET 방식이라서 URL 쿼리 스트링으로 반환이 된다.

headers

  • HTTP 요청 헤더를 지정하는 건데 여기서는 ["Content-Type": "application/json"]을 설정했다.
  • “JSON 형식의 데이터를 전송한다”는 의미를 서버에 알려주기 위한 것이다.
  • 전에 했던 BookApp에서는 카카오 API처럼 헤더 기반 인증(Authorization: KakaoAK ...)이 필요했었어서 이 프로퍼티에 그 키를 넣어주면 됐었다.
  • 근데 OpenWeatherMap쿼리 파라미터appid를 넣는 방식을 사용하기 때문에 별도의 인증 헤더를 넣을 필요가 없었다.

왜 이렇게 구조화할까?

  1. 타입 안전성

    • 열거형 연관값을 사용해, 각 API마다 필요한 파라미터의 타입을 명확하게 정의한다.
    • 잘못된 타입이 들어오면 컴파일 에러로 바로 확인 가능하다.
  2. 가독성 및 유지보수 용이

    • TargetType 프로토콜을 통해 API 호출 정보를 하나의 구조로 관리한다.
    • API가 늘어나도 열거형 케이스와 프로토콜 구현부만 수정하면 된다.
  3. 중복 제거

    • baseURL, 공통 헤더, 에러 처리가 필요한 경우에도 쉽게 확장 가능하다.
    • 기존 Alamofire 기반의 네트워킹 로직보다 더 깔끔하게 유지할 수 있다.
  4. API별 요구 사항 대응

    • 일부 API는 헤더(Authorization)로 키를 보내야 하고,
    • 일부 API는 쿼리 파라미터(appid)로 키를 보내도록 요구할 수 있다.
    • switch self 구문 안에서 각각의 로직을 자유롭게 구현할 수 있다.

음...

순차적으로 해보니까 Moya와 TargetType을 사용하면 각 엔드포인트별로 필요한 정보를 체계적으로 관리할 수 있다는게 좀 더 와닿은게, enum으로 API를 정의하고, task 메서드에서 파라미터와 인코딩 방식을 결정하니 코드의 가독성유지보수성이 대폭 향상된 느낌이 뭔지 느껴졌다.

OpenWeatherMap의 One Call API뿐 아니라, 저번에 했던 bookApp을 했을 때처럼 다른 서비스를 연동허게 될 때도 비슷한 방식으로 접근할 수 있는거고

API마다 인증이나 파라미터 요구가 다를 수는 있겠지만 TargetType 프로토콜을 구현하는 패턴은 대부분 동일하기 때문에 혹시라도 여러 API를 동시에 연동해야 하는 상황이 생긴다면 이 구조를 사용할 경우 혼동을 줄이고 팀원분들과 역할 분담도 조금이나마 명확하게 할 수 있을 것 같다.

profile
iOS 좋아. swift 좋아.

0개의 댓글

관련 채용 정보