이전에 실패한 코드
#include <string>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
int getNodeCnt(const vector<vector<int>>& graph, int startNode, vector<bool>& visited, pair<int, int> excludeLine)
{
int nodeCnt = 0;
queue<int> nextNodes;
nextNodes.push(startNode);
visited[startNode] = true;
while(!nextNodes.empty())
{
int nowNode = nextNodes.front();
nextNodes.pop();
for(int newNode : graph[nowNode])
{
if(visited[newNode])
{
continue;
}
if(nowNode == excludeLine.first && newNode == excludeLine.second)
{
continue;
}
else if(nowNode == excludeLine.second && newNode == excludeLine.first)
{
continue;
}
nextNodes.push(newNode);
visited[newNode] = true;
nodeCnt++;
}
}
return nodeCnt;
}
int solution(int n, vector<vector<int>> wires) {
int answer = 100;
vector<vector<int>> graph(100, vector<int>());
for(vector<int>& row : wires)
{
int startNode = row[0] - 1;
int endNode = row[1] - 1;
graph[startNode].push_back(endNode);
graph[endNode].push_back(startNode);
}
for(int i = 0; i < graph.size(); i++)
{
for(int j = 0; j < graph[i].size(); j++)
{
vector<bool> visited(graph.size(), false);
pair<int, int> excludeLine = make_pair(i, j);
int cnt1, cnt2;
cnt1 = getNodeCnt(graph, i, visited, excludeLine);
cnt2 = getNodeCnt(graph, j, visited, excludeLine);
answer = min(answer, abs(cnt1 - cnt2));
}
}
return answer;
}
바닥 노드에서 올라오며 기록한다 == DFS를 활용한 서브트리 크기 구하기
이 방식이 좋은 이유
트리에서 간선 하나를 끊으면 무조건 두 개의 전력망으로 나뉩니다. 이때 한쪽 전력망의 노드 개수를 S라고 하면, 나머지 한쪽은 반드시 N - S 가 됩니다.
따라서 모든 간선을 일일이 끊어보고 BFS를 매번 돌릴 필요가 없습니다.
Visited가 필요없다는 의미
일반적인 그래프에서는 사이클(순환)이 있을 수 있어 방문한 곳을 다시 가지 않기 위해 visited 배열이 필수, 하지만 트리(Tree)는 사이클이 없는 구조이기에 부모 노드에서 자식 노드로 내려가는 상황이라면 "내가 방금 왔던 노드(부모)"만 다시 가지 않으면 무조건 새로운 노드로 가게 됩니다.
통과 코드
#include <string>
#include <vector>
#include <cmath>
using namespace std;
int DFS(const vector<vector<int>>& graph, int curr, int parent, int& answer, const int total)
{
int subTreeSize = 1; // self
for(int next : graph[curr])
{
if(next == parent)
{
continue;
}
int childTreeSize = DFS(graph, next, curr, answer, total);
int otherSize = total - childTreeSize;
answer = min(answer, abs(childTreeSize - otherSize));
subTreeSize += childTreeSize;
}
return subTreeSize;
}
int solution(int n, vector<vector<int>> wires) {
int answer = 100;
vector<vector<int>> graph(n + 1, vector<int>());
for(vector<int>& row : wires)
{
int startNode = row[0] - 1;
int endNode = row[1] - 1;
graph[startNode].push_back(endNode);
graph[endNode].push_back(startNode);
}
DFS(graph, 0, -1, answer, n);
return answer;
}
Rider에서는 Makrdown을 지원하는 Plugin이 존재하고 있음에도 md 파일에 대한 Priview가 화면에 나타나지 않는 오류가 발생 중이었습니다.
해당 문제를 해결한 과정을 서술합니다.


다시 켜진 에디터를 보니 정상적으로 MD 파일의 Priview가 나오고 있습니다.

해당 방법을 찾은 링크
https://stackoverflow.com/questions/74523328/markdown-plugin-for-intellij-not-showing-preview
당신은 Unreal Engine 멀티플레이 게임 전문 QA 엔지니어입니다.
README.md 파일의 내용과 프로젝트의 소스 파일들을 확인 하고,
특히 네트워크 동기화, 세션 처리, RPC 관련 케이스를 빠뜨리지 말아주세요.
최소 30개의 테이스가 작성되어야 하며 각 테스트 케이스를 QA_CheckList.md 파일로 만들어주었으면 합니다.
각 테스트 케이스에 대한 플로우 차트를 QA_FlowChart.md 파일로 만들어 주었으면 합니다.
[영역]
싱글플레이, 멀티플레이
[카테고리]
기능, 네트워크, UI, 성능, 크래시
[우선순위]
P0-Critical, P1-High, P2-Medium, P3-Low
[출력형식]
| 영역 | 카테고리 | 우선순위 | 테스트케이스 |
결과 요약
QA_CheckList.md
총 40개 테스트 케이스 작성 (최소 30개 요건 충족)
┌─────────────┬─────────┬─────────────────────────────────────────────────────────────────────────────────────────┐
│ 우선순위 │ 케이스 │ 주요 내용 │
│ │ 수 │ │
├─────────────┼─────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ P0-Critical │ 9개 │ 세션 참가, 호스트 강제종료, 팀 전원 사망, ReviveCount 동기화, 아이템 픽업 동기화, 레벨 │
│ │ │ 전환 크래시, 퍼즐 동기화, 공격 어빌리티, 게임 클리어 │
├─────────────┼─────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ P1-High │ 11개 │ 음성 채팅 유지, 파쿠르 동기화, 부활 칩, 연료 동기화, Steam Invite, 카오스 파괴, │
│ │ │ 인벤토리 사이클, 관전 모드, Local Prediction, 클라이언트 비정상 종료, AI 인지 │
├─────────────┼─────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ P2-Medium │ 12개 │ 커스터마이징, 레벨 전환 유지, UI 정확성, 퍼즐 타입, 자판기, 네임플레이트, 파쿠르, AI │
│ │ │ 스폰, 탐사기록, 성능 │
├─────────────┼─────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ P3-Low │ 8개 │ 설정 저장, VFX 성능, 뮤트/언뮤트, 비밀번호, DataTable 안전 실패, RestRoom 통계, │
│ │ │ 스태미나, Ready 시스템 │
└─────────────┴─────────┴─────────────────────────────────────────────────────────────────────────────────────────┘
---
QA_FlowChart.md
40개 전 테스트 케이스에 대한 Mermaid 플로우차트 포함
- ✅ / ❌ / ⚠️ 분기 구조로 합격/불합격/경고 경로 명확히 구분
- 네트워크 관련 케이스는 서버/클라이언트 동기화 검증 분기 포함
- GAS 케이스는 어빌리티 발동 조건 및 롤백 분기 포함
---
특이사항
테스트 환경 섹션에 UE5 콘솔 명령 포함:
Net PktLag=200 -- 고레이턴시 시뮬레이션
showdebug abilitysystem -- GAS 상태 시각화
stat net -- 네트워크 통계
전체 파일을 확인하여 테스트 케이스를 추가해주었으면 합니다.
추가 완료 요약
추가된 테스트 케이스: TC-041 ~ TC-072 (32개)
┌─────────────┬──────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ │ 추가 │ │
│ 우선순위 │ 케이스 │ 주요 대상 시스템 │
│ │ 수 │ │
├─────────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ P0-Critical │ 1개 │ 기차 연료 소진 → 스테이지 강제 실패 │
├─────────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ P1-High │ 11개 │ Kidnap 동기화, Stalker 천장 전환, 기차 연료/도어, PlayerState 지속성, AutoReviveQueue, │
│ │ │ 사망 뮤트, 세션 IN_GAME 필터, 스태미나 Lockout │
├─────────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ │ │ 납치 DoT/쿨다운, Werewolf PackCoord, Bull 돌진/쿨다운, AttributeSet 속도, 퍼즐 │
│ P2-Medium │ 17개 │ HoldActive/블로킹/TimeLimit/순서, DamageVolume, 패시브 아이템, Gait 동기화, AI │
│ │ │ NavMesh/소리기억, 오디오 설정, 홀드 인터랙션, 사망 드롭 │
├─────────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ P3-Low │ 3개 │ 퍼즐 OneTime 재시도, FindSessions 중복 가드, SetToDefaults │
└─────────────┴──────────┴────────────────────────────────────────────────────────────────────────────────────────┘
전체 누계
┌────────────────────────┬───────────┐
│ 구분 │ 케이스 수 │
├────────────────────────┼───────────┤
│ 기존 (TC-001 ~ TC-040) │ 40개 │
├────────────────────────┼───────────┤
│ 추가 (TC-041 ~ TC-072) │ 32개 │
├────────────────────────┼───────────┤
│ 총합 │ 72개 │
└────────────────────────┴───────────┘
주요 추가 커버리지
- AO_KidnapComponent — CurrentVictim 리플리케이션, DoT, 쿨다운
- AO_PackCoordComp — 하울 전파, 포위 포지션 예약 타임아웃
- AO_Bull / AO_Stalker — 특수 AI 전투 패턴
- AO_Train / AO_TrainDoor — 연료 시스템, 도어 OnRep
- AAO_PlayerState — 레벨 전환 간 인벤토리/체력 지속성
- AO_PuzzleConditionChecker — HoldActive, BlockingTag, TimeLimit, OrderedTags
- AO_DamageVolume — GAS Effect 진입/이탈 동기화
- UAO_GameSettingsManager — 오디오 타입별 독립 조절
- AO_OnlineSessionSubsystem — bFinding 가드, KEY_IN_GAME 필터
현재 테스트케이스는 플레이어 입력에 대한 피드백 테스트(캐릭터, UI 등)이 존재하지 않는 것으로 보입니다. 그 외에도
누락된 테스트케이스가 존재하는 것으로 보이니 테스트 케이스의 추가를 요청합니다.
추가 완료 요약 (TC-073 ~ TC-097, 25개)
입력 피드백 — 캐릭터 (신규)
┌────────┬──────────┬──────────────────────────────────────────────────┬──────────────────────────────────────────┐
│ # │ 우선순위 │ 테스트케이스 │ 소스 근거 │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-073 │ P1 │ 공격 입력 → 몽타주 + GameplayCue 피해 이펙트 + │ GA_MeleeAttack, GA_MeleeHitConfirm │
│ │ │ 피해 수치 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-074 │ P1 │ 점프 입력 → 공중 애니메이션 + 착지 사운드 + │ GA_Jump, LandVelocity 리플리케이션 │
│ │ │ bJustLanded 타이머 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-075 │ P1 │ 피격 시 → 방향별 HitReact 몽타주 + 무적 프레임 + │ GA_HitReact 방향 분기 │
│ │ │ 화면 비네트 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-076 │ P2 │ 스프린트 → 속도 즉시 증가 + 스태미나 2f/초 감소 │ GA_Sprint, StaminaCost=2.0f │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-077 │ P2 │ 웅크리기 → 자세 전환 + 캡슐 콜리전 조정 + 좁은 │ AO_PlayerCharacter Crouch │
│ │ │ 공간 통과 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-078 │ P2 │ 사망 → Death 몽타주 + 래그돌 + Dead 태그 + │ GA_Death, 카메라 오프셋 300f │
│ │ │ 카메라 오프셋 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-079 │ P2 │ 아웃라인 어빌리티 → CustomDepth 외곽선 + 시간 │ GA_Outline │
│ │ │ 만료 제거 │ │
├────────┼──────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────┤
│ TC-080 │ P2 │ 발소리 → 표면 GameplayTag 기반 Foley 사운드 구분 │ AO_FoleyAudioBank, │
│ │ │ │ IAO_FoleyAudioBankInterface │
└────────┴──────────┴──────────────────────────────────────────────────┴──────────────────────────────────────────┘
입력 피드백 — UI (신규)
┌────────┬──────────┬─────────────────────────────────────────────────────┬───────────────────────────────────────┐
│ # │ 우선순위 │ 테스트케이스 │ 소스 근거 │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-081 │ P1 │ 체력 피해 → AO_HealthWidget 실시간 감소 + 색상 변화 │ ASC OnAttributeChanged 이벤트 │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-082 │ P1 │ 스프린트 → AO_StaminaWidget 실시간 감소 + 회복 │ OnRep_Stamina │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-083 │ P1 │ 인터랙션 접근/이탈 → AO_InteractionWidget 팝업/소멸 │ FAO_InteractionMessage 브로드캐스트 │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-084 │ P2 │ 슬롯 스크롤 → OnRep_SelectedIndex 하이라이트 즉시 │ ServerSetSelectedSlot │
│ │ │ 변경 + 랩어라운드 │ │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-085 │ P2 │ 아이템 픽업 → Multicast_PlayInventorySound + 슬롯 │ OnInventoryUpdated 리스너 │
│ │ │ 즉시 갱신 │ │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-086 │ P2 │ 아이템 사용 → 사운드 + 수량 즉시 감소 + VFX │ UseInventoryItem_Server │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-087 │ P2 │ 아이템 드롭 → 드롭 사운드 + 발 앞 스폰 + 슬롯 │ DropInventoryItem_Server │
│ │ │ 초기화 │ │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-088 │ P2 │ 음성 발화 → AO_NameTagWidget VOIP 아이콘 표시/소멸 │ IsRemotePlayerTalking │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-089 │ P2 │ 사망 후 관전 → AO_SpectateWidget 대상 체력/스태미나 │ AO_SpectateWidget │
│ │ │ 실시간 표시 │ │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-090 │ P2 │ 스태미나 25% 잠금 → AO_StaminaWidget 경고 색상 전환 │ StaminaLockoutPercent=0.25f │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-091 │ P3 │ 세션 목록 페이지네이션 5개씩 + 이전/다음 버튼 │ AO_LobbyListWidget │
├────────┼──────────┼─────────────────────────────────────────────────────┼───────────────────────────────────────┤
│ TC-092 │ P3 │ 로비 레디 보드 Ready 상태 실시간 갱신 │ AO_LobbyReadyBoardWidget, │
│ │ │ │ OnRep_LobbyIsReady │
└────────┴──────────┴─────────────────────────────────────────────────────┴───────────────────────────────────────┘
기타 누락 케이스 (신규)
┌────────┬──────────┬───────────────────────────────────────────────────────────────────────────────────────┐
│ # │ 우선순위 │ 테스트케이스 │
├────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ TC-093 │ P2 │ 세이프존(AO_InventorySaveZone) 진입/이탈 인벤토리 보존 │
├────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ TC-094 │ P2 │ 커스터마이징 변경 → 다른 플레이어 실시간 표시 (ServerRPC_SetCharacterCustomizingData) │
├────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ TC-095 │ P2 │ 빠른 연속 입력 → GAS 어빌리티 충돌 크래시 없음 │
├────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ TC-096 │ P3 │ 늦은 참가(Late Join) → GameState 기존 상태 동기화 │
├────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────┤
│ TC-097 │ P3 │ Insect 납치 투척 → 물리 임펄스 적용 + 착지 후 이동 복구 │
└────────┴──────────┴───────────────────────────────────────────────────────────────────────────────────────┘
---
전체 누계
┌───────────────────────┬───────────┐
│ 구분 │ 케이스 수 │
├───────────────────────┼───────────┤
│ 1차 (TC-001 ~ TC-040) │ 40개 │
├───────────────────────┼───────────┤
│ 2차 (TC-041 ~ TC-072) │ 32개 │
├───────────────────────┼───────────┤
│ 3차 (TC-073 ~ TC-097) │ 25개 │
├───────────────────────┼───────────┤
│ 총합 │ 97개 │
└───────────────────────┴───────────┘
정리된 QA문서의 일부




정리된 테스트 케이스 중 중요도가 높은 것 & 테스트가 용이한 것을 먼저 처리할 계획입니다.
상태 전환에 따른 처리 로직을 실행할 대 애니메이션을 전환하기 전에 Delay를 주어 기존 애니메이션이 종료될 때가지 기다려줘야 동작의 전환이 자연스럽습니다. - 강의의 내용
그런데 남은 애니메이션의 시간을 어떻게 구해야 할까요?
일단 애니메이션의 길이를 구하는 공식은 다음과 같습니다.
애니메이션 프레임 수(FrameCounts) / 애니메이션 FPS
아니면 애니메이션 에셋에 커서를 올려서 속성이 화면에 띄워질 때 Sequence Length로 확인할 수 있습니다.
물론 이렇게 하면 애니메이션의 전체 시간만 알 수 있고 남은 시간은 알 방도가 없습니다.
EventGraph에서 현재 재생 중인 애니메이션의 남은 시간을 가져오는 방법을 알아보았지만 마땅한 방법이 없는것 같습니다. 굳이 EventGraph에서 처리하지 않고, AnimGraph에서 스테이트 머신의 전환 규칙을 활용하거나, Automatic Rul-Based Transition을 사용하거나, Montage의 델리게이트를 사용하는 등의 방법을 사용합니다.
EvenetGraph에서 시간을 체크해야 할 경우는 AnimNotify로 종료 시점에서 이벤트를 호출하게 하는 것이 좋다고 합니다.
강의에서 처럼 Tick/Delay의 사용하는 것은 지양되고 있습니다.
EventGraph에서도 좋은 방법이 있는 지 알아본 결과는 "없다" 입니다.
강의에서도 뒷부분에 그냥 이런 식으로 흐름이 진행되는 것이라는 것을 이해시키기 위해 이렇게 했을 뿐이라는 내용이 나오는 군요.
효율적인 상태 변환 방법
1. 스테이트 머신의 전환 규칙을 활용
스테이트 머신 내부의 Transition Rule(전환 규칙) 안에서는 남은 시간을 즉시 계산해 주는 전용 노드를 사용할 수 있습니다. 가장 표준적인 방식이라고 합니다.
Get Relevant Anim Time Remaining (Ratio): 현재 상태에서 재생 중인 애니메이션의 남은 시간 비율을 0~1 사이로 반환합니다.
Get Relevant Anim Time Remaining: 비율이 아닌 실제 “초(Seconds)” 단위로 남은 시간을 반환합니다.
전환이 부자연스럽다면 남은 시간 문제보다는 Blend Settings의 Duration(전환 시간)이나 Blend Option을 조절하여 애니메이션 간의 연결을 부드럽게 만드는 것이 추천됩니다.
좀 더 정리하자면
단순히 상태를 넘기기 == Automatic Rule-Based Transition
특정 로직(적의 Elimination 처리 등)을 실행 == Anim Montage의 On Completed 또는 Anim Notify를 사용
이 가장 정확한 방법입니다.
에디터가 픽셀을 제어하고 나이아가라(Niagara)가 파티클을 제어하듯이, 메타사운드는 오디오 데이터를 샘플 단위로 정밀하게 제어할 수 있게 해준다고 합니다.
이전 사운드 큐에서는 가청 범위 밖에서 소리가 재생된 경우, 가청범위로 뒤늦게 집입하면 소리가 들리지 않았습니다. 이는 재생 시점에 소리가 픽스되어서 생기는 문제라고 합니다. 하지만 메타 사운드에서는 다릅니다,
주요 특징 및 장점
고성능 비동기 렌더링 : 메타사운드 그래프는 게임 스레드와 분리된 오디오 렌더링 스레드에서 비동기적으로 실행됩니다. 최적화된 C++ 코드로 자동 변환되어 실행되므로 매우 효율적입니다.
샘플 정확도(Sample-accurate) : 이전 시스템인 사운드 큐와 달리 샘플 단위의 정밀한 타이밍 제어가 가능합니다. 이는 리듬 게임이나 복잡한 절차적 음악 생성에 필수적입니다.
절차적 오디오 생성 : .wav 파일 같은 샘플뿐만 아니라 노이즈 생성기, 오실레이터 등을 사용하여 처음부터 끝까지 코드로 소리를 합성할 수 있습니다.
동적 상호작용 : 게임플레이 데이터(속도, 거리, 체력 등)를 입력 파라미터로 받아 실시간으로 사운드의 높낮이, 필터, 구조를 변경할 수 있습니다.
사운드 큐(Sound Cue)와의 차이점
|특징|사운드 큐 (Legacy)|메타사운드 (Next Gen)|
|-|-|-|
|타이밍|프레임 기반 (부정확할 수 있음)|샘플 기반 (매우 정밀함)|
|성능|오디오/게임 스레드 종속성 있음|비동기식, 고도로 최적화된 C++ 객체|
|기능|단순 재생 및 기본 변형|복잡한 합성, 필터링, 로직 포함 가능|
|확장성|제한적임|노드 구성을 통한 높은 재사용성 및 확장성|
개발자가 고려해야 할 사항