본문 바로가기

디버깅과 테스트

버그를 재현하고 로그·브레이크포인트로 원인을 좁힌 뒤 개발 테스트와 QA로 회귀를 방지합니다.

이전 절에서 우리는 언리얼 엔진에서 게임 시스템을 기획하고 구현하는 방법에 대해 알아보았습니다.

코드를 작성하고 기능을 추가하는 것은 개발의 핵심이지만, 완벽한 코드는 존재하지 않으며 예상치 못한 버그와 문제가 끊임없이 발생합니다.

이때 필요한 것이 바로 디버깅(Debugging)과 테스트(Testing)입니다.

이 두 과정은 게임의 품질을 확보하고 플레이어에게 안정적인 경험을 제공하는 데 필수적입니다.

이번 절에서는 언리얼 엔진 프로젝트에서 효과적인 디버깅 기법과 다양한 테스트 전략을 통해 게임의 버그를 찾아내고 수정하는 방법에 대해 자세히 살펴보겠습니다.


작은 탄약 루프의 예상과 실패 확인

앞 절의 첫 목표는 새 슬롯에서 0/30 → Pickup → 10/30 → F5 저장 → 새 실행 10/30으로 이어지는 것입니다. 다음 표는 코드를 같은 실제 클래스에 합치고 에셋·입력을 설정한 뒤 확인할 예상입니다. 예제의 실행 로그나 오류 재현 결과로 제시하는 표는 아닙니다.

조건/행동예상 결과먼저 확인할 증거
처음 쓰는 AmmoLoopSlot·UserIndex 0읽기 판정 뒤 0/30, 파일 자동 생성 없음슬롯/사용자·DoesSaveGameExist·캐릭터 초기화
현재 캡슐로 Pickup 한 번 습득실제 증가량 10, getter/HUD 10/30, Pickup 1회 소모현재 소유·초기 판정·양쪽 Overlap·캡슐 필터·AddAmmo 결과
10/30에서 F5 저장SavedAmmo 10을 복사하고 API bool true일 때만 성공 표시SaveAmmoAction/활성 IMC/바인딩·현재 Pawn/getter·쓰기 결과
종료 뒤 같은 유효 슬롯으로 새 실행읽은 탄약 10→준비된 Pawn 적용→HUD 10/30LoadAmmoLoopOnce·null/cast/range·pending 해제·현재 getter·HUD snapshot
slot 있음, null·다른 타입·값이 0~30 밖오류 상태, 저장/습득 중단; 초깃값 0 자동 저장·파일 삭제 없음bLoadFailed·실제 SaveGame 타입·SavedAmmo; clamp하지 않음
현재 Pawn 없음·다른 클래스·초기화 전·미결 loadSaveAmmoLoop false, 쓰기 호출 전 중단현재 Controller0/GetPawn·actual Character·read/ready/pending
AmmoSaveGameClass 누락·객체 생성 실패false, 쓰기 API/성공 표시 없음실제 GameMode의 별도 Ammo Save Game Class·생성 cast
SaveGameToSlot false쓰기 실패 표시; 이전 파일의 원자적 보존 보장 없음API bool·사용 환경; backup/rollback은 미구현
저장값 0·초깃값 0유효한 적용 성공, pending 해제TryApplyLoadedAmmo의 검증/true; 증가량 0과 구별
HUD가 적용 뒤에 준비파일을 다시 읽지 않고 현재 getter 값 10을 표시AddToViewport/Super::OnPossess 뒤 snapshot·OnAmmoChanged
준비 중 Overlap이 거절됨증가량 0·Pickup 남음; 준비 뒤 나갔다 다시 들어가야 재시도준비 조건·새 BeginOverlap; box 안에서 자동 재지급 없음
새 실행의 Pickup을 다시 습득복원된 10→20 가능탄약만 저장하며 레벨 Pickup 소모·월드는 저장하지 않음

getter가 10인데 화면이 0이면 저장/습득을 다시 실행하기보다 Controller 통지와 위젯 갱신·현재값 조회를 비교합니다. 반대로 getter도 0이면 HUD보다 앞의 읽기 판정·pending·실제 Pawn 적용을 확인하세요. 초깃값 0 화면만 보고 저장 키를 눌러도 미결·오류 상태에서는 준비 조건이 쓰기를 막습니다. 유효한 저장값을 적용한 뒤 현재 탄약 0을 의도적으로 저장하는 것과 구별합니다.

기존 슬롯 읽기 실패에 대한 삭제·복구·재시도 버튼, 파일 버전 migration, 위치·체력·전체 월드·Pickup 소모 저장은 제공하지 않습니다. 입력 요청은 단순 Bool 액션의 Started 한 경로이며 Blueprint에서도 중복 저장하지 않습니다. 단일 로컬 Standalone의 최초 시작/로드만 다루고 리스폰·전체 재소유/소유 해제·분할 화면·네트워크 동기화는 별도 구현이 필요합니다. 아래의 개발 테스트·QA 방법은 이 작은 흐름에서 확인할 증거를 넓힐 때 참고합니다.


디버깅: 문제의 원인 찾기

디버깅은 코드의 오류를 식별하고 수정하는 과정입니다.

언리얼 엔진은 강력한 디버깅 도구들을 내장하고 있으며, 외부 IDE와의 연동을 통해 더욱 세밀한 디버깅을 가능하게 합니다.

언리얼 에디터 내 디버깅 도구

  • 인게임 콘솔 (~ 키)
    • 가장 빠르고 기본적인 디버그 도구입니다. 게임 실행 중 ~ 키를 눌러 콘솔을 열고 다양한 명령어를 입력하여 게임 상태를 확인하고 변경할 수 있습니다.
    • 예시
      • stat fps: 현재 프레임 속도 표시
      • stat unit: CPU 및 GPU 시간 통계 표시
      • show collision: 콜리전 메시 시각화
      • toggledebugcamera: 자유 시점 디버그 카메라 활성화
      • Gameplay Debugger: 실행 중 기본 작은따옴표 키로 열고 대상 AI와 AI/Behavior Tree 카테고리를 선택합니다. 단축키·카테고리 활성화는 프로젝트 설정에 따라 다를 수 있습니다.
      • log LogTemp Warning: 특정 로그 카테고리의 출력 수준 변경 (실시간)
  • 블루프린트 디버거
    • Window -> Developer Tools -> Blueprint Debugger: 블루프린트 로직의 실행 흐름을 시각적으로 추적하고 변수 값을 확인할 수 있습니다.
    • 브레이크포인트: 노드에 브레이크포인트를 설정하여 특정 지점에서 실행을 일시 중지하고 단계별로 실행할 수 있습니다.
    • 와치(Watch): 특정 변수의 값을 실시간으로 모니터링할 수 있습니다.
  • 비주얼 로거 (Visual Logger)
    • Window -> Developer Tools -> Visual Logger: 게임 실행 중 발생하는 이벤트, AI 행동, 이동 경로 등을 시각적으로 기록하고 나중에 재생하여 분석할 수 있습니다. 특히 복잡한 AI나 물리 시뮬레이션 디버깅에 유용합니다.
    • 지원되는 Visual Logger 창에서 기록을 시작하고, Visual Logger 이벤트가 실제 기록되는지 확인합니다. 일반 로그가 모두 자동으로 공간 기록이 되는 것은 아닙니다.
  • 프로파일러: Unreal Insights의 Timing Insights로 CPU 스레드·작업을 분석하고 GPU 프로파일과 함께 병목을 구분합니다. stat startfile / stat stopfile의 stats 캡처와 Insights trace는 서로 다른 기록 형식입니다.
  • 메모리 분석: Memory Insights를 위해 지원되는 Development 실행을 처음부터 -trace=default,memory로 시작합니다. 할당·해제·콜스택과 시점별 살아 있는 메모리를 비교합니다. Session Frontend에 범용 Memory 스냅샷 탭이 있다고 가정하지 않습니다.

외부 IDE (Visual Studio/Xcode) 디버깅

C++ 코드를 사용하는 프로젝트에서 가장 강력한 디버깅 도구입니다.

  • 브레이크포인트: 코드의 특정 줄에 브레이크포인트를 설정하여 프로그램 실행을 일시 중지하고, 해당 지점에서의 모든 정보(변수 값, 호출 스택, 레지스터)를 검사할 수 있습니다.
  • 단계별 실행
    • Step Over (F10): 현재 라인을 실행하고 다음 라인으로 이동합니다. 함수 호출 시 함수 내부로 들어가지 않습니다.
    • Step Into (F11): 현재 라인을 실행하고, 함수 호출 시 해당 함수 내부로 진입합니다.
    • Step Out (Shift+F11): 현재 함수를 모두 실행하고, 해당 함수를 호출한 곳으로 돌아갑니다.
  • 조사식 (Watch) / 지역 (Locals) 창: 변수들의 값을 실시간으로 확인하고 변경할 수 있습니다.
  • 호출 스택 (Call Stack): 현재 코드가 어떤 함수 호출 경로를 통해 실행되었는지 보여주어 버그의 근본 원인을 추적하는 데 매우 중요합니다.
  • 디버그 빌드: Visual Studio 프로젝트의 DebugGame Editor 또는 DebugGame 설정은 게임 코드의 최적화를 낮춰 디버깅하기 편하게 합니다. Development·Shipping도 구성에 따라 심볼을 만들 수 있으며 정확히 일치하는 실행 파일·심볼을 보관해야 합니다. 최적화된 코드는 일부 변수나 소스 단계가 보이지 않을 수 있습니다.

로그 시스템 활용

  • UE_LOG(LogCategory, Verbosity, TEXT("Your message here..."));: C++ 코드에서 특정 카테고리와 중요도(Error, Warning, Log, Verbose 등)를 지정하여 메시지를 출력합니다. 이는 Output Log 창과 로그 파일에 기록됩니다.
  • Print String 노드: 블루프린트에서 디버그 메시지를 화면에 표시하거나 Output Log에 출력하는 가장 흔한 방법입니다.

디버깅 도구는 증상의 위치가 월드 상태인지, 블루프린트 흐름인지, C++ 호출 경로인지에 따라 먼저 볼 화면이 달라집니다.


예시: 화면의 0에서 원인을 한 단계씩 좁히기

현재 실행에서 최초 읽기가 끝났고 pending이 해제됐으며 Character getter는 10인데 HUD만 0이라고 가정합니다. 이미 적용된 데이터가 있으므로 저장 파일부터 덮어쓰거나 Pickup을 다시 지급하지 않습니다. 실제 Controller와 위젯 클래스·자식 이름, OnAmmoChanged 전달과 HUD 준비 뒤 SyncHUDFromPawn의 현재값 조회를 비교합니다. 같은 실행 파일·맵·슬롯·입력 순서에서 getter와 화면을 함께 남겨 수정 전·후의 증거를 비교하세요.

단위 확인은 유효 범위와 남은 용량의 계산을, 통합 확인은 Character→Controller→HUD의 전달을, 끝까지의 확인은 F5 저장 뒤 새 실행의 읽기·적용까지 다룹니다. 함수 한 개의 계산이 맞다고 파일·입력·실제 에셋 설정까지 검증됐다고 확대하지 않습니다.

새 조건에서 화면도 getter도 0이지만, 로드값 17은 유효하고 bHasPendingAmmo가 true이며 현재 Pawn이 아직 준비되지 않았습니다. 앞 사례와 같은 HUD 문제로 확정할 수 있나요? 준비된 현재 캐릭터에 17을 적용해 pending이 해제된 뒤 HUD가 생기면 기대할 숫자는 무엇이며, 무슨 증거로 확인하겠습니까?

아직 적용하지 않은 값과 표시하지 못한 값을 구별하기

이번에는 17이 아직 pending에 있으므로 getter 0을 HUD 전달 실패로 확정할 수 없습니다. 현재 Pawn의 실제 타입·초기화·소유 조건과 준비 경로의 TryApplyPendingAmmo 호출을 먼저 확인합니다. 유효한 적용 뒤 getter 17과 pending 해제, 이후 HUD 현재값 조회의 17/30을 각각 확인합니다. getter 17인데 화면만 0으로 남으면 그때 앞 사례의 전달·표시 경로를 좁힙니다. 이는 가정한 증거의 판정 연습이며 실제 오류 재현 로그는 아닙니다.

테스트: 게임의 품질 보증

테스트는 게임이 의도한 대로 작동하고, 안정적이며, 플레이어에게 즐거운 경험을 제공하는지 확인하는 과정입니다.

개발 중 테스트 (Developer Testing)

  • 유닛 테스트 (Unit Testing)
    • 게임의 가장 작은 단위(단일 함수, 특정 클래스)가 올바르게 작동하는지 확인하는 테스트입니다.
    • 언리얼 엔진은 Automation Testing Framework를 통해 유닛 테스트를 지원합니다. C++로 작성하며, 테스트를 포함할 모듈과 빌드 플래그·실행 컨텍스트를 정해 테스트 케이스를 정의합니다.
    • 엔진의 자동화 테스트 예제와 사용 중인 버전의 모듈 의존성을 확인합니다.
    • Misc/AutomationTest.h: IMPLEMENT_SIMPLE_AUTOMATION_TEST 또는 IMPLEMENT_COMPLEX_AUTOMATION_TEST 매크로를 사용하여 테스트를 정의합니다.
    • Session Frontend -> Automation 탭: 에디터에서 자동화 테스트를 실행하고 결과를 확인할 수 있습니다.
  • 통합 테스트 (Integration Testing)
    • 여러 모듈이나 시스템이 함께 작동할 때 올바르게 연동되는지 확인하는 테스트입니다. (예: 인벤토리 시스템과 UI 시스템이 잘 통합되는지)
  • 엔드 투 엔드 테스트 (End-to-End Testing)
    • 게임의 시작부터 끝까지 전체 흐름이 의도한 대로 작동하는지 확인합니다. 실제 플레이어처럼 게임을 진행하며 테스트합니다.
  • 블루프린트 유효성 검사 (Blueprint Validation)
    • 에디터 유틸리티 위젯이나 Python 스크립트를 사용하여 블루프린트 에셋의 특정 규칙(변수 명명 규칙, 불필요한 노드 여부 등)을 자동으로 검사합니다.

QA (Quality Assurance) 테스트

개발팀 내부 또는 전문 QA 팀에 의해 수행되는 체계적인 테스트입니다.

  • 기능 테스트 (Functional Testing): 모든 게임 기능이 GDD에 따라 올바르게 작동하는지 확인합니다.
  • 호환성 테스트 (Compatibility Testing): 다양한 하드웨어 구성, 운영 체제, 드라이버 환경에서 게임이 올바르게 작동하는지 확인합니다. (특히 PC 게임)
  • 성능 테스트 (Performance Testing): 목표 프레임 속도, 로딩 시간, 메모리 사용량 등 성능 지표를 측정하고 목표를 달성하는지 확인합니다. (Stress Test, Load Test 포함)
  • 안정성 테스트 (Stability Testing): 장시간 플레이, 반복적인 액션, 극단적인 상황에서도 게임이 충돌하지 않고 안정적으로 작동하는지 확인합니다. (크래시 테스트)
  • 멀티플레이어 테스트: 네트워크 지연, 패킷 손실, 동시 접속자 수 증가 등 다양한 네트워크 환경에서 동기화 및 안정성을 테스트합니다.
  • 사용성 테스트 (Usability Testing): 게임의 UI/UX가 직관적이고 사용하기 편리한지 실제 사용자의 피드백을 통해 검증합니다.
  • 지역화 테스트 (Localization Testing): 게임의 모든 텍스트, 음성 등이 번역된 언어로 올바르게 표시되고 작동하는지 확인합니다.
  • 규정 준수 테스트 (Compliance Testing): 플랫폼별 가이드라인(예: 콘솔 인증, 모바일 스토어 정책)을 준수하는지 확인합니다.

플레이테스트 (Playtesting)

  • 목적: 게임의 재미, 밸런스, 플레이어 경험 등 정성적인 부분을 평가하기 위해 실제 플레이어들에게 게임을 시켜보고 피드백을 수집합니다.
  • 방법: 내부 팀원, 친구, 또는 외부 베타 테스터 그룹을 모집하여 진행합니다.
  • 피드백 수집: 설문조사, 인터뷰, 직접 관찰, 인게임 데이터 로깅 등을 통해 피드백을 수집하고 분석합니다.

버그 트래킹과 관리

  • 버그 트래킹 시스템: Jira, Trello, Asana, MantisBT 등 전용 버그 트래킹 시스템을 사용하여 발견된 모든 버그를 기록하고 관리합니다.
    • 필수 정보: 버그 제목, 재현 단계, 예상 결과, 실제 결과, 스크린샷/영상, 발생 빈도, 중요도, 담당자, 상태(새로 생성, 할당됨, 수정됨, 검증됨, 닫힘 등).
  • 재현 가능성: 버그를 정확히 재현할 수 있는 명확한 단계를 작성하는 것이 가장 중요합니다.
  • 우선순위: 버그의 심각도(게임 진행 불가, 크래시)와 발생 빈도에 따라 우선순위를 부여하고 수정 계획을 세웁니다.
  • 회귀 테스트 (Regression Testing): 버그 수정 후, 해당 수정으로 인해 다른 기능에서 새로운 버그가 발생하지 않았는지 확인하는 테스트입니다.

디버깅과 테스트 결과는 버그 리포트, 자동화 테스트, 회귀 확인으로 이어져야 같은 문제가 다시 숨어들 가능성을 낮출 수 있습니다.


디버깅과 테스트는 게임 개발 과정에서 끊임없이 반복되어야 하는 필수적인 활동입니다.

언리얼 엔진이 제공하는 강력한 내장 디버깅 도구와 외부 IDE의 기능을 적극적으로 활용하여 코드 레벨의 문제를 해결하고, 유닛 테스트부터 QA 테스트, 그리고 플레이테스트에 이르기까지 다양한 테스트 전략을 통해 게임의 기능, 성능, 안정성, 그리고 재미를 검증해야 합니다.

효과적인 버그 트래킹과 관리는 팀의 생산성을 높이고, 궁극적으로 플레이어에게 고품질의 게임을 제공하는 데 기여할 것입니다.

증상먼저 볼 증거수정 후 확인
C++ 크래시동일 빌드 심볼·콜스택·로그최소 재현과 주변 경로
Blueprint 분기대상 인스턴스·Breakpoint·Watch같은 입력·상태 순서
AI 정지Blackboard·현재 Task·NavMesh목표 변경·실패·Abort
성능·메모리Timing/GPU·Memory Insights같은 장치·장면·품질의 수치

버그 기록에 예상 결과와 관측 결과를 분리합니다. 수정 전의 실패를 보존하고 동일 조건의 재검사와 필요한 회귀 확인을 연결해야 합니다.