2014년 6월 28일 토요일

안드로이드 앱, 태블릿 PC에서만 인터넷 체크 구문 익셉션 발생

첫 앱을 론칭하고 10일만에 별다른 홍보 없이 130만 페이지뷰를 달성하면서 순항을 하고 있었습니다. 그러던 중 애널리틱스 로그와 플레이마켓 개발자 콘솔에서 익셉션 접수가 밀려 들어오기 시작했습니다. 론칭 초기에 하루 66건의 익셉션이 모두 태블릿 PC에서만 발생했습니다.

66건의 익셉션 모두 앱을 시작할 때 onstart() 메소드안에 구현해 놓은 인터넷 접속 여부를 체크하는 구문에서 발생했습니다. 익셉션이 다발로 터지고 있던 구문은 다음과 같습니다.

cManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo mobile;
NetworkInfo wifi;

mobile = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
wifi = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

if (mobile.isConnected() || wifi.isConnected()) {
  Toast.makeText(MainActivity.this, "환영!", Toast.LENGTH_LONG).show();
} else {
  Toast.makeText(MainActivity.this, "네트워크확인", Toast.LENGTH_LONG).show();
}

이 구문을 이틀넘게 요리조리 뜯어봤습니다. 아무리 봐도 이상한 부분은 없었습니다. 와이파이나 LTE, 3G 같은 데이터 통신을 할 수 있는 상태면 '환영!'이라는 토스트 메시지를 출력하면 되고 인터넷을 쓸 수 없는 상황이면 '네트워크확인'이라는 토스트를 띄워주면 그만이었죠. 아무리 생각해도 이 구문에서 익셉션이 발생할리가 없어 보였습니다.

태블릿, 그 중에서도 갤럭시 TAB시리즈에서 압도적으로 문제가 생기고 있었고 TAB 유저를 중심으로 태블릿 이용자들 대부분이 아예 제앱을 쓰지 못하고 있었습니다. 시간이 갈수록 고객 누수가 생긴다는 불안감이 커져갔습니다.

여기저기 다니면서 갤럭시TAB이라는 탭은 모조리 테스트를 다 해봤는데 제가 테스트 했던 갤럭시TAB에서는 제 앱이 잘 작동했습니다.

그리고 또 이상한 점이 있었습니다. 전체 방문 비율에서 매우 적은 비율을 차지 하기는 하지만 제 앱을 잘 사용하는 분들 중 태블릿 유저도 있기는 있었습니다. 개중에는 갤럭시TAB 유저도 몇몇 있었구요.

이렇게 되다보니 익셉션이 미궁으로 빠지게 되었습니다. 분명히 태블릿PC 유저들 중심으로 인터넷 접속 체크를 하는 구문에서 대량으로 익셉션이 생겨서 제 앱을 쓰지도 못하고 튕겨나가고 있는데 속은 타들어 갔습니다.

사흘 정도 지나자 유저들은 더 이상 참아주지 않았습니다. 평점 4.5 이상을 유지하고 있던 별점이 조금씩 깎여나가기 시작했습니다. 미국, 스페인, 한국 등지의 유저들이 '왜 태블릿에서 앱이 동작 안하냐?'며 리뷰란에 문의를 하기 시작했고, CS메일 계정으로도 문의 메일이 들어오기 시작했습니다.

주식 투자를 하면서 취미로 시작한 앱 개발이라고는 하지만 솔직히 조금씩 스트레스를 받기 시작했습니다.

그러던 어느날 문득, 문제 해결의 실마리가 될만한 아이디어가 지나갔습니다.

태블릿 PC를 구매할 때, 통신 요금제 적용을 받지 않는 '와이파이 전용' 태블릿 PC가 있다는 사실이 떠올랐습니다. 그렇다면 와이파이 전용 태블릿 PC에서는 'ConnectivityManager'나 'ConnectivityManager.TYPE_MOBILE' 중 하나가 null 값을 리턴 할수도 있겠다는 생각이 들었습니다. 이 가설을 가지고 스택오버플로우에 검색을 해보니 아니나 다를까 이미 이 문제로 앱이 crush 돼 문의를 올린 사례가 많았습니다.

스택오버플로우는 위대합니다. 제가 겪고 있는 문제는 이미 누군가가 다 겪었던 문제로군요.

어쨌든 와이파이 전용 태블릿 PC를 위해서 코드를 몇 줄 더 추가해서 인터넷 접속 여부를 확인하는 구문은 아래와 같이 방어 코딩하여 수정하였습니다.
ConnectivityManager cManager = null;
cManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

// ConnectivityManager가 null일 경우를 위한 방어 코딩
if (cManager != null) {
  
  NetworkInfo mobile;
  NetworkInfo wifi;
   
  mobile = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
  wifi = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

  // ConnectivityManager.TYPE_MOBILE이 리턴되지 않는 경우를 대비한 방어 코드 추가
  if (mobile != null && mobile.isConnected() || wifi.isConnected()) {
    Toast.makeText(MainActivity.this, "환영!", Toast.LENGTH_LONG).show();
  } else {
    Toast.makeText(MainActivity.this, "네트워크확인", Toast.LENGTH_LONG).show();
  }

} else {
  return;
}

이렇게 컨넥티비티매니저와 TYPE_MOBILE이 null로 리턴되는 경우를 위해 방어 코딩을 추가하니 태블릿 PC 에서 앱을 시작할 때 발생하던 익셉션이 하나도 발생하지 않게 되었습니다.

문제를 해결하고나니 허탈한 기분까지 들었습니다. 안드로이드 개발의 경우에는 기기도 다양하고 특수 상황도 많아서 정말 다양한 환경에 대응을 해야하는데 이번 경우는 정말 문제 원인을 알아내는 것도 힘들었고 수수께끼를 푸는 기분이었습니다.

이번일로 어쨌든 와이파이 전용 태블릿 PC에서는 인터넷 접속 체크 구문을 위와 같이 방어 코딩을 해야 한다는 점을 숙지하게 되었습니다. 사흘간 수백명의 유저를 잃고 얻은 경험입니다.

2014년 6월 28일
송종식 드림