comnic's Dev&Life

[Golang] 9. 디버깅(debugging) 본문

Golang

[Golang] 9. 디버깅(debugging)

comnic 2023. 12. 31. 11:35
반응형

9. 디버깅(debugging)

디버깅(Debugging)은 소프트웨어에서 발생하는 버그(오류)를 찾아내고 수정하는 과정을 말합니다. 버그는 프로그램이 의도한 대로 동작하지 않거나 예상치 못한 동작을 나타냅니다. 디버깅은 프로그래머가 소스 코드를 검사하고 실행 중인 프로그램의 동작을 이해하는 데 도움이 되는 도구와 기술을 사용하여 이러한 문제를 해결하는 과정입니다.

 

9.1 디버깅 과정:

  1. 문제 파악:
    • 먼저, 프로그램이 어떤 문제를 일으키는지 파악해야 합니다. 사용자의 보고서, 로그 파일, 테스트 결과 등을 통해 문제의 원인을 추정할 수 있습니다.
  2. 코드 검토:
    • 소스 코드를 검토하여 문제가 발생할 수 있는 부분을 찾습니다. 이때 코드의 흐름, 변수 값, 함수 호출 등을 주의깊게 살펴봅니다.
  3. 디버깅 도구 활용:
    • 다양한 디버깅 도구를 활용하여 프로그램의 실행 중인 상태를 확인합니다.
    • 주요 디버깅 도구에는 변수 값을 확인하고 코드를 중단할 수 있는 디버거(debugger), 로그를 출력하는 도구, 성능을 측정하는 도구 등이 있습니다.
  4. 출력 및 기록:
    • fmt 패키지를 이용하여 중간 결과를 출력하거나 log 패키지를 사용하여 로그를 남기는 등의 방법을 통해 프로그램의 동작을 기록합니다.
  5. 문제 해결:
    • 발생한 문제의 원인을 파악한 후 수정하는 작업을 수행합니다. 이때는 코드를 수정하고, 수정된 코드를 테스트하여 문제가 해결되었는지 확인합니다.
  6. 단위 테스트:
    • 수정된 코드에 대한 단위 테스트를 작성하여 새로운 변경이 다른 부분에 영향을 미치지 않는지 확인합니다.
  7. 반복:
    • 위의 과정을 반복하여 여러 문제를 해결하고 코드를 점진적으로 개선합니다.

 

9.2 디버깅 도구:

  1. 디버거(Debugger):
    • 코드를 중단하고 변수 값을 검사하며 프로그램의 실행 흐름을 단계별로 살펴볼 수 있는 도구입니다. Go 언어에서는 내장된 delve 디버거가 제공됩니다.
  2. 프로파일링 도구:
    • 코드의 성능을 측정하고 병목 현상을 찾는 데 사용됩니다. Go 언어에서는 pprof 패키지를 활용한 프로파일링이 가능합니다.
  3. 로그 출력:
    • fmt 패키지를 이용하여 중간 결과를 출력하거나 log 패키지를 사용하여 로그를 남겨 디버깅 정보를 수집합니다.
  4. 테스트 도구:
    • 단위 테스트와 통합 테스트를 작성하고 실행하여 코드의 일부분이나 전체가 예상대로 동작하는지 확인합니다.
  5. 프로그램 상태 확인:
    • 프로그램이 실행 중일 때 상태를 확인할 수 있는 도구를 사용합니다. 이를 통해 변수 값, 메모리 상태 등을 실시간으로 살펴볼 수 있습니다.
  6. 커버리지 도구:
    • 테스트 코드가 얼마나 많은 코드를 커버하는지 확인하는 데 사용됩니다. go test 명령에 -cover 옵션을 사용하여 커버리지를 확인할 수 있습니다.

디버깅은 프로그래머에게 중요한 역할을 하며, 문제 해결 능력과 소프트웨어 품질을 높이는 데 도움을 줍니다. 효과적인 디버깅은 경험과 도구의 숙련도, 디버깅 기법에 대한 이해를 통해 향상됩니다.

 

9.3 Go 언어에서의 기본적인 디버깅 도구

- fmt 패키지를 이용한 출력 디버깅:

  • fmt.Printf, fmt.Println 등의 함수를 사용하여 코드 중간 결과를 출력하고 디버깅할 수 있습니다.
package main

import "fmt"

func main() {
    x := 42
    fmt.Printf("Value of x: %d\n", x)
}
Value of x: 42

 

- log 패키지를 이용한 로그 디버깅:

  • log 패키지를 사용하여 로그를 출력하고 디버깅할 수 있습니다.
package main

import "log"

func main() {
    x := 42
    log.Printf("Value of x: %d\n", x)
}
2023/12/31 11:04:16 Value of x: 42

 

- panic 함수를 이용한 강제 종료:

  • panic 함수를 사용하여 예기치 않은 상황에서 프로그램을 강제로 종료하고 디버깅 정보를 출력할 수 있습니다.
package main

func main() {
    x := 0
    if x == 0 {
        panic("x should not be zero")
    }
}
panic: x should not be zero

goroutine 1 [running]:
main.main()
        /Users/comnic/Documents/workspace/golang/debug_test.go:6 +0x27
exit status 2

 

- debug 패키지를 이용한 디버깅:

  • debug 패키지를 사용하여 프로그램의 상태를 출력하고 디버깅할 수 있습니다.
package main

import "runtime/debug"

func main() {
    debug.PrintStack()
}
goroutine 1 [running]:
runtime/debug.Stack()
        /usr/local/Cellar/go/1.18.1/libexec/src/runtime/debug/stack.go:24 +0x65
runtime/debug.PrintStack()
        /usr/local/Cellar/go/1.18.1/libexec/src/runtime/debug/stack.go:16 +0x19
main.main()
        /Users/comnic/Documents/workspace/golang/debug_test.go:6 +0x17

 

9.4 코드 품질을 높이는 디버깅 기법

코드 품질을 높이는 디버깅 기법:

1. 단위 테스트 작성:

  • 코드의 각 기능에 대한 단위 테스트를 작성하여 예상치 못한 동작을 방지하고 디버깅을 용이하게 합니다.

2. 로그 활용:

  • fmt 패키지나 log 패키지를 적절히 활용하여 중요한 지점에서 로그를 남겨 디버깅을 수행합니다.

3. panic 함수 활용:

  • 예상치 못한 상황이 발생할 때 panic 함수를 사용하여 프로그램을 중단하고 디버깅 정보를 출력합니다.

4. 문제 발생 시 스택 트레이스 확인:

  • 문제가 발생한 곳에서 스택 트레이스를 출력하여 어떤 경로를 따라 코드가 실행되었는지 확인합니다.
package main

import (
    "log"
    "runtime/debug"
)

func main() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered from panic: %v", r)
            debug.PrintStack()
        }
    }()

    // 프로그램 로직
    panic("Unexpected situation")
}

 

5. go vet 명령 사용:

  • [go vet] 명령을 사용하여 코드의 정적 오류를 검사하고 개선할 수 있습니다.

6. go fmt 명령 사용:

  • [go fmt] 명령을 사용하여 코드 스타일을 일관되게 유지하고 가독성을 향상시킵니다.

 

디버깅을 효과적으로 활용하여 코드의 품질을 높이고, 버그를 신속하게 찾아내고 수정함으로써 안정적이고 효율적인 개발을 할 수 있습니다.

반응형
Comments