Kotlin android (Debugging 디버깅)

subak96·2023년 9월 13일
post-thumbnail

Kotiln 포함한 모든 언어 및 코딩을 하다보면 마지막의 마지막에는 크나큰 문제에 부딪히게 된다.

그렇다 바로 버그 혹은 에러라고 부른다. 오늘은 버그를 잡는 행위, 디버깅에 대해 알아보고자 한다.

디버깅의 중요성

  • 앱 개발 시간 단축
    • 초기 단계의 에러 발견으로 이후 시간 절약
    • 코드 흐름과 동작 방식 파악으로 유사 오류 빠르게 대응
    • 복잡한 문제를 단계별로 분해하여 효과적 관리
  • 품질 향상 및 사용자 경험 개선
    • 앱 안정성 증대로 사용자 신뢰 향상
    • 오류 감소로 사용자 경험 개선
    • 문제 원인 미리 파악으로 유지보수 용이성 향상
  • 개발자의 성장
    • 다양한 문제 대처 능력 향상
    • 프로젝트 흐름과 구조 파악으로 관리 능력 증진

등 디버깅은 중요하면서도 꼭 해야만한다. 안하면 어차피 코드가 돌아가지도 않을테지만..

에러의 종류와 다응 방법

  • Syntax Error (구문 에러)
    • 정의: 코드의 문법적 오류
    • 대응: 코드 문법 확인 및 편집기의 오류 표시 참조
    • 자주 발생하는 구문 에러 예시
      1. 변수 타입 선언:
        Kotlin은 변수의 타입을 자동으로 추론할 수 있기 때문에, 타입 선언을 생략할 수 있습니다. 하지만, 자료형을 잘못 지정하거나 필요한 경우에 지정하지 않으면 에러가 발생합니다.

        val age: String = 25  // Int 값을 String으로 선언하려고 시도
      2. Null Safety:
        Kotlin은 null 안전성을 중요시합니다. 따라서 null 값을 허용하지 않는 변수에 null을 할당하려고 하면 에러가 발생합니다.

        var name: String = null  // null 값을 허용하지 않는 String 변수에 null 할당
        

        해결: var name: String? = null

      3. 함수 선언:
        Java와는 다르게 Kotlin에서 함수를 선언할 때 fun 키워드를 사용합니다. 이를 잊으면 에러가 발생합니다.

        void display() {  // 'fun' 키워드 누락
            println("Displaying...")
        }

        해결: fun display() { ... }

      4. 클래스 상속:
        Kotlin에서 클래스는 기본적으로 final입니다. 따라서 클래스를 상속하려면 open 키워드를 추가해야 합니다.

        class Parent { ... }
        class Child : Parent()  // Parent 클래스가 open으로 표시되지 않았기 때문에 에러 발생
        

        해결: open class Parent { ... }

      5. 프로퍼티 초기화:
        Kotlin에서는 프로퍼티(변수)를 선언할 때 초기값을 제공하거나, 나중에 초기화하겠다는 것을 명시적으로 표시해야 합니다.

        class User {
            var name: String  // 초기값이 없거나 lateinit 키워드 누락
        }
        

        해결: 초기값 제공 var name: String = "Default" 또는 lateinit var name: String

  • Logical Error (논리 에러)
    • 정의: 코드는 실행되지만 원하는 결과를 얻지 못할 때 발생
    • 대응: 로직 검토, 테스트 케이스 사용하여 오류 시나리오 확인
    • 자주 발생하는 논리 에러 예시
      1. 잘못된 범위 사용:
        until..의 차이를 헷갈려 잘못 사용하면 범위가 의도치 않게 설정될 수 있습니다.

        for (i in 0 until 5) {
            println(i)  // 0, 1, 2, 3, 4 출력
        }
        
        for (i in 0..5) {
            println(i)  // 0, 1, 2, 3, 4, 5 출력
        }
      • Runtime Error (런타임 에러)
    • 정의: 프로그램 실행 중에 발생하는 오류
    • 대응: Logcat을 통한 오류 메시지 분석 및 해당 코드 섹션 검토
    • 자주 발생하는 런타임 에러 예시
      1. NullPointerException:
        Null 참조를 사용하려고 할 때 발생합니다.

        var name: String? = null
        println(name!!.length)  // !! 연산자 사용으로 강제 참조시도 시 NullPointerException 발생
      2. IndexOutOfBoundsException:
        잘못된 인덱스로 리스트나 배열에 접근하려고 할 때 발생합니다.

        val numbers = listOf(1, 2, 3)
        println(numbers[5])  // 인덱스 범위 초과
      3. ClassCastException:
        잘못된 타입으로 객체를 변환하려고 할 때 발생합니다.

        val obj: Any = 12345
        val str: String = obj as String  // Int 객체를 String으로 캐스팅하려고 시도
      4. ArithmeticException:
        수학적 계산 중에 예외 상황이 발생할 때, 예를 들면 0으로 나누려고 할 때 발생합니다.

        val result = 5 / 0  // 0으로 나눔
      5. NoSuchElementException:
        예상한 요소가 컬렉션에 없을 때 발생합니다.

        val numbers = listOf(1, 2, 3)
        println(numbers.first { it > 5 })  // 5보다 큰 첫 번째 요소가 없음
        
      6. IllegalStateException:
        객체의 현재 상태가 작업을 수행하기에 적합하지 않을 때 발생합니다.

        val builder = StringBuilder()
        val result = builder[2]  // 비어 있는 builder에서 인덱스 2의 문자를 가져오려고 함
        
      7. Resources.NotFoundException:
        안드로이드에서 특정 리소스(ID, 이미지 등)를 찾을 수 없을 때 발생합니다.

        val color = resources.getColor(R.color.non_existent_color)  // 존재하지 않는 리소스 ID를 사용
  • 구글 검색 방법
    • 키워드 선택의 중요성: 오류 메시지와 관련된 키워드 사용
    • 검색 팁: 에러 코드, 라이브러리 이름, 상황 설명 등으로 검색

이처럼 에러의 종류는 다양하다. 해결방법은 보면 알겠지만 대부분 한 번 씩은 실수하는 사항들이다.

잘못 썻거나 빼먹었거나, 초기화 하지 않았거나 하는것들은 나름 빠른 시간안에 잡을 수 있다. 하지만 그 외의 상황이 발상된다면 더 많은 시간과 갈라나가는 자신을 볼 수밖에 없을때가 있다.

그떄가 바로 코드가 돌아가서 테스트 중인데 어디선가 에러가 발생되어 멈춘다던가 팅기는 현상이 일어날때이다.

이럴때는 Logcat 을 사용해서 어디서 에러가나는지 찾아야한다. 안드로이드 스튜디오를 기준으로 설명하자면

하단에 빨간색 처진 부분에 Logcat이라고 써진 부분을 클릭하면

이러한 화면이 나오는걸 볼 수 있다. 보는 봐야같이 빨간색 글씨로 E라고 써잇는 부분이 에러가 뜬 부분이다.

대체로 뭐때문에 에러가 났는지 알려주기때문에 바로 알아볼 수있지만 그마저도 안될때는 다음과 같은 코드를 에러가 날것같은 혹은 난거 같은 코드 사이에 껴 넣어줘야한다.

	Log.v("", "Verbose level log")
	Log.d("", "Debug level log")
	Log.i("", "Information level log")
	Log.w("", "Warning level log")
	Log.e("", "Error level log")
    

앞부분에는 tag를 입력 뒤에는 Logcat에서 알아 볼 수 있는 글씨를 써 넣어주면 된다.

tag는 로그캣 상단 부분에 필터 역활을 해준다.

예를 들어 tag부분에 Log를 적고 뒤 msg 부분에 this here error 를 적고 실행한다면 다음과 같은 화면이 나온다.

코드 형식은 이렇다.

	 Log.d("tag", "this here error")

하나씩 설명하자면 1번에는 본인이 사용중인것 혹은 저장해둔 에뮬레이터를 선택 할 수있다. 만일 Logcat에서 로그가 안나온다면 저부분에 에뮬레이터가 잘 못 선택 된건 아닌지 먼저 보도록하자.

2번에 보면 package:mine tag 이라고 적힌걸 볼 수 있다. tag 라는 필터를 줘서 tag라고 적힌 부분만 골라서 보여주는게 가능하다. 본인에게 알맞게 작성해서 로그를 볼 수 있게 하면 된다.

3번을 또 보면 제대로 로그가 나오는걸 볼 수 있다. Log.d("tag", "this here error") 라고 적은대로 tag가 나오고 마지막에 this here error라고 잘 출력된걸 볼 수있다.

이걸 써서 에러가 날것 같은 부분 혹은 에러가 날것으로 추정되는 부분에 넣는다면 쉽게 에러가 난 지점을 찾을 수 있다. 추가로 만일 데이터를 넘겨받고 한다면 데이터를 넘겨받고 하는 코드 부분에 로그를 넣을 경우 데이터가 제대로 넘어가는지 안넘어 가는지 알 수 있을 것이다.

상황별 Logcat 해석 및 사용 예제

1. Null Pointer Exception

  • Logcat 메시지 예제
    csharpCopy code
    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.myapp, PID: 12345
        java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
            at com.example.myapp.MainActivity.someFunction(MainActivity.java:50)
    
  • 해석
    • java.lang.NullPointerException은 널 객체 참조에 메서드를 호출하려고 했을 때 발생합니다.
    • MainActivity.java:50에서 이 오류가 발생했음을 알 수 있습니다.

2. Out of Memory Error

  • Logcat 메시지 예제
    vbnetCopy code
    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.myapp, PID: 12345
        java.lang.OutOfMemoryError: Failed to allocate a 1234568 byte allocation with 123456 free bytes and 123KB until OOM
            at com.example.myapp.MainActivity.loadLargeImage(MainActivity.java:100)
    
  • 해석
    • java.lang.OutOfMemoryError은 메모리 부족으로 인해 발생합니다.
    • MainActivity.java:100에서 대용량 이미지를 로드하려고 했을 때 메모리 부족 문제가 발생했음을 알 수 있습니다.

3. Resource Not Found Exception

  • Logcat 메시지 예제
    yamlCopy code
    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.myapp, PID: 12345
        android.content.res.Resources$NotFoundException: Resource ID #0x7f0800e3
            at com.example.myapp.MainActivity.onCreate(MainActivity.java:30)
    
  • 해석
    • android.content.res.Resources$NotFoundException은 참조한 리소스(ID #0x7f0800e3)를 찾을 수 없을 때 발생합니다.
    • MainActivity.java:30에서 존재하지 않는 리소스를 참조하려고 했음을 알 수 있습니다

흔히겪는 문제와 해결법

  1. 앱 갑자기 종료
    • 원인: 대부분 NullPointerException 또는 리소스 참조 오류 때문입니다.
    • 해결: Logcat을 확인하여 오류 메시지와 스택 트레이스를 분석하세요.
  2. UI 업데이트가 되지 않음
    • 원인: 메인 스레드 외부에서 UI 업데이트를 시도할 경우 발생합니다.
    • 해결: runOnUiThread 또는 Handler를 사용하여 UI 업데이트를 메인 스레드에서 실행하세요.
  3. 메모리 누수
    • 원인: 오랜 시간 동안 불필요하게 객체가 메모리에 남아 있을 때 발생합니다.
    • 해결: LeakCanary와 같은 도구를 사용하여 메모리 누수를 감지하고 해결하세요.
  4. 앱의 반응 속도 저하
    • 원인: 메모리 부족, 네트워크 지연, 비효율적인 코드 등이 원인이 될 수 있습니다.
    • 해결: Android Profiler를 사용하여 앱의 성능 문제를 분석하고 최적화하세요.

이상 디버깅에 관한 글이였다.

0개의 댓글