안녕하세요!


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

이때 정말 편하게 사용할 수 있는 라이브러리인 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 사용법 글을 마치겠습니다 감사합니다!




+ Recent posts