프로젝트/Android Project) 가장 가까운 주유소 찾기 app

가장 가까운 주유소 찾기 (4차 업데이트) 내용 (2)

roder 2023. 9. 21. 17:18

https://github.com/leeugun123/find_gas_station

 

GitHub - leeugun123/find_gas_station: find_gas

find_gas. Contribute to leeugun123/find_gas_station development by creating an account on GitHub.

github.com

 

계속해서 업데이트 내용을 소개하겠다.

 

3. 정렬 기준 추가

opinet 주유소 사이트의 공공 API를 이용하여 만든 어플들은 시중에 정말 많았다.

따라서 시중에 나온 어플들과 차이점을 두기 위해 정렬 기준을 추가하여 사용자의 편의성을 높이는 방법을 찾고 싶었다.

 

3-1 . 소요시간 , 도로거리  추가

 

Kakao Developer 사이트에서 다중 목적지의 소요시간과 도로 거리를 알려주는 API를 찾게 되었다.

생각해보면 기존에 있었던 직경 거리 같은 경우, 직경 거리가 짧아도 실제로 가는 도로 거리가 길다면 돌아가는 경우가 있기 때문에 직경거리라는 기준은 적절하지 않았다.

 

하지만 도로 거리와 소요시간이라는 기준은 가까운 주유소를 찾는 명확한 기준이기 때문에  기존에 있던 직경 거리보다 훨씬 효과적으로 

사용자들에게 편리한 정렬 옵션을 제공해준다.

 

https://developers.kakaomobility.com/docs/navi-api/destinations/

 

카카오모빌리티 디벨로퍼스

카카오모빌리티 디벨로퍼스

developers.kakaomobility.com

 

API를 소개하자면 다중 목적지 길찾기 API는 request로 보내는 목적지(x,y 좌표 , 목적지를 식별할 수 있는 key)객체로 하는 배열을

매개변수로 삼고 각 목적지의 요소의 소요시간과 도로거리를 응답해주는 API이다.

 

3-2 . 카카오 API 호출 

 

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public void getOilDetail(String sort, int size, RecyclerView mRecyclerView,
                             GoogleMap mMap, ProgressBar progressBar , String uid, String name,
                             String gas_price, String distance, String inputOil,
                             int imageResource, float DestinationX, float DestinationY){
 
 
        opinet_retrofitApi.getOilDetail(opinet_apiKey,"json",uid)
                .enqueue(new Callback<GasStationInfo>() {
 
                        @Override
                        public void onResponse(Call<GasStationInfo> call, Response<GasStationInfo> response) {
 
                            if(response.isSuccessful()){
 
                                GasStationInfo gasDetail = response.body();
                                GasStationInfo.Result result = gasDetail.getRESULT();
 
                                //세차장, 편의점 정보
                                String carWash = result.getOIL().get(0).getCAR_WASH_YN();
                                String conStore = result.getOIL().get(0).getCVS_YN();
 
                                //상세 정보 받아오기
                                String lotNumberAddress = result.getOIL().get(0).getVAN_ADR();
                                String roadAddress = result.getOIL().get(0).getNEW_ADR();
 
                                String tel = result.getOIL().get(0).getTEL();
                                String sector = result.getOIL().get(0).getLPG_YN();
 
                                int dis = (int)Double.parseDouble(distance);
                                //소수점 짜르기
 
                                moil_list.add(new OilList(uid, name, gas_price, Integer.toString(dis),
                                        inputOil,imageResource, DestinationX, DestinationY,carWash,conStore,lotNumberAddress,roadAddress,
                                        tel,sector,"",""));
 
                                if(moil_list.size() == size || moil_list.size() == 30){
 
                                    if(sort.equals("3"|| sort.equals("4")){
 
                                        getOilKakaoApi(progressBar ,mMap, mRecyclerView,sort);
 
                                        return;
 
                                    }//추가적인 카카오 api를 요구하는 경우
 
 
                                    if(sort.equals("1")){
                                        Collections.sort(moil_list,new OilPriceComparator());
                                    }//가격순
                                    else if(sort.equals("2")){
                                        Collections.sort(moil_list,new OilDistanceComparator());
                                    }//직경 거리순
 
                                    //불필요한 정렬 생성
 
                                    progressBar.setVisibility(View.GONE);
                                    myRecyclerAdapter = new MyRecyclerAdapter(moil_list,mMap,sort);
                                    mRecyclerView.setAdapter(myRecyclerAdapter);
                                    myRecyclerAdapter.notifyDataSetChanged();
 
                                }//데이터가 모두 도착 하면 실행
 
                            }
 
 
                        }
 
                        @Override
                        public void onFailure(Call<GasStationInfo> call, Throwable t) {
 
 
                        }
 
 
                });
 
 
    }
cs

 

코드를 보면 기존 코드는 매개변수로 오는 sort 변수가 1,2 밖에 없었지만 정렬 기준을 추가적으로 도입하였기 때문에 3,4가 추가 되었다.

따라서 sort가 3,4인 경우, getOilKakaoApi 메소드를 추가적으로 호출함으로서 kakao에서 제공하는 api를 요청 할 수 있게 하였다.

 

코드를 자세히 보면 주유소 개수를 30개 이하로 설정하였음을 볼 수 있는데 그 이유는 Kakao Developer 사이트에서 목적지 개수를

30개 이하밖에 response하지 않기 때문이다. (30개를 초과 하는 경우 따로 제휴 신청이 필요함)

 

그 이유도 모르고 일주일동안 API request를 보내도 왜 안되는지 도무지 이유를 몰라 온갖 삽질을 다 하였다.

 

API key가 잘못된줄 알고 key도 변경하고 , 매개변수 형식이 잘못된줄 알고 자료형도 바꾸는 등 수많은 요소를 하나씩 체크하고 매일매일 API를 요청하여 테스트하였지만 도무지 response가 오지 않아 뭐가 잘못됐는지 몰랐다.

 

API 문서를 자세히 읽어보니 요청 개수가 30개 이하인 것을 고려하지 못하고 코드를 작성하니 response가 올 턱이 있나...

 

이 경험을 통해 API 문서를 자세히 읽는 것이 정말 중요하다는 것을 깨닫게 되었다.

만약 API 문서를 꼼꼼히 읽지 않으면  그저 코드가 문제인 줄 알고 일주일동안 삽질을 해버리는 결과가 초래된다.

 

또한 지금까지는 HTTP 호출 메소드인 GET 사용하였다면, 다중 목적지 길찾기 API를 이용하면서 GET과 POST의 차이점에 대해 알게 되었다. POST가 그저 데이터를 생성하는 메소드인줄 알았는데, 요청변수를 url에 담는 GET과 달리 body에 담아 서버에 보낸다는 점에서

데이터가 많아지고 보안성을 유지해준다는 장점을 가지고 있다는 점도 새롭게 알게 되었다.

 

 

3-3 . 연쇄적인 API 호출 

 

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public void getOilKakaoApi(ProgressBar progressBar, GoogleMap mMap, RecyclerView mRecyclerView, String sort){
 
 
        Destination[] destinations = new Destination[moil_list.size()];
 
        for(int i=0; i<moil_list.size(); i++){
            destinations[i] = new Destination(moil_list.get(i).getUid(), moil_list.get(i).getWgs84X(), moil_list.get(i).getWgs84Y());
        }
 
        kakao_retrofitApi.getMultiDirections(new DirectionRequest(new Origin(Double.parseDouble(getWgsMyX) , Double.parseDouble(getWgsMyY)),
                        destinations,10000))
 
                .enqueue(new Callback<DirectionResponse>() {
                    @Override
                    public void onResponse(Call<DirectionResponse> call, Response<DirectionResponse> response) {
 
                        if(response.isSuccessful()){
 
                            DirectionResponse multiRouteResponse = response.body();
 
                            Route[] routes = multiRouteResponse.getRoutes();
 
                            for(int i=0; i < moil_list.size(); i++){
 
                                OilList oilList = moil_list.get(i);
 
                                String distance = Integer.toString(routes[i].getSummary().getDistance());
                                String spendTime = Integer.toString(routes[i].getSummary().getDuration());
 
                                oilList.setActDistance(distance);
                                oilList.setSpendTime(spendTime);
 
                                plusOilList.add(oilList);
 
                            }
 
                            if(sort.equals("4")){
                                Collections.sort(plusOilList , new OilSpendTimeComparator());
                            }else
                                Collections.sort(plusOilList , new OilRoadDistanceComparator());
 
                            progressBar.setVisibility(View.GONE);
                            myRecyclerAdapter = new MyRecyclerAdapter(plusOilList,mMap,sort);
                            mRecyclerView.setAdapter(myRecyclerAdapter);
                            myRecyclerAdapter.notifyDataSetChanged();
 
 
                        }
 
 
 
                    }
 
                    @Override
                    public void onFailure(Call<DirectionResponse> call, Throwable t) {
 
                    }
 
 
 
                });
 
 
    }
    //카카오 api 이용하여 추가정보(실제 거리 , 소요시간)를 가져옴.
cs

 

연쇄적인 API호출의 단점은 시간이 너무 오래 걸린다는 점이다.

 

위에 있는 코드를 보면 각 주유소의 상세정보를 알기 위해 getOilDetail 메소드로 요청보내고 응답받는 과정을 볼 수 있다. 이를 동기적으로

처리 할 경우, n개의 주유소를 보내고 받고 보내고 받고 등 이 과정을 반복함으로서 시간이 지연되어 사용자가 정보를 받기 위해 기다리다가 어플을 지워버리는 불상사가 발생하게 된다. 따라서 이를 비동기적을 처리하여 속도를 높여야했다.

 

따라서 이전 응답이 오기전에 바로 다음 요청을 보내는 비동기적인 방식으로 요청을 보내야 한다. 이에 따라 속도를 높여 사용자의 인내심이

0이 되기 전에 모든 데이터 처리가 완료되어야 한다.

 

하지만 비동기적인 방식의 문제점은 무엇일까??

바로 순차적인 요청 순서와 달리 응답 받는 순서가 비순차적이라는 것이다.

 

즉 이미 정렬되어 받아온 데이터를 getOilDetail를 비동기적으로 호출함으로서 이전에 응답받아온 데이터의 순서가 다시 바뀌게 된다.

 

이를 해결하기 위해 기존 순서를 다시 유지시키는 방법을 고민하였다.

 

그 방법은 if(요청한 데이터의 개수 == 응답 받은 개수) 조건을 응답받을때마다 체크하여

조건에 부합하다면 Collections.sort()를 이용해

sort마다 이전에 정의한 Comparator 인터페이스를 implement한 class를 적용하여 리스트를 정렬해주었다.

 

물론 정렬하는 오버헤드가 들긴 하지만 상세정보까지 얻기 위한 시간 지연을 비동기적인 순서로 해결하여 속도 저하를 해결 할 수 있다는

장점이 있다.

 

하지만 여기에서 멈추지 않고 정렬을 하지 않고도 데이터의 순서를 유지 할 수 있는 방법을 연구해야 한다...