Unreal 개발 본 캠프 43일차

HappyCircle·2026년 1월 28일

Unreal 개발

목록 보기
60/163

📘 TIL – 성격 유형 검사 문제 리팩터링 정리 (C++)

오늘은 카카오 성격 유형 검사 문제를 풀고,
“이미 통과한 코드인데 더 깔끔하게 만들 수 있는가?”를 기준으로 리팩터링을 진행했다.

이 문제는 알고리즘 자체보다
👉 점수 누적 방식과 코드 구조를 얼마나 명확하게 표현했는지가 중요한 문제였다.

🧩 문제 성격 정리

질문 수 최대 1000

각 질문은 1개의 성격 지표만 판단

선택지에 따라 고정 점수(1~3점) 부여

모든 질문 점수를 누적한 뒤

각 지표에서 점수가 높은 성격 유형 선택

동점이면 사전순으로 빠른 유형 선택

👉 따라서 이 문제는
DP / Greedy / Graph 같은 알고리즘이 아니라

구현 + 시뮬레이션 + 점수 누적(카운팅) 문제

시간복잡도도 자연스럽게 O(n) 이 최선이다.

✅ 최초 통과 코드 (요약)

unordered_map<char, int>로 성격 점수 관리

choices 값에 따라 점수 누적

마지막에 "RT", "CF", "JM", "AN" 쌍 비교

문제는 정답, 성능도 충분했음.

하지만:

분기가 조금 많고

같은 의미의 비교가 반복되고

“동점이면 사전순” 조건이 코드에 잘 드러나지 않음

🔧 리팩터링 목표

기존 코드 구조 최대한 유지

로직 의미가 더 잘 드러나게

불필요한 조건 분기 제거

“동점 → 사전순” 규칙을 코드에서 한 줄로 표현

✨ 최소 수정 + 더 깔끔한 리팩터링 코드

#include <string>
#include <vector>
#include <unordered_map>
using namespace std;

string solution(vector<string> survey, vector<int> choices) {
    string answer = "";

    unordered_map<char,int> check = {
        {'R',0},{'T',0},{'C',0},{'F',0},
        {'J',0},{'M',0},{'A',0},{'N',0}
    };

    vector<string> answer_check = {"RT","CF","JM","AN"};

    const int n = (int)survey.size();

    for(int i = 0; i < n; i++){
        const int num = choices[i];

        if(num < 4){
            check[survey[i][0]] += 4 - num;   // 1→3, 2→2, 3→1
        }
        else if(num > 4){
            check[survey[i][1]] += num - 4;   // 5→1, 6→2, 7→3
        }
        // num == 4 → 점수 없음
    }

    for(const string& s : answer_check){
        const char c1 = s[0];
        const char c2 = s[1];

        // 동점이면 사전순 빠른 쪽
        answer += (check[c1] >= check[c2]) ? c1 : c2;
    }

    return answer;
}

🔍 리팩터링 포인트 정리
1️⃣ 분기 구조 정리

기존:

if(num <= 3) ...
if(num >= 5) ...

변경:

if(num < 4) ...
else if(num > 4) ...

선택지는 1~3 / 4 / 5~7로 완전히 구분됨

else if로 의미가 더 명확해짐

4(모르겠음)은 자연스럽게 아무 처리도 하지 않음

2️⃣ 반복 비교 제거 (동점 처리 개선)

기존:

if(a > b) ...
if(a < b) ...
if(a == b) ...

변경:

answer += (check[c1] >= check[c2]) ? c1 : c2;

비교를 한 번만

answer_check = {"RT","CF","JM","AN"} 구조 덕분에
→ c1이 항상 사전순으로 빠름

그래서 >= 하나로 동점 처리까지 포함

👉 문제 조건이 코드 한 줄에 그대로 드러나는 구조

3️⃣ const 사용

const int n

const int num

const char c1, c2

값이 변하지 않는다는 의도가 명확해지고
읽는 사람이 로직을 추적하기 쉬워짐.

📘 언리얼엔진 CI/CD, 빌드 산출물, 디버깅 & Profiling 정리

오늘은 언리얼엔진 프로젝트를 “어떻게 빌드하고, 어떻게 배포하고, 어떻게 디버깅/프로파일링까지 이어지는지”를 하나의 흐름으로 정리했다.

단순히 버튼 위치를 외우는 게 아니라
👉 CI/CD 관점에서 어떤 옵션이 어떤 의미를 가지는지를 이해하는 데 초점을 맞췄다.

🔁 CI / CD란?
CI (Continuous Integration)

지속적인 통합

코드 변경 사항을 자주 빌드하고 검증하는 과정

컴파일 에러, 링크 에러, 기본 테스트를 빠르게 발견하는 목적

CD (Continuous Distribution / Deployment)

지속적인 배포

빌드 산출물을 실제 실행 가능한 결과물로 만들어 배포

언리얼에서는 플랫폼별 패키징까지 포함되는 개념

📦 언리얼 빌드 산출물(Artifact) 개념

빌드 결과로 생성되는 실행 파일, pak 파일, 리소스 구조 전체를 통칭해서 Artifact라고 부른다.

지원 플랫폼 (Content / SDK / Device Management)

Android

iOS

Linux / LinuxArm64

Mac

tvOS

Windows (Win64)

👉 플랫폼이 달라지면 SDK + 패키징 구조 + 실행 파일이 전부 달라진다.

📁 Content Management 옵션
1️⃣ Package Project

프로젝트 전체 빌드

코드 컴파일 + 리소스 쿠킹 + 패키징까지 전부 수행

실제 배포용 빌드에서 사용

2️⃣ Cook Content

리소스만 쿠킹

코드 변경 없이 콘텐츠만 수정했을 때 유용

CI 단계에서 빠른 검증용으로 활용 가능

Cook = Resource Cooking
→ 게임에서 사용하기 최적화된 형태로 리소스를 가공

⚙️ Binary Configuration (빌드 설정)
Use Project Setting

프로젝트 설정을 그대로 사용

Development

개발용 빌드

최적화가 들어가서 변수 추적이 잘 안됨

실 디버깅에는 거의 안 씀

DebugGame

디버깅용 빌드

게임 로직 디버깅에 가장 적합

Shipping

출시용 빌드

디버그 코드 제거

Assertion, 로그 대부분 비활성화

👉 CI에서는 Development / DebugGame
👉 CD(배포)에서는 Shipping이 일반적

🐞 실행 중 디버깅 방법 (Attach 방식)

단축키 CTRL + ALT + P

프로세스 필터에 Unreal 검색

실행 중인 프로세스 선택

우측 하단 연결(Attach) 클릭

👉 에디터 실행 중인 상태에서도
Visual Studio에서 직접 브레이크포인트 잡고 디버깅 가능

🧪 CLI 환경 – UAT(Unreal Automation Tool)

CI/CD에서 가장 핵심이 되는 부분.

"\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun ^
-project="D:\Proj\MyGame\MyGame.uproject" ^
-noP4 ^
-platform=Win64 ^
-clientconfig=Shipping ^
-build -cook -stage -pak ^
-archive -archivedirectory="D:\Builds\MyGame_Win64"

주요 옵션 정리

project
→ 빌드 대상 .uproject 경로

platform
→ 대상 플랫폼 (Win64, Android 등)

clientconfig
→ 빌드 설정(DebugGame / Shipping 등)

build
→ C++ 소스 코드 컴파일

cook
→ 리소스 쿠킹

stage
→ 플랫폼별 실행 구조로 정리 (Saved 폴더)

pak
→ 콘텐츠를 .pak 파일로 묶음

archive
→ 결과물을 지정 폴더로 복사/보관

archivedirectory
→ 최종 산출물 저장 위치

👉 CI/CD 자동화에서는 RunUAT가 사실상 핵심 도구

🛑 Assertion (검증 매크로)

언리얼에서는 Exception 대신 Assertion 매크로를 적극 사용한다.

check 계열 (치명적)

조건이 false면 즉시 Crash

“절대 발생하면 안 되는 상태” 강제

check(Expression);
checkf(Expression, TEXT("Message"));
checkNoEntry();
checkNoReentry();
checkNoRecursion();
unimplemented();

Shipping 빌드에서는 기본적으로 비활성화

빠르게 버그를 터뜨려 원인 추적 비용 감소

verify 계열

check와 동일하지만 Shipping에서도 표현식은 평가

사이드 이펙트가 있는 함수 검증용

ensure 계열 (비치명적)

false여도 보통 크래시 없이 계속 진행

로그 + 콜스택 기록

디버깅 중에는 브레이크 걸리고 F5로 진행 가능

ensure(Expression);
ensureAlways(Expression);
ensureMsgf(Expression, TEXT("Message"));

👉 “이상하긴 한데 당장 죽일 정도는 아님”일 때 사용

📊 Profiling
Stat 명령어 (실시간 확인)

콘솔에서:

Stat FPS
Stat UNIT
Stat Game
Stat GPU
Stat CPUStalls

👉 프레임 단위 성능 병목을 빠르게 확인

🔍 Unreal Insights
Trace 활성화

에디터 상단에서 빨간 버튼으로 기록 시작/중지

Insights Frontend

저장된 .utrace 파일 목록 확인

LIVE 트레이스도 확인 가능

더블클릭으로 분석 시작

Timing Insights

CPU 실행 시간 분석

프레임 단위로 어떤 함수가 얼마나 오래 걸리는지 확인

Memory Insights

기본적으로 꺼져 있음

명시적으로 활성화 필요

VS2022
-trace=default,memory

Rider

실행 인수에 동일하게 추가

🧠 Profiling 매크로 정리
CPU 시간 누적 (Stat 기반)
DECLARE_STATS_GROUP(TEXT("MyGroup"), STATGROUP_MyGroup, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("MyFunc"), STAT_MyFunc, STATGROUP_MyGroup);

SCOPE_CYCLE_COUNTER(STAT_MyFunc);

Stat MyGroup으로 확인

Unreal Insights 이벤트 마커
TRACE_CPUPROFILER_EVENT_SCOPE(MyScope);

Insights에서 타임라인 구간 표시

메모리 태그 추적 (LLM)
LLM_DEFINE_TAG(MyTag);
LLM_SCOPE_BYTAG(MyTag);

어떤 시스템이 메모리를 쓰는지 태그별로 확인 가능

🚀 빌드 산출물에서 Trace 실행

패키징된 실행 파일에서도 가능.

.\Game.exe "-trace=default" "-tracefile=C:\Traces\test.utrace"

.\Game.exe "-trace=default,memory" "-tracefile=C:\Traces\mem.utrace"

생성된 .utrace 파일을
👉 Unreal Insights Frontend에 드래그하면 분석 가능

✨ 오늘의 정리 포인트

언리얼 CI/CD는 RunUAT 중심

빌드 옵션 하나하나가 자동화 파이프라인의 단계

Assertion은 “방어 코드”가 아니라 디버깅 비용 절감 도구

Profiling은

실시간(Stat)

정밀 분석(Insights)
두 축으로 나눠서 접근

profile
개발합시다!

0개의 댓글