안녕하세요!


안드로이드 앱을 개발하다보면 서버와의 통신 작업이 많이 필요하게 되는데요

이때 정말 편하게 사용할 수 있는 라이브러리인 Retrofit을 소개해보려고 합니다.


이 글을 작성할 때 

생각자유의 안드로이드 이야기 - Retrofit 기본 기능에 대해서 알아보자 

http://goo.gl/fOumEV

이 글을 많이 참조하였습니다!


http://square.github.io/retrofit/


저는 Retrofit을 이용하여 날씨 정보를 가져오는 API 통신을 해보겠습니다.

https://developers.skplanetx.com/apidoc/kor/weather/

SKplanet 에서 제공하는 Weather Planet에서 정보를 받아오겠습니다.


먼저 build.gradle(Module : app) 에 다음과 같이 추가해줍니다.

dependencies {
compile 'com.squareup.retrofit2:retrofit:2.1.0' // Retrofit
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3' // Gson 변환을 위한 converter
}


인터넷 통신을 하기 위해 퍼미션을 추가 해 줍니다.

<uses-permission android:name="android.permission.INTERNET"/>


이로써 Retrofit 을 사용하기 위한 준비가 끝났습니다.

이제 본격적으로 날씨 정보 얻어오기를 달려보겠습니다!


저는 현재날씨(시간별) 데이터를 받아보겠습니다.

Weather Planet 의 현재 날씨 API는 다음과 같은 양식으로 되어있습니다.

Resource URI

http://apis.skplanetx.com/weather/current/hourly?version={version}&lat={lat}&lon={lon}&city={city}&county={county}&village={village}

Protocol - REST

HTTP Method - GET


여기서 

http://apis.skplanetx.com/ 은 baseURL

weather/current/hourly 는 현재날씨(시간별)에 해당하는 URL

? 뒤쪽 부분은 변수 명이 됩니다.


이를 이용하여 Interface Class를 선언해 보겠습니다

저는 WeatherRepo 클래스 안에 인터페이스를 선언하여 Call<WeatherRepo> 로 나오게 되었습니다.

WeatherRepo 클래스는 API 통신을 하며 데이터를 받는 객체 클래스로 아래에서 설명드리겠습니다.

버전 정보와 위도, 경도만 필요하기 때문에 country 와 village는 생략하였습니다.

public interface WeatherApiInterface {
@Headers({"Accept: application/json"})
@GET("weather/current/hourly")
Call<WeatherRepo> get_Weather_retrofit(@Query("version") int version, @Query("lat") String lat, @Query("lon") String lon);
}

이와 같이

@Headers 에는 필요한 헤더 부분을 ({ ~~ . ~~ }) 형식으로 넣어줄 수 있습니다.

@GET 부분에는 현재날씨(시간별)에 해당하는 URL 을 넣어줍니다.

다음으로 Call입니다.

Call <'주고받을 객체'> '함수명' (@Query ('변수 이름') '자료형' '변수 이름')  형태로 나타나 있는데요

@Query 를 통해 위치가 바뀌어도 동적으로 값을 받아올 수 있습니다!


다음으로는 응답 샘플 코드를 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
    "result":{
        "message":"성공",
        "code":9200,
        "requestUrl":"/weather/current/hourly?lon=&village=도곡동&county=강남구&lat=&city=서울&version=1"
    },
    "common":{
        "alertYn":"Y",
        "stormYn":"N"
    },
    "weather":{
        "hourly":[
            {
                "grid":{
                    "latitude":"37.4870600000",
                    "longitude":"127.0460400000",
                    "city":"서울",
                    "county":"강남구",
                    "village":"도곡동"
                },
                "wind":{
                    "wdir":"266.00",
                    "wspd":"3.20"
                },
                "precipitation":{
                    "type":"0",
                    "sinceOntime":"0.00"
                },
                "sky":{
                    "name":"맑음",
                    "code":"SKY_O01"
                },
                "temperature":{
                    "tc":"6.80",
                    "tmax":"8.10",
                    "tmin":"-0.90"
                },
                "humidity":"31.00",
                "lightning":"0",
                "timeRelease":"2013-11-11 14:00:00"
            }
        ]
    }
}
cs


여기서 저희는 필요한 것만 골라서 받아올 수 있습니다!

갓 구글께서는 JSON을 쉽게 파싱할 수 있도록 GSON 라이브러리를 만들어 주셨습니다.

이를 야무지게 사용해 보겠습니다.


public class WeatherRepo {

@SerializedName("result")
Result result;
@SerializedName("weather")
weather weather;

public class Result {
@SerializedName("message") String message;
@SerializedName("code") String code;

public String getMessage() {return message;}
public String getCode() {return code;}
}

public class weather {

public List<hourly> hourly = new ArrayList<>();
public List<hourly> getHourly() {return hourly;}

public class hourly {
@SerializedName("sky") Sky sky;
@SerializedName("precipitation") precipitation precipitation;
@SerializedName("temperature") temperature temperature;
@SerializedName("wind") wind wind;

public class Sky{
@SerializedName("name") String name;
@SerializedName("code") String code;

public String getName() {return name;}
public String getCode() {return code;}
}

public class precipitation{ // 강수 정보
@SerializedName("sinceOntime") String sinceOntime; // 강우
@SerializedName("type") String type; //0 :없음 1:비 2: 비/눈 3: 눈

public String getSinceOntime() {return sinceOntime;}
public String getType() {return type;}
}
public class temperature{
@SerializedName("tc") String tc; // 현재 기온

public String getTc() {return tc;}
}
public class wind{ // 바람
@SerializedName("wdir") String wdir;
@SerializedName("wspd") String wspd;

public String getWdir() {return wdir;}
public String getWspd() {return wspd;}
}
public Sky getSky() {return sky;}
public hourly.precipitation getPrecipitation() {return precipitation;}
public hourly.temperature getTemperature() {return temperature;}
public hourly.wind getWind() {return wind;}
}
}
public Result getResult() {return result;}
public weather getWeather() {return weather;}

public interface WeatherApiInterface {
@Headers({"Accept: application/json"})
@GET("weather/current/hourly")
Call<WeatherRepo> get_Weather_retrofit(@Query("version") int version, @Query("lat") String lat, @Query("lon") String lon);
}
}

위의 응답 데이터 양식과 아래의 코드를 보면 

GSON이 쉽고 편하고 아름답게 파싱해 주는 것을 볼 수 있습니다!

여기서 @SerializedName 어노테이션이 포인트인데요

이 어노테이션을 이용하면 wspd(풍속) 을 

@SerializedName("wspd") String windSpeed; 처럼 변수 이름을 바꿔 넣을 수도 있습니다.


응답코드에 hourly 뒤를 보시면 JSONArray 형태로 되어 있는데요

이를 위와 같이 클래스로 만들어 ArrayList<hourly> 형태로 만들어준다면 쉽게 파싱이 가능합니다!

이부분을 잘 몰라서 삽질 했던 기억이 나네요...


다음으로는 위에서 만든 Retrofit interface를 구현해보도록 하겠습니다!

저는 날씨 정보를 가져오는 스레드인

WeatherThread 에서 날씨 정보를 받아오겠습니다.


public class WeatherThread extends Thread {
final static String TAG = "WeatherThread";
Context mContext;
WeatherRepo weatherRepo;
Handler handler;

int version = 1;
String lat;
String lon;

public WeatherThread(Handler handler, Context mContext, double lat, double lon) {
this.mContext = mContext;
this.lat = String. valueOf(lat);
this.lon = String .valueOf(lon);
this.handler = handler;
}

@Override
public void run() {
super.run();
Retrofit client = new Retrofit.Builder().baseUrl("http://apis.skplanetx.com/").addConverterFactory(GsonConverterFactory.create()).build();
WeatherRepo.WeatherApiInterface service = client.create(WeatherRepo.WeatherApiInterface.class);
Call<WeatherRepo> call = service.get_Weather_retrofit(version, lat, lon);
call.enqueue(new Callback<WeatherRepo>() {
@Override
public void onResponse(Call<WeatherRepo> call, Response<WeatherRepo> response) {
if(response.isSuccessful()){
weatherRepo = response.body();
Log.d(TAG,"response.raw :"+response.raw());
if(weatherRepo.getResult().getCode().equals("9200")){ // 9200 = 성공
Weather.getInstance().setTemperature(weatherRepo.getWeather().getHourly().get(0).getTemperature().getTc());
Weather.getInstance().setCloud(weatherRepo.getWeather().getHourly().get(0).getSky().getName());
Weather.getInstance().setWind_direction(weatherRepo.getWeather().getHourly().get(0).getWind().getWdir());
Weather.getInstance().setWind_speed(weatherRepo.getWeather().getHourly().get(0).getWind().getWspd());
Weather.getInstance().setIcon(weatherRepo.getWeather().getHourly().get(0).getSky().getCode());

Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("weather","weather");
msg.setData(bundle);
handler.sendMessage(msg);
}else{
Log.e(TAG,"요청 실패 :"+weatherRepo.getResult().getCode());
Log.e(TAG,"메시지 :"+weatherRepo.getResult().getMessage());
}
}
}

@Override
public void onFailure(Call<WeatherRepo> call, Throwable t) {
Log.e(TAG,"날씨정보 불러오기 실패 :" + t.getMessage() );
Log.e(TAG,"요청 메시지 :"+call.request());
}
});
}
}

생성자를 통해 위도와 경도를 받아와 

service.get_Weather_retrofit(version, lat, lon) 부분에 값을 넣어줍니다.

요청이 성공적으로 수행되면 onResponse로 진입을 하게 되는데 이곳에서 response.isSuccessful() 로 요청이 성공적으로 이루어 지면

WeatherRepo 에 response.body를 입혀줍니다. response.body 에는 위의 응답 샘플 코드와 같은 정보가 들어있어 이를 

GSON으로 파싱 할 수 있을 것입니다.

response.raw 함수로 응답으로 온 http raw 데이터를 볼 수 있습니다.

응답코드가 정상인지 확인한 후 

weatherRepo.get~~~ 함수들로 값을 받아와 객체에 저장할 수 있습니다!


Retrofit은 제가 사용한 GET 방법 외에도 POST, PUT 등 다양한 Request 방식을 지원합니다.

여러가지 사용법은 추후 공부를 더 하게되면 추가 해 나가도록 하겠습니다!


이상 Retrofit 사용법 글을 마치겠습니다 감사합니다!




  1. ㅁㅁㅁ 2016.09.08 14:39 신고

    좋은 글 감사합니다 !

  2. ㅇㅁㄸ 2016.10.19 22:04 신고

    좋은 정보 감사합니다!!!
    작성자님께 질문이있는데요
    날씨 API 구현 공부 중 올려주신 코드를 참고해 실행해봤는데
    run클래스에서
    Weather.getInstance().setTemperature(weatherRepo.getWeather().getHourly().get(0).getTemperature().getTc());
    Weather.getInstance().setCloud(weatherRepo.getWeather().getHourly().get(0).getSky().getName());
    Weather.getInstance().setWind_direction(weatherRepo.getWeather().getHourly().get(0).getWind().getWdir());
    Weather.getInstance().setWind_speed(weatherRepo.getWeather().getHourly().get(0).getWind().getWspd());
    Weather.getInstance().setIcon(weatherRepo.getWeather().getHourly().get(0).getSky().getCode());
    이 다섯줄의 Weather에 오류가 납니다
    Error:(192, 29) error: cannot find symbol variable Weather
    에러명은 이건데요...
    제가 아직 많이... 초보라서 아직 어느부분이 잘못된건지 찾지못했네요...
    올려주신 코드에 Weather 변수가 선언되어있지 않은데 따로 필요한가요???

    • 형필 2016.10.21 12:55 신고

      네! 본문에는 올리지 않았지만 Weather 클래스를 만들어주어야 해요
      저는 현재위치의 현재 날씨만 받아왔지만
      만약 시간별이나 날짜별로 여러 날씨를 받아오려면 ArrayList<Weather> 형태로 만들어 사용하시면 될거에요!

  3. 까치 2016.11.25 17:06 신고

    참고 잘 했습니다!

  4. 범범 2016.12.05 22:38 신고

    아 죄송한대 클래스를 몇개만들고 gson을 어떻게 해야되는지 알려주시면 안될까요 ?//

    • 형필 2016.12.22 11:36 신고

      gson은 WeatherRepo 클래스를 통해 파싱하실 수 있어요 클래스는 어떤 클래스를 말씀하시는건가요?

  5. 우헹 2016.12.20 05:15 신고

    안녕하세요 잘봤습니다. 혹시 api key를 따로 입력은 안해도되는건가요?

  6. 1 2017.01.13 16:28 신고

    나머지 부분도 좀더 자세하게 올려주시면 ㅠㅠ

  7. 이재영 2017.01.16 14:07 신고

    Header 부분에 api key 추가를 어떻게 하는건가요?? sk planet에서 키를 발급 받았는데 어떻게 방법으로를 추가 시키는지..ㅠㅠ 잘모르겠습니다.ㅠ

    • 이재영 2017.01.16 17:42 신고

      header 부분에 appkey를 어떻게 선언해야할지 찾다가 쿼리문에 직접 넣어서 해결했네요.

    • 형필 2017.01.17 13:55 신고

      안녕하세요!
      저는
      @Headers({"Accept: application/json","access_token: ~~~~","appKey: ~~~"})

      이렇게 구현했었습니다!

  8. 2017.01.17 11:10

    비밀댓글입니다

    • 2017.01.17 13:51

      비밀댓글입니다

  9. ㅠㅠ 2017.01.18 13:57 신고

    좋은 글 올려주셔서 감사합니다.
    근데 댓글에 Weather 클래스를 만들라고 하셨는데
    아직 초보라 어떻게 해야할지 모르겠어서
    죄송하시만 Weather 클래스 소스좀 보여주시면 안될까요?..

    • 형필 2017.01.19 17:55 신고

      public class Weather {
      // 필요시 생성자 추가하기
      String temperature;
      String dust_grade;
      String dust_value;
      String uv;
      String cloud;
      String wind_direction;
      String wind_speed;
      String icon;
      // get, set 메소드 추가하기
      }
      이런 식으로 Weather 객체를 만드시면 될거같아요! 본문에는 싱글턴패턴을 사용해서 getInstance()를 사용했지만 불필요한것같네요. 코드 수정해서 곧 다시 올릴게요!!

  10. js 2017.03.20 21:33 신고

    이 방식이 OKHttp 라이브러리와 다른건가요?
    사용방식이 초보입장에서 상당히 어려워 보이는데요?
    Okhttp와 Gson을 이용하면 서버와 통신으로 json 받아 오는게 수월하던데
    차이와 장점이 궁금합니다.

+ Recent posts