Debugging
디버깅 이란?
디버깅은 프로그램의 버그, 오류를 찾아내고 원인을 분석해서 고치는 것, 프로그램 내부에서 지금 무슨 일이 일어나고 있는지
직접 관찰하는 과정을 말한다.
예를 들어 이러한 문제가 생길 때 사용한다.
- 실행은 되는데 값이 이상하게 나온다
- 프로그램이 중간에 꺼진다
- 반복문이 끝나지 않는다. 무한 반복된다.
- 함수가 내가 생각한 순서대로 돌아가지 않는다.
- 변수 값이 이상해진다 등
이러한 문제들이 생기면 디버깅 도구를 사용하여 특정 줄에서 멈추고 현재 변수 값을 확인하며 함수 호출 흐름을 보고 한 줄씩 실행하면서 어디서 잘못돼 있는지 추적하는 것들을 할 수 있다.
왜 디버깅이 중요한가?
필자 같은 초보들은 사용하던 코드들이 중복으로 작성되게 되며 코드가 길어지고 한계가 생긴다 그럴 때 어딘가에서 문제가 생겨서
오류를 잡아내야 하지만 코드가 많이 길어져 있어서 하나하나 찾아보기 힘들기 마련이다.
그래서 디버깅을 하게 되면
- 코드의 실제 실행 흐름을 볼 수 있다
- 변수 값이 언제 어떻게 바뀌는지 확인이 가능하다
- 에러의 원인을 추측이 아닌 확인으로 찾을 수 있다
- 내가 작성한 코드의 동작 원리를 더 깊게 이해하게 된다.
디버깅은 단순히 오류를 잡는 것만이 아닌 작성자가 코드를 이해하는 도구의 하나이기도 하다.
Logging
logging 이란?
Logging은 프로그램이 실행되면서 생기는 정보들을 출력해서 기록하는 것을 말한다.
즉, 지금 프로그램이 어디까지 실행이 되었고 변수 값이 뭔지, 어떤 함수가 호출이 됐는지 출력해서 확인하는 방법을 말한다.
// Main.cpp
#include <iostream>
#include <string>
int main(void)
{
int playerHP = 500;
int Matk = 50;
int Pdef = 10;
int Damage = Matk - Pdef;
std::cout << "player HP: " << playerHP << std::endl;
std::cout << "Damage: " << Damage << std::endl;
}
여기서 Logging은
std::cout << "player HP: " << playerHP << std::endl;
std::cout << "Damage: " << Damage << std::endl;
이런 것들을 말한다.
왜 쓰는가?
로깅은 가장 쉽고 빠르게 확인할 수 있는 방법이다.
- 여기까지 코드가 실행됐는지
- if문 어느 쪽으로 들어갔는지
- 반복문이 몇 번 도는지
- 특정 변수 값이 몇인지
확인해야 할 때 아주 좋은 디버깅이다.
장점
- cout 만으로도 간단하게 확인할 수 있다.
- 찍어볼수록 흐름 확인이 쉽다.
- 값의 확인이 쉽다.
단점
- 로그가 많아질수록 코드가 길어지고 지저분해진다
- 필요한 순간, 위치마다 직접 추가해야 한다
- 값이 바뀐 정확한 순간의 추적은 어렵다
Break Point
Break Point (브레이크 포인트) 란?
프로그램 실행 중에 특정 줄에서 강제로 멈추게 하는 것을 말한다. 즉 프로그램이 실행되다가 지정한 줄에 도달하면 일시 정지한다.
예시를 들자면 검문소에서 멈춰서 상태를 확인하는 거다.
이때 확인할 수 있는 것들
- 현재 변수 값
- 객체 내부 값
- 포인터 상태
- 어떤 함수에서 호출됐는가
- 프로그램 실행 흐름
브레이크 포인트는 프로그램을 멈춰서 "현재 상태를 직접 확인하는 디버깅 도구"다
왜 브레이크 포인트가 중요한가?
코드는 위에서부터 아래로 굉장히 빠른 속도로 실행된다.
아래의 코드를 예시로 들면
// Main.cpp
#include <iostream>
#include <string>
int main(void)
{
int playerHP = 500;
int Matk = 50;
int Pdef = 10;
int Damage = Matk - Pdef;
std::cout << "player HP: " << playerHP << std::endl;
std::cout << "Damage: " << Damage << std::endl;
if (Damage > 0)
{
std::cout << "\n" << Damage << "만큼의 타격을 입었다.\n";
}
else
{
Damage = 1;
std::cout << "\n" << Damage << "만큼의 타격을 입었다.\n";
}
playerHP -= Damage;
std::cout << "\nplayer HP: " << playerHP << std::endl;
}
컴퓨터는 이 코드를 인간이 감지하지 못하는 속도로 실행된다. 그래서 우리는 중간 상태를 확인할 수 없다.
하지만 브레이크 포인트를 걸면 "여기서 멈출 수 있도록 해"라고 프로그램을 멈출 수 있다.
그때 우리는 HP 값, Damage 값, 현재 실행 위치 같은 것들을 확인할 수 있다.

11번 옆에 빨간 점이 중단점이며 여기까지 실행하며 나온 값이다.

F5를 눌러 추가로 다시 실행 후 다시 중단점까지 왔을 때 실행 창이다.
사용 방법
F5 - 디버깅 진행하다 중단점이 있으면 멈춤
F9 - 중단점을 지정 또는 해제
F10 - Step Over. 함수 안으로는 들어가지는 않고 한 줄씩 실행
F11 - Step Into. 함수 안으로도 들어가서 한 줄씩 실행
중요해서 강조해 두는 것 이 4개 단축키는 필히 외워야 된다.
F10, 11은 중단점에서 멈춘 후 사용 할 수 있다.
찾아보니 Shift + F11 - Step Out. 도 있는데 함수 실행을 끝내고 밖으로 나갈 때 사용한다고 한다.
장점
실행 과정 자체를 보게 됨으로 하나씩 확인하여 원인까지 전부 확인할 수 있다.
단점
프로그램 실행 흐름을 완전히 멈추게 하기에 실시간 시스템이나 타이밍이 중요한 코드 분석 시에는 동작을 왜곡할 수 있다
Call Stack
Call Stack 이란?
현재 실행 중인 함수가 어떤 함수 호출 흐름을 통해 도달했는지 보여주는 것을 말한다.
예시를 보자면
// Main.cpp
#include <iostream>
#include <string>
void startGame();
void battle();
void monsterAttack();
void calculateDamage();
int main()
{
startGame();
}
void startGame()
{
battle();
}
void battle()
{
monsterAttack();
}
void monsterAttack()
{
calculateDamage();
}
void calculateDamage()
{
}

Call Stack에서 어디 라인에서 어떤 함수가 호출되었는지 확인할 수 있다
Cell Stack이 왜 중요한가?
- 프로그램이 어디서 왔는지 알 수 있다.
"이 함수가 왜 실행됐는가, 어디서 호출됐는가"를 알 수 있다. - 프로그램 흐름을 역추적할 수 있다.
"어디서 잘못됐는가? 어디서 멈췄는지"확인이 가능하다. - 프로그램 구조를 이해할 수 있다.
대형 프로젝트에서 어떻게 돌아가는지 어떤 구조로 돌아가는지 보인다. - 크래시 원인을 찾을 수 있다.
프로그램이 죽게 되면 거의 Call Stack에 기록이 되어있어 찾을 수 있다.
장점
- 함수 호출 흐름을 한눈에 볼 수 있다.
- 버그 발생 위치를 찾기 쉽다.
- 프로그램 구조 이해에 도움이 된다.
- 디버깅 도구들과 함께 사용하기 좋다
단점
- 함수가 많아질수록 너무 복잡해진다.
- 현재 실행 경로만 보여준다.
- 비동기/멀티스레드에서 해석이 어렵다.
Watch
Watch 란?
디버깅 중에 특정 변수나 식의 값을 계속 보여주는 창으로
- 이 변수 값이 지금 얼마인가?
- 한 줄 실행하면 어떻게 바뀌는가?
- 함수 안으로 들어가면 값이 어떻게 달라지는가?
- 반복문을 돌 때 값이 몇으로 변하는가?
이걸 계속 관찰하게 해주는 것을 말한다.
int hp = 100;
int damage = 30;
hp = hp - damage;

이렇게 내가 추가해서 확인가능하다.
Watch가 왜 중요한가?
예를 들자면
player->setHP(player->getHP() - damage);
이런 식이 있다면 우리는 한 줄에서 어떠한 값이 나올지 궁금해진다.
- player->getHP()는 지금 얼마인가?
- damage는 얼마인가?
- 계산 결과는 얼마인가?
- 그래서 setHP로 들어가는 값은 얼마가 되는가?
이때 Watch가 있으면 이 값들을 실시간으로 따로 계속 볼 수 있다.
Watch는 디버깅 중 특정 변수나 표현식의 값을 계속 추적하고 관찰할 수 있게 해주는 기능이다.
Watch는 언제 쓰는가?
1변수 값이 이상할 때
damage가 -20이 나옴
값이 어디서 바뀌는지 알고 싶을 때
hp가 언제 100에서 0이 되었는지
반복문 상태를 보고 싶을 때
i, j, count 값 추적
포인터 상태를 보고 싶을 때
player가 nullptr인지 아닌지
객체 내부 값을 보고 싶을 때
player->HP, monster->HP
Watch 와 Logging의 차이
Logging
코드에 내가 직접 출력문을 넣어 사용하는것
- 코드에 직접 작성
- 콘솔 출력으로 확인
- 실행 후 흔적을 보는 느낌
Watch
코드를 수정하지 않고 디버깅 창에서 값만 등록해서 보는 것
- 디버그 창에 등록
- 멈춘 순간 값을 바로 확인
- 실시간으로 확인
Logging 은 기록을 뜻하며 Watch는 관찰을 뜻한다.
장점
- 변수 값을 실시간으로 추적할 수 있다.
- 코드 수정 없이 변수 확인이 가능하다.
- 여러 값을 동시에 관찰할 수 있다.
- 계산식도 확인할 수 있다.
- 반복문 디버깅에 매우 유용하다.
- 객체 내부 값 확인이 쉽다.
단점
- 디버그 모드에서만 사용이 가능하다.
- 무엇을 봐야하는지 알아야 한다.
- 너무 많은 값을 넣으면 오히려 작성자가 보기 어려워진다
- 프로그램 흐름 자체는 보여주지 않는다.
마무리
Logging은 출력해서 내가 프린트로 확인하고 싶어서 자주 사용해 왔던게 디버깅이 하나 엿다는 것이 신기했으며
다른 디버깅들은 한번도 사용해 본적이 없었으나 내용을 정리하면서 필수로 배워두고 사용해야 하는 기능임을 느꼇다.
'학습일지 > C와 C++' 카테고리의 다른 글
| C++ 스마트 포인터 (0) | 2026.03.18 |
|---|---|
| C++ 인벤토리 시스템 구현 (0) | 2026.03.17 |
| Moderen C++ (1) | 2026.03.15 |
| C++ Vector (0) | 2026.03.14 |
| C++ Template Programming (0) | 2026.03.13 |