생산성

디버깅

개요

 버그(Bug)란 프로그램이 개발자가 의도하지 않은 방향으로 실행된다거나 갑자기 오류가 발생해 실행이 중단되는 등 프로그램의 임무를 정상적으로 실행하지 못하는 오작동을 의미합니다. 디버그(Debug)란 버그를 잡는다는 의미로 개발자가 의도하지 않은 결과나 오류를 해결하는 작업을 뜻합니다. PyCharm은 디버깅(Debugging) 작업을 쉽게 할 수 있도록 고급 디버깅 기능들을 제공합니다. 이러한 디버깅 기능의 활용 정도에 따라 개발 단계 중후반에서 각종 버그를 잡고 고치는 데에 상당한 시간 비용 차이를 만들어 냅니다.

중단점(Breaking Point)

 중단점은 프로그램의 실행을 잠시 중단하는 기능이며, 특정 지점에서 프로그램의 실행을 잠시 중단하여 변수의 값을 확인한다던지 각종 오류를 잡기 위해 자주 사용합니다.

 다음은 현재 날짜와 시간을 출력하기 위한 View 함수입니다.

from django.shortcuts import render


# Create your views here.

def index(request):
    from datetime import datetime
    now = datetime.now()
    now = now.strftime('%Y년 %m월 %d일'.encode('unicode-escape').decode()).encode().decode('unicode-escape')
    now = datetime.now().strftime(' %H시 %M분 %S초 %f'.encode('unicode-escape').decode()).encode().decode('unicode-escape')
    now += datetime.now().strftime(' %p')
    return render(request, 'www/index.html', {'time_now': now})

 아래는 출력될 index.html입니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
현재 시간: {{ time_now }}
</body>
</html>

 우리는 시간 정보를 render 함수의 인수(time_now)를 통해 전달하려 합니다. 아래 이미지처럼 날짜와 시간이 나오도록 하고 싶습니다.

 하지만 무언가 잘못된 건지 아래 이미지처럼 출력됩니다. 시간정보는 나오지만 날짜 정보가 안보입니다.

 일단, 출력은 정상적으로 되긴 하므로 index 함수의 로직이 잘못된 것 같습니다. 우리는 주요 로직이 시작하는 8번 줄에서 중단점을 걸어 그 이후에 now 변수의 값을 확인하려 합니다. 또한 render 함수가 최종적인 now 함수를 전달받는 순간 중단점을 걸어 그 값을 확인하려 합니다.

 특정 줄에 중단점을 거는 방법은 저 빨간 점이 있는 위치에 마우스 클릭을 한 번 하면 됩니다. 또는 특정 줄에 커서를 두고 Ctrl F8 키를 사용할 수도 있습니다. 중단점 해제도 똑같이 하면 됩니다.

 이제 디버깅 모드로 프로젝트를 실행해봅니다. Shift F9를 누릅니다. Shift F10은 디버그 모드가 아니기 때문에 중단점을 걸어도 프로그램의 실행이 멈추지 않지만 디버깅 모드로 실행하면 중단점이 걸립니다.

 다음과 같이 PyCharm이 디버깅 모드가 되어 Debug 탭이 열립니다. 또한 8번 줄에서 프로그램의 실행이 멈춘 상태로 대기 중입니다. 여기서 헷갈리지 말아야하는 부분이 있는데 8번 줄은 아직 실행되지 않은 상태입니다. 중단점은 프로그램의 실행을 중단하긴 하지만 해당 줄을 실행하지 않은 채로 즉, 실행하기 전인 상태로 멈춥니다. 그래서 아직 now 변수가 생성되지도, datetime.now() 함수가 호출되지도 않은 상태입니다.

 이 상태에서 F9 키를 누르면 중단된 상태가 풀리고 프로그램의 실행이 재개됩니다. 그러나 마지막 return 줄에 다시 중단점을 만나므로 다시 중단됩니다.

 이 시점에서는 now 변수가 이미 할당되어 값을 가지고 있는 상태입니다. 마우스 포인터를 now 변수 위에 놓으면 현재 중단된 시점에서 now 변수가 가지고 있는 값이 보입니다.

 왼쪽 {str}은 now 변수의 데이터 타입이며, 오른쪽은 그 값입니다. 또 PyCharm의 Debug 탭을 보면 Variables 영역에 현재 할당되어 있는 변수 목록이 뜹니다.

 다시 F9를 눌러 프로그램 실행을 재개해보면 이제서야 웹 브라우저가 페이지 탐색을 마치고 결과를 내놓았습니다. 이번엔 코드를 한 줄씩 실행시켜 보겠습니다. 웹 브라우저를 새로고침하면 다시 django 앱에 요청이 들어오므로 중단점이 다시 걸립니다.

 8번 줄에서 프로그램의 실행이 중단되면 F8 키를 누릅니다. F8 키는 중단된 지점에서 코드를 한 줄씩 실행합니다. 어라? 보다시피 날짜와 시간이 모두 잘 나옵니다. 그렇단 이야기는 8번 줄까지는 논리적인 버그가 없다는 이야기입니다. 계속 살펴보겠습니다. 다시 F8을 누릅니다.

 갑자기 날짜가 한글로 바뀌고 시간 정보가 날라갔네요. 시간 정보는 보다시피 10번 줄에서 추가될 예정인 것 같습니다. 다시 코드를 한 단계 실행해 봅니다.

 보다시피 날짜 데이터가 날라갔습니다. 우리가 원하는 결과가 아닙니다.

 now 변수의 데이터 타입은 str이네요. 문자열입니다. 문자열과 문자열을 연결하는 연산자인 +을 빼먹고 now에 그냥 대입(=) 해버리니 날짜 정보가 날아가죠. 10번 줄에서 문제를 발견했습니다. now = datetime 대신 now += datetime으로 코드를 수정합니다. 다시 웹 페이지를 새로고침 해봅니다. 문제가 해결되었다면 걸어두었던 중단점을 모두 해제합니다.

표현식 평가(Evaluate Expression)

 로직을 작성하면서 가끔 특정 지점에서 변수의 값을 확인해보고 싶을 때가 있습니다. 예를 들어, 위의 완성된 예제를 보면, 11번 줄에서 지금까지 완성된 문자열이 제대로 조합이 됬는지 확인하고 싶어서 다음과 같이 코드를 출력할 수 있습니다.
from django.shortcuts import render


# Create your views here.

def index(request):
    from datetime import datetime
    now = datetime.now()
    now = now.strftime('%Y년 %m월 %d일'.encode('unicode-escape').decode()).encode().decode('unicode-escape')
    now += datetime.now().strftime(' %H시 %M분 %S초 %f'.encode('unicode-escape').decode()).encode().decode('unicode-escape')
    print('현재 now 값:' + now)
    now += datetime.now().strftime(' %p')
    return render(request, 'www/index.html', {'time_now': now})

 웹 페이지를 새로고침 해보면 Debug 탭의 Console 영역에서 정상 출력되는 것을 볼 수 있습니다.

 하지만 디버깅이나 간단한 값 확인을 목적으로 코드를 추가하면, 완벽한 로직에 불필요한 부분이 생기는 것은 물론, 소스코드 길이가 길어지기도 하고, 나중에 깜박 까먹고 지우는 것을 잊을 수도 있고, 그런 코드를 시간이 지난 후에 보면 이걸 왜 썼지?하고 고민하는 경우도 생깁니다. 물론 무조건 이런 방법이 나쁜 것은 아니며, 상황에 따라 이렇게 하는 방식이 더 간단한 경우도 있습니다. 상황에 맞게 판단하는 것이 좋습니다.

 이런 간단한 값 확인을 따로 할 수 있는 기능이 존재합니다. 일단 값을 확인하고 싶은 시점에서 중단점을 걸고 웹 페이지를 새로고침 해봅니다. now 변수에 커서를 올려 놓고 마우스 메뉴에서 Evaluate Expression... 또는 Alt F8 키를 눌러 표현식 평가 창을 띄웁니다.

 Expression 칸에서 그냥 Enter 키를 누르면 값이 평가되어 출력됩니다.

 또는 코드에서 특정 줄을 복사해 평가하면 그 코드를 그대로 실행시킵니다.

 now.split() 함수를 실행시켜 결과 값을 확인할 수도 있습니다.

 now 값과 오전/오후 값을 즉석에서 더해보고 그 결과를 확인할 수 있습니다.

 표현식 평가창에서는 변수의 값을 확인하는 행위 말고, 변수의 값을 변경하는 행위(예를 들어, now = '123' 등) 또한 그대로 적용되어, 실제 변수의 값을 바꿉니다.

오류 페이지

 django 오류 페이지는 오류가 난 지점과 그 시점의 변수 값 정보, 오류 정보 등을 제공하는 유용한 페이지입니다.

 중단점을 소개할 때 썼던 예제에서 View 함수만 살짝 바꾸었습니다.

from django.shortcuts import render


# Create your views here.

def index(request):
    from datetime import datetime
    now = datetime.now()
    now = now.strftime('%Y-%m-%d %H:%M:%S %f %P')
    return render(request, 'www/index.html', {'time_now': now})

 실행시켜보면 아래와 같은 오류 페이지가 나타납니다. 오류의 원인을 찾을 수 있는 몇 가지 중요한 단서들이 있습니다.

 그리고 그 아래에 Trackback이라고 해서 호출 스택(Stack Frame) 정보가 나타나는 데, 아직 호출 스택이란 건 몰라두 되지만, 맨 밑에 짙게 강조된 부분을 클릭해봐요. 지금 당장 ctrftime() 함수의 문법 및 포맷을 알아야 된다는 말이 아니라, 오류 페이지에서 버그의 단서를 찾을 수 있는 방법을 습득해야 됩니다!

 보다시피 우리가 작성한 View 함수에서 오류가 난 부분인 9번 줄과 그 주변을 출력해 줍니다. 우리는 저 9번이 오류의 원인이라는 것을 알 수 있습니다. 위에서 문자열 포맷이 올바르지 않다고 했습니다. 잘 살펴보면 %p로 써야하는 것을 %P라고 적었네요.

 올바르게 수정하면 정상적으로 출력됩니다.

댓글

댓글 본문
  1. C코딩 시 조사식과 같은 기능이 파이참에도 있는지 찾고 있었는데, 표현식 평가부분이 큰 도움이 되었습니다. 감사합니다.