VSCode에서 제대로 Debugging 하는 방법
안녕하세요, chanmuzi입니다.
오늘은 많은 개발자가 사용하는 VSC에서 어떻게 debugger를 제대로 사용할 수 있는지에 대해 포스팅하려고 합니다! 🪲❌
코드를 작성하기 시작한지 그래도 꽤 시간이 지났는데, 말로만 debugging이 중요하다 듣기만 하고 print 함수를 사방팔방 썼다가 지우는 방법밖에 잘 몰랐습니다.
그런다고 사용 방법을 찾아봐도 본인만 알아보기 쉬운 글들 뿐이고..
다양한 상황에 어떻게 대처할 수 있는지 알기가 어렵더라고요 🥲
그래서 디버깅을 하면서 직접 알게 된 아주 유용하고 쓸모있는 기능들을 글로 정리하여 나중에도 또 활용하는 것이 목표입니다.
혹시라도 잘못된 내용이 있거나 추가적으로 알려주고 싶은 내용이 있다면 댓글로 편하게 말씀 부탁드립니다 ☺️
1. json 파일부터 작성해야 된다!
동일한 코드를 사용하면 좋겠지만..
세팅하는 것도 시간이 걸리는 일이니 경로나 설정을 알아서 잘 바꾸실 수 있을 것이라 믿겠습니다.
참고로 디버깅 대상 파일을 지정하면서 가상환경은 어떻게 설정하지..? 싶은 생각이 들 수도 있는데요, 이에 대해서도 다루겠습니다!
우선 vscode 좌측 패널을 보면 벌레 모양의 아이콘이 있습니다.
이걸 눌러보면 빈 창이 열리고 'Run and Debug' 버튼이 활성화되어 있는 것을 보실 수 있을 겁니다.
설명을 보면 Run and Debug를 커스텀하기 위해서는 launch.json 파일을 생성하라고 되어 있네요.
하지만 일단 버튼이 있으니 눌러보겠습니다.
그럼 아래와 같이 Python Debugger가 나옵니다.
이걸 클릭해보겠습니다.
그럼 여러가지 옵션들이 나오는데요, 이중에 예를 들어 첫 번째 'Python File Debug the currently active Python file'을 클릭하면 현재 화면에 띄워진 파이썬 파일을 실행하고 디버깅 해줍니다.
클릭하면 코드가 실행되고, 저는 에러가 발생해 코드가 동작을 멈추었습니다.
물론 이런 식으로 디버깅을 할 수도 있겠지만 굉장히 비효율적이기도 하고, 원하는대로 컨트롤 할 수 있는 영역이 굉장히 적습니다.
그래서 저희는 json 파일을 직접 작성해보도록 하겠습니다.
현재 실행 중인 터미널에서 'ctrl + c' 를 입력하거나 종료 버튼을 눌러서 과정을 종료합니다.
그리고 다시 디버깅 패널을 열어서 'create a launch.json file'을 클릭하고 python debugger를 선택합니다.
이어서 위와 마찬가지로 Python File을 클릭하면 아래와 같은 launch.json 파일이 생성됩니다.
이 파일은 현재 vscode를 실행한 윈도우의 root 폴더 최상단에 '.vscode' 라는 폴더 내에 위치하게 됩니다.
저는 downloads 폴더에서 vscode를 실행했기 때문에 위 경로에 폴더 및 파일이 생긴 것입니다.
2. 커스텀 디테일
위 상태로 실행한다고 문제가 되는 것은 아닙니다.
하지만 우리는 굉장히 다양하고 복잡한 환경에서 코드를 실행할 가능성이 높죠..
그래서 스스로에게 필요한 세팅을 직접 해야 합니다.
아래와 같이 json 파일을 수정하겠습니다.
{
"version": "0.2.0",
"configurations": [
{
"name": "example",
"type": "debugpy",
"request": "launch",
"program": "/downloads/example.py",
"console": "integratedTerminal",
"python": "/Users/chanmuzi/opt/miniconda3/envs/PyTorch/bin/python",
"env": {
"HYDRA_FULL_ERROR": "1",
"TOKENIZERS_PARALLELISM": "false",
"CUDA_VISIBLE_DEVICES": "0"
},
"args": [
"--model_name", "bert-base-uncased",
"--output_dir", "./outputs",
],
"justMyCode": false
}
]
}
각 요소를 간단히 살펴보면 이렇습니다.
- program
- debug 대상이 되는 파일입니다. 원래 터미널에서 'python /downloads/example.py' 명령어로 코드를 실행했을 때 이와 같이 작성할 수 있습니다.
- 단, 이렇게 하지 않고 script 파일을 작성하여 bash 명령어로 실행하는 경우 인자를 보다 편리하게 전달할 수 있습니다. (이에 대해 잘 모르면 위처럼 작성하는 게 편합니다)
- python
- 가상환경을 활성화하는 방법입니다. 위 예시에서 저는 'PyTorch'라는 가상환경을 활성화해서 코드를 실행하게 됩니다. 로컬의 경우 아래와 같은 경로에 conda가 설치되어 있을 가능성이 높으니 이미지를 참고해주시기 바랍니다!
- 여기서도 마찬가지로 script 파일에서 가상환경을 활성화하는 명령어를 사용할 수 있으나, 이 방법에 대해 잘 모른다면 그냥 위 방식을 따라해주세요.
- env
- 계층 구조를 갖는 config 파일을 관리하기 위해 hydra 라는 라이브러리를 사용 중입니다. 이를 위한 세팅으로 이해하시면 되겠습니다.
- hydra를 사용하지 않는다면 해당이 되지 않습니다.
- args
- 파일을 실행할 때, args로 전달할 것들을 위처럼 지정할 수 있습니다.
- 위 예시를 보면 python /downloads/example.py --model_name "bert-base-uncased" 와 같은 명령어를 원래 터미널에 입력했을 것을 알 수 있습니다.
- 필요에 따라 args에 전달하는 내용은 굉장히 많아질 수 있습니다!
- justMyCode
- default는 true이므로 건드리지 않아도 됩니다. 하지만 원활한 이해를 위해 false로 설정하겠습니다.
- false로 설정하는 경우, 본인이 정의하지 않은 변수, 클래스 등에 대해 skip이 불가능해집니다. 원래는 내장 함수나 라이브러리 및 패키지에 포함된 함수 및 클래스는 자동 스킵되는데 이를 막기 위함입니다.
이렇게 수정된 내용을 저장하고 디버깅 아이콘을 클릭하면 실행 가능한 설정이 확인됩니다.
실제 디버깅으로 넘어가기 전에 하나만 더 살펴보겠습니다.
그럼 한 번에 한 파일만 디버깅이 가능한가요? 같은 폴더 내에 다른 파일에 대해 디버깅하고 싶은 경우는 어떻게 하죠?
이런 상황에서는 방금 작성한 launch.json 파일의 configurations에 요소를 추가해주면 됩니다.
대괄호 안에 유사한 방식으로 세팅을 추가하는 것이죠.
이름만 바꿔서 내용을 추가하고 launch.json 파일을 저장해보겠습니다.
{
"version": "0.2.0",
"configurations": [
{
"name": "example",
"type": "debugpy",
"request": "launch",
"program": "/downloads/example.py",
"console": "integratedTerminal",
"python": "/Users/chanmuzi/opt/miniconda3/envs/PyTorch/bin/python",
"env": {
"HYDRA_FULL_ERROR": "1",
"TOKENIZERS_PARALLELISM": "false",
"CUDA_VISIBLE_DEVICES": "0"
},
"args": [
"--model_name", "bert-base-uncased",
"--output_dir", "./outputs",
],
"justMyCode": false
},
{
"name": "example2",
"type": "debugpy",
"request": "launch",
"program": "/downloads/example.py",
"console": "integratedTerminal",
"python": "/Users/chanmuzi/opt/miniconda3/envs/PyTorch/bin/python",
"env": {
"HYDRA_FULL_ERROR": "1",
"TOKENIZERS_PARALLELISM": "false",
"CUDA_VISIBLE_DEVICES": "0"
},
"args": [
"--model_name", "bert-base-uncased",
"--output_dir", "./outputs",
]
}
]
}
파일을 저장한 뒤 토글 화살표를 눌러보면 이렇게 두 개의 디버깅 대상 중 하나를 선택할 수 있게 되었다는 걸 알 수 있습니다.
이제 상황에 따라 설정들을 바꿔주고 원하는 파일을 디버깅 할 수 있게 되었습니다!!
3. break point & 버튼별 설명
준비는 다 마쳤으니 실제로 디버깅을 어떻게 하는지 확인해보겠습니다.
초록색 재생 버튼을 눌러 디버깅을 시작합니다.
단, 시작 이전에 내가 코드를 확인하고 싶은 지점에 빨간 점을 만들어줘야 합니다.
line의 숫자가 쓰인 부분에 마우스 커서를 갖다 대면 클릭이 가능한데요, 클릭하는 지점마다 break point가 생성됩니다.
이것의 의미는 코드가 실행되는 과정에서 break point 직전까지만 코드를 전부 실행하고 멈추라는 것입니다.
즉, 이미지에서는 22번째 줄이 실행되기 전까지의 상황만 모두 자동적으로 코드가 실행된 것이고 이제 더 진행할지를 물어보게 됩니다.
화면 중앙에 보면 여섯 개의 버튼이 있습니다.
각 버튼의 기능은 아래와 같습니다.
- Continue
- 코드 실행이 진행됩니다. 코드 진행 중 이상이 없다면 다음 break point 까지 실행됩니다.
- 단, 코드 실행 중 에러가 발생하는 경우에도 당연히 작동이 멈추게 됩니다.
- 위 이미지에서 Continue를 클릭했을 때는 다음 break point인 24번째 줄에 걸리게 됩니다.
- Step Over
- 다음 줄의 코드를 실행해줍니다. 그러나 내부로 들어가지 않고 건너 뛰는 기능입니다.
- 예를 들어 24번째 줄에서 break가 걸렸을 때, Step Over를 클릭하면 사전학습된 토크나이저를 불러와 tokenizer 변수에 담고 다음 줄로 넘어갑니다.
- Step Into
- 다음 줄의 코드를 실행합니다. 그러나 Step Over와 달리 내부 함수나 클래스 내로 파고듭니다.
- 예를 들어 25번째 줄에서 break가 걸렸을 때, Step Into를 클릭하면 read_csv 함수가 정의된 내부 패키지 코드로 이동합니다. 굳이 이런 내용까지 보고 싶지 않다면 Step Over를 클릭하면 됩니다.
- Step Out
- Step Into로 들어간 파일에서 탈출시켜줍니다.
- 위 예시에서 생각을 이어나가 본다면, read_csv 함수가 정의된 pandas 파일 내에서 Step Out을 클릭하면 현재 파일 위치로 돌아오게 됩니다.
- 자세한 내용을 살펴보다가 과정이 충분히 이해되었을 때 탈출을 도와준다고 이해할 수 있습니다.
- Restart
- 말 그대로 재시작입니다. 중간에 에러가 발생했거나 수정된 내용을 반영하고 싶을 때 주로 사용합니다.
- 하지만 이전으로 돌아가는 것은 절대로 불가능합니다. 코드가 어느 정도 꽤 실행이 된 상태라면 신중하게 눌러야 할 버튼입니다.
- Stop
- 디버깅을 중단합니다. 더이상 볼 내용이 없거나 디버깅을 진행하고 싶지 않을 때 누르시면 됩니다.
break point와 버튼들을 적절히 사용하여 원하는 위치에서 디버깅을 자세하게 수행하거나 스킵할 수 있게 됩니다.
참고로 좌측 하단에는 어떤 지점에 break를 걸었는지, 그리고 Exceptions를 어떻게 처리할 것인지에 대한 설정이 나타나 있습니다.
필요에 따라 조절 가능합니다.
4. Debugging Console
지금가지 진행 상황은 아래 이미지와 같습니다.
즉 모델과 토크나이저를 불러온 상황이고, main 함수가 전달받은 config에 대한 값도 저장되어 있다는 걸 좌측의 VARIABLES 탭을 통해 확인할 수 있습니다.
config를 확인해 볼까요?
토글을 확장해보면 대충 이런 구성입니다.
딕셔너리 형태로 값을 저장하고 있는 것이 보이네요.
우리는 이런 식으로 global, local 변수들을 전부 매 시점마다 어떻게 변화하고 있는지 확인할 수 있습니다.
즉 코드가 어떤 식으로 동작하는지 볼 수 있는 것입니다.
하지만 이것은 실제로 우리가 디버깅 과정에서 다룰 아주 사소한 기능일 뿐입니다.
전체 화면 내에서 터미널 창 위치에 DEBUG CONSOLE 이 활성화되어 있다는 것이 보이실 것입니다.
이것은 마치 터미널처럼 활용 가능하며 여기서 직접 변수를 확인할 수 있습니다.
방금과 마찬가지로 config 변수를 확인해 보겠습니다.
그렇습니다.
우리는 실시간으로 로컬 및 글로벌 변수에 접근해서 어떤 값들이 저장되어 있는지 확인할 수 있게 된 것입니다...
이렇게 우리가 원래 코드를 사용하는 방식 그대로 변수를 이용할 수 있습니다.
즉, 필요한 위치에서 다음 줄로 코드를 넘겨 가면서 이 값이 어떻게 변화하고 있는지 바로바로 확인이 가능한 것이죠.
재밌는 것은 단순한 출력도 가능하고, 값을 할당하는 것도 가능합니다.
예를 들어 test 라는 변수에 문자열을 할당해보겠습니다.
그럼 이렇게 로컬 변수가 생성이 됩니다.
이 로컬 변수는 continue, step over, step into 등으로 코드를 진행시켜도 같은 경로 상에 남아있다면 사라지지 않습니다.
추가적인 테스트에 활용하기 너무 좋겠죠.
혹은 이와 같이 기존 변수를 활용하거나, 변수를 사용하지 않은 출력도 가능합니다.
여기까지 Vscode에서 지원하는 Debugging 관련 기능들을 살펴보았습니다!
저는 이전까지 디버깅을 제대로 한 적도 없고..
python debugger (pdb) 도 충분히 좋다고 생각을 했었는데요..!
vscode에서 지원하는 debugging 기능을 제대로 알고나니 이전으로 돌아갈 수 없는 몸이 되어버렸습니다 🥹
아직까지 막연해서 시도를 해보지 않으셨거나, 디테일한 부분들에서 어려움을 느끼셨던 분들에게 도움이 되면 좋겠습니다.
혹시라도 어려운 부분이 있다면 ChatGPT님에게 질문을 드려보시면 잘 알려주실 겁니다 ㅎㅎ