Privacy Policy

Hyeongpil built the 실력 측정기: 오버워치 app as an Ad Supported app. This SERVICE is provided by Hyeongpil at no cost and is intended for use as is.

This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.

If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at 실력 측정기: 오버워치 unless otherwise defined in this Privacy Policy.

Information Collection and Use

For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Android Ad ID. The information that I request will be retained on your device and is not collected by me in any way.

The app does use third party services that may collect information used to identify you.

Link to privacy policy of third party service providers used by the app

Log Data

I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.


Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.

This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.

Service Providers

I may employ third-party companies and individuals due to the following reasons:

  • To facilitate our Service;
  • To provide the Service on our behalf;
  • To perform Service-related services; or
  • To assist us in analyzing how our Service is used.

I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.


I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.

Links to Other Sites

This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.

Children’s Privacy

These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions.

Changes to This Privacy Policy

I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.

Contact Us

If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at


오늘은 제가 삽질했던 오류 해결법을 올려드리려고 합니다

ZipException은 Gradle에서 라이브러리를 추가할 때 가끔 발생하는 오류인데요

라이브러리 안에 같은 모듈이 중복되어 생기는 오류입니다.

보통 서드파티 라이브러리에서 구글 gms 등을 포함하여 충돌나는 경우가 많습니다.

다음과 같이 해결할 수 있습니다!

compile ('패키지 이름'){
exclude group: ''

이렇게 하면 해당하는 패키지에 gms를 제외하고 컴파일되게 되어 충돌을 막을 수 있습니다.

하지만 저는 특수한 케이스에 부딪혔습니다!

기존 앱에 '파이어베이스'를 연동한 상태에서 '구글 플레이 게임' 을 연동하기 위해

BaseGameUtils를 모듈로 추가했었는데요

이때 zip exception com/google/android/gms/internal/z~~~.class 오류가 발생되어 고통을 받았었습니다.

이 해결법을 알려드리겠습니다!!

build.gradle(Module: BaseGameUtils) 에는 다음과 같이 선언되어있습니다.

if (!project.hasProperty('gms_library_version')) {
ext.gms_library_version = '8.4.0'
compile "${gms_library_version}"
compile "${gms_library_version}"

제 build.gradle(Module: app) 에는 파이어베이스가 다음과 같이 선언되어 있었습니다.

compile ''
compile ''

여기서 app의 파이어베이스에도 gms가 포함되어있었고, BaseGameUtils에도 gms가 포함되어 충돌이 일어났었는데요!

모듈이 달라 디버깅하기 쉽지 않았었습니다...

따라서 다음과 같이

compile ""
compile ""

build.gradle(Module: BaseGameUtils) 의 버전을 맞춰주시면 아름답게 컴파일 되는 것을 확인할 수 있습니다!

저와 같은 고통을 받지 않으셨으면 좋겠습니다.



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

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

이 글을 작성할 때 

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

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

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

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{version}&lat={lat}&lon={lon}&city={city}&county={county}&village={village}

Protocol - REST

HTTP Method - GET

여기서 은 baseURL

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

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

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

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

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

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

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

이와 같이

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

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

다음으로 Call입니다.

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

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

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

                "timeRelease":"2013-11-11 14:00:00"

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

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

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

public class WeatherRepo {

Result result;
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"})
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; = String. valueOf(lat);
this.lon = String .valueOf(lon);
this.handler = handler;

public void run() {;
Retrofit client = new Retrofit.Builder().baseUrl("").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>() {
public void onResponse(Call<WeatherRepo> call, Response<WeatherRepo> response) {
weatherRepo = response.body();
Log.d(TAG,"response.raw :"+response.raw());
if(weatherRepo.getResult().getCode().equals("9200")){ // 9200 = 성공

Message msg = Message.obtain();
Bundle bundle = new Bundle();
Log.e(TAG,"요청 실패 :"+weatherRepo.getResult().getCode());
Log.e(TAG,"메시지 :"+weatherRepo.getResult().getMessage());

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 구현 공부 중 올려주신 코드를 참고해 실행해봤는데
    이 다섯줄의 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