디버그

내위키

Debug.

컴퓨터 프로그램에 숨어 있는 각종 오류, 즉 버그를 찾아서 바로잡는 것. 디버그를 위해 사용하는 소프트웨어를 디버거(debugger)라고 한다. 컴퓨터 프로그래밍은 사람이 하는 일이라 버그가 하나도 없는 소프트웨어는 제로에 가깝다. 버그가 없는 아름다운 프로그램으로 유명한 TeX도 2022년 초 기준으로 버전 3.141592653인데, 버전 3이 나온 이후로 버그가 발견되어 고치면 소숫점 뒤의 숫자들이 하나씩 추가된다.[1] 즉, 이 프로그램조차도 버그가 없다기보다는 '거의 없다'에 가깝다.

하나의 완성된 소프트웨어를 만들기 위해 개발자들은 코딩에 쓰는 시간보다 디버그에 쓰는 시간이 더 많다. 누구는 코딩 시간 대비 디버깅 시간이 3대 7이라고도 하고 누구는 2대 8이라고도 한다. 그만큼 디버그가 개발자의 시간과 노력을 많이 잡아먹기 때문에 프로그래밍 언어와 개발환경은 어떻게 하면 잠재된 버그의 가능성을 줄이고, 개발자가 더 빠르게 버그를 찾아낼 수 있을지, 버그가 있더라도 어떻게 하면 그에 따른 오류의 규모와 피해를 최소화할지에 많은 신경을 쓰고 있다.

디버그를 위한 개발자의 노력은 마치 요리조리 도망다니는 범인을 쫓아다니는 형사와도 비슷하다. 문법 오류 같은 거야 요즈음은 개발 환경에서 바로바로 잡아주기 때문에 별로 문제가 안 되지만 진짜 시간과 노력을 잡아먹는 건 실행은 멀쩡히 되는데 원하는 결과가 나오지 않거나 실행 도중에 런타임 오류가 터질 때다. 다행히 현대의 개발 트렌드는 모듈화, 객체지향, 의존성 감소와 같은 기법을 통해 버그의 원인을 더 빨리 찾아내는 데에 도움을 주고 있지만 여전히 도대체 어디서부터 문제가 생겼는지 그 실마리를 한동안 찾지 못해서 헤멜 때도 부지기수다.

디버거

디버거는 코드를 한 줄씩 실행시키거나, 특정 지점까지 프로그램 실행이 왔을 때 일시정지하는 기능이 있으며, 특정 실행시점에서 변수의 값을 확인할 수 있는 기능을 가지고 있다. 디버거마다 그밖에 다른 추가 기능들이 있지만 일단 이 세 가지가 디버거라면 공통으로 가지고 있으며 가장 많이 쓰이는 기능이다.

  • 먼저 버그가 의심스러운 부분에 실행 중지점(breakpoint)을 설정해 놓고, 개발환경에서 디버그 모드로 프로그램을 실행시킨다.
  • 각 변수의 값이 어떻게 되어 있는지, 즉 원하는 값을 가지고 있는지를 살펴본다.
  • 문제가 없다면 한 줄씩 실행시켜 본다. 만약 함수나 메서드를 호출하는 줄이라면 그 함수 안으로 들어가서 한 줄씩 실행시키는 step in 실행을 하거나, 함수는 통으로 실행시키고 결과를 받아와서 다음 줄로 넘어가는 step ovrer 실행을 시킬 수 있다. 어떤 함수 안이라면 그 함수의 나머지 코드를 모두 실행시키고 함수를 호출했던 코드의 다음 줄에서 정지하는 step out 실행도 가능하다.

이렇게 디버거의 기능을 활용하려면 실행 파일을 디버그 모드로 빌드해야 한다. 실행 파일의 어떤 부분이 소스 코드의 어떤 파일 몇 번째 줄에 해당하는지와 같은 정보를 심어 줘야 디버거가 소스 코드의 줄에 맞춰서 프로그램의 실행을 중지시키거나, 변수의 값을 보여줄 수 있기 때문이다. 디버그 모드로 빌드된 실행 파일은 이러한 정보가 추가로 들어가므로 덩치가 크고 실행 속도가 느려지며, 디버그 정보가 보안 취약점이 될 수 있으므로 외부에 공개하는 실행 파일은 반드시 릴리즈 모드로 빌드해야 한다.

로깅

프로그램이 실행되었을 때, 코드의 특정한 부분에서 개발자가 원하는 데이터를 출력할 수 있는 로깅(logging) 기능을 활용하는 방법도 있다. 로깅 메시지는 콘솔이나 파일로 출력할 수 있다. 예를 들어 코드의 특정 지점에서 개발자가 알고자 하는 변수의 값을 메시지에 넣어서 로깅을 하면 콘솔이나 파일에 그 기록이 출력된다. 함수나 메서드 안에 이 함수가 호출되었다는 것을 로깅으로 남기면 함수의 호출이 올바른 순서로 되었는지를 체크해 볼 수 있다. 로깅을 디버그 모드로 빌드했을 때에만 할지, 릴리즈 모드로 빌드했을 때에도 로깅을 할지를 선택하거나, 로그가 단순 정보인지, 경고인지, 오류 메시지인지를 지정하는 옵션을 줄 수도 있다. 로깅은 대부분 개발자가 아난 일반 사용자에게는 불필요한 정보이며 오히려 보안 취약점의 원인이 될 수 있으므로[2] 릴리즈 모드에서도 로깅을 하는 것은 정말 꼭 필요한 경우가 아니라면[3] 피해야 한다.

로깅 대신 간단하게 표준 출력 스트림에 메시지를 출력하는 기능을 사용해서 로깅 비스무리하게 활용하기도 하는데, 짧은 코드라면 이렇게 테스트해 보고 나중에 릴리즈 빌드할 때에는 지우면 그만이겠지만 코드의 덩치가 크다면 나중에 이거 지우느라 수많은 코드 속을 허우적거리게 될 수도 있다. 잠깐 잠깐 테스트하고 바로바로 지우는 거 아니면 이런 식으로는 하지 말자.

Assert

Assert는 '주장'이라는 뜻인데, 코드의 어떤 지점에서 상태를 테스트하기 위해 쓰는 명령어다. 프로그래밍 언어마다 차이가 있지만 보통은 assert() 함수를 제공하는데, 함수의 매개변수를 테스트해서 참이면 통과하고 거짓이면 프로그램 실행이 중지되고 오류 메시지를 낸다. 즉, 버그가 밟으면 터지는 일종의 지뢰를 설치해 놓은 것과도 같다. 프로그래밍 언어에 따라서는 이를 더 확장해서 두 값을 비교해서 같은지 다른지를 테스트하거나, 두 개의 매개변수 중 첫 번째보다 두 번째가 큰 지 작은 지를 테스트하거나, 하는 여러 가지 assert 함수를 제공하는 경우도 있지만 결국은 참 거짓으로 테스트가 되어야 하므로 assert()로 모두 수렴할 수 있다. assert() 함수는 디버그 모드일 때에만 동작하고 릴리즈 모드일 때에는 아예 실행 코드에 포함되지 않는 게 보통이다.

각주

  1. 딱 보면 알겠지만, 원주율 숫자다.
  2. 2021년에 아파치재단이 만든 자바용 로깅 라이브러리인 Log4j에서 해커가 서버 로그인에만 성공하면 루트로 로그인하지 않아도 사실상 컴퓨터를 장악해서 원격조종할 수 있는 보안 취약점이 발견되어 그야말로 전 세계 인터넷 서비스 산업계가 뒤집어졌다.
  3. 예를 들어 서버용 프로그램은 서버 관리자가 프로그램의 시동, 중지, 재시동이나 외부 공격 시도와 같은 이벤트 기록을 알아야 하므로 릴리즈 모드라고 해도 로깅이 필요하다. 그런데 이 때문에 전 세계 인터넷 서비스에 막대한 보안 위협을 일으킨게 2021년 Log4j 보안 취약점 사태다.