티스토리 뷰

반응형

Do

 

세계의 날씨 정보를 제공하는 API 중 OpenWeatherMap이 있습니다.

무료로 제공되는 API이고 REST 방식으로 제공됩니다.

RxJava를 사용하여 데이터를 가져올 것입니다.

 

 

 

Api Key 받기

 

API Key 생성을 위해 OpenWeatherMap 홈페이지에 접속한 후 중간 위의 Sign UP을 눌러 회원 가입합니다.

 

 

회원 가입을 완료하면 API 메뉴를 누른 후, Cueent weather data에 있는 Subscribe를 누릅니다.

 

 

Free에 있는 Get Api key and Start를 누른 후 맨 위 중간에 있는 Sign in을 누릅니다.

 

 

API Keys를 누르면 Default에 API Key 하나가 생긴 것을 볼 수 있습니다.

 

 

API 준비가 끝났으니 이제 간단한 REST API 호출을 이용해 특정 도시의 현재 날씨를 얻어올 것입니다.

 

 

OpenWeather Json 구조

제공되는 JSON 데이터는 아래와 같습니다.

 

{
    "coord":{
        "lon":-0.13,
        "lat":51.51
    },
    "weather":[
        {
            "id":801,
            "main":"Clouds",
            "description":"few clouds",
            "icon":"02d"
        }
    ],
    "base":"stations",
    "main":{
        "temp":279.49,
        "feels_like":272.57,
        "temp_min":278.15,
        "temp_max":280.93,
        "pressure":1037,
        "humidity":45
    },
    "visibility":10000,
    "wind":{
        "speed":6.2,
        "deg":50,
        "gust":11.8
    },
    "clouds":{
        "all":20
    },
    "dt":1585476180,
    "sys":{
        "type":1,
        "id":1414,
        "country":"GB",
        "sunrise":1585460504,
        "sunset":1585506501
    },
    "timezone":3600,
    "id":2643743,
    "name":"London",
    "cod":200
}

 

JSON 데이터를 통해 현재 온도, 도시 이름, 국가 이름을 얻어올 것입니다.

 

 

 

코드 작성

입력

import common.CommonUtils
import common.Log
import common.OkHttpHelper
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import java.util.regex.Pattern

class OpenWeatherMapV1 {
    private val URL = "http://api.openweathermap.org/data/2.5/weather?q=London&appid="
    private val API_KEY = "여기에 API KEY"

    fun run() {
        val source = Observable.just(URL + API_KEY)
            .map(OkHttpHelper()::getWithLog)
            .subscribeOn(Schedulers.io())

        val temperature = source.map(this::parseTemperature)
        val city = source.map(this::parseCityName)
        val country = source.map(this::parseCountry)

        CommonUtils.start()

        Observable.concat(temperature, city, country)
            .observeOn(Schedulers.newThread())
            .subscribe { data -> Log.it(data) }
        CommonUtils.sleep(3000)

    }

    private fun parseTemperature(json: String): String = parse(json, "\"temp\":[0-9]*.[0-9]*")

    private fun parseCityName(json: String): String = parse(json, "\"name\":\"[a-zA-Z]*\"")

    private fun parseCountry(json: String): String = parse(json, "\"country\":\"[a-zA-Z]*\"")

    private fun parse(json: String, regex: String): String {
        val pattern = Pattern.compile(regex)
        val match = pattern.matcher(json)

        if (match.find()) return match.group()

        return "N/A"
    }
}

fun main() {
    val demo = OpenWeatherMapV1()
    demo.run()
}

 

출력

RxCachedThreadScheduler-1 | debug = OkHttp call URL = http://api.openweathermap.org/data/2.5/weather...
RxNewThreadScheduler-1 | 438 | value = "temp":279.63
RxCachedThreadScheduler-2 | debug = OkHttp call URL = http://api.openweathermap.org/data/2.5/weather...
RxNewThreadScheduler-1 | 559 | value = "name":"London"
RxCachedThreadScheduler-1 | debug = OkHttp call URL = http://api.openweathermap.org/data/2.5/weather...
RxNewThreadScheduler-1 | 680 | value = "country":"GB"

 

위 코드에 대한 설명은 아래와 같습니다.

 

 

  1. temperature, city, country 각각에 map 함수를 호출하여 파싱 한다.
  2. 간단한 형태로 이루어져 있어서 정규 표현식을 사용했다. 
  3. 파싱 해서 얻은 각각의 정보를 취합하기 위해서 concat 함수를 호출했다.
  4. subscribeOn(Schedulers.io())를 통해 IO 스케줄러에서 REST API 호출을 했다.
  5. 호출 결과는 observeOn(Schedulers.newThread())를 통해 뉴 스레드 스케줄러에서 실행했다.

 

정규 표현식을 사용했지만, 복잡한 내용을 파싱 할 때는 Gson 등의 라이브러리를 활용하는 것이 좋습니다.

 

결과적으로 보면 원하는 정보가 나왔지만 REST API 호출이 3번 발생한 것을 볼 수 있습니다.

즉, 원하는 정보가 10 개였다면 10번의 API를 호출해야 되니 굉장히 비효율적인 것을 볼 수 있습니다.

 

 

위에서 쓰인 OkHttpHelper클래스의 getWithLog 메서드의 코드는 아래와 같습니다.

 

입력

@Throws(IOException::class)
    fun getWithLog(url: String): String {
        Log.d("OkHttp call URL = $url")
        return get(url)
    }

 

 

 

 

아래는 위의 코드를 개선한 코드입니다.

 

입력

import common.CommonUtils
import common.Log
import common.OkHttpHelper
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import java.util.regex.Pattern

class OpenWeatherMapV2 {
    private val URL = "http://api.openweathermap.org/data/2.5/weather?q=London&appid="
    private val API_KEY = "여기에 API KEY"

    fun run() {
        val source = Observable.just(URL + API_KEY)
            .map(OkHttpHelper()::getWithLog)
            .subscribeOn(Schedulers.io())
            .share()
            .observeOn(Schedulers.newThread())

        source.map(this::parseTemperature).subscribe { data -> Log.it(data) }
        source.map(this::parseCityName).subscribe { data -> Log.it(data) }
        source.map(this::parseCountry).subscribe { data -> Log.it(data) }

        CommonUtils.sleep(3000)
    }

    private fun parseTemperature(json: String): String = parse(json, "\"temp\":[0-9]*.[0-9]*")

    private fun parseCityName(json: String): String = parse(json, "\"name\":\"[a-zA-Z]*\"")

    private fun parseCountry(json: String): String = parse(json, "\"country\":\"[a-zA-Z]*\"")

    private fun parse(json: String, regex: String): String {
        val pattern = Pattern.compile(regex)
        val match = pattern.matcher(json)

        if (match.find()) return match.group()

        return "N/A"
    }
}

fun main() {
    val demo = OpenWeatherMapV2()
    demo.run()
}

 

출력

RxCachedThreadScheduler-1 | debug = OkHttp call URL = http://api.openweathermap.org/data/2.5/weather...
RxNewThreadScheduler-1 | value = "temp":279.46
RxNewThreadScheduler-2 | value = "name":"London"
RxNewThreadScheduler-3 | value = "country":"GB"

 

 

ConnectableObservable 클래스를 사용하였습니다.

 

ConnectableObservable 클래스는 1개의 Observable을 여러 구독자가 공유하는 방식으로 차가운 Observable을 뜨거운 Observable로 변환해줍니다.

 

여기에서는 ConnectableObservable 클래스의 publish 함수와 refCount 함수를 활용했습니다. 소스 코드에 보이지 않는 이유는 두 함수를 합하면 Observable의 share 함수가 되기 때문입니다.

 

map 함수와 subscribe 함수를 차례로 호출했습니다. subscribe 함수를 호출하면 Observable의 데이터가 다시 발행되기 때문에 서버의 REST API를 호출하지 않아도 됩니다.

반응형

'알려주는 이야기 > RxJava' 카테고리의 다른 글

24. RxJava 예외 처리  (0) 2020.08.14
23. RxJava 디버깅  (0) 2020.07.23
21. RxJava - observeOn 함수  (0) 2020.07.22
20. RxJava - 콜백 지옥  (0) 2020.07.21
19. RxJava - 스케줄러 종류  (0) 2020.07.21
댓글