TIL 2026-05-19
2026-05-19 CS 27 메모리 단편화 신규 + 좀비 라그돌·히트스톱 PR #34 머지
목차
- 2026-05-19 CS 27 메모리 단편화 신규 + 좀비 라그돌·히트스톱 PR #34 머지
오늘 한 일 요약
- 5/18 TIL 정리 완료 —
5월/2026-05-18.md(10:58). 5/18 세션이 빌드 에러로 중단되어 오늘 오전에 마무리. 좀비 라그돌 본 폴백·HitReact ExcludedBones 확장·캡슐 ECC_Pawn 일시 Ignore·CS 25 꼬리질문 4건·CS 26 페이지 폴트 신규까지 604줄 - 2026-05-19 스크럼 작성 —
scrum/2026-05-19.md(11:25 수정). 어제 한 일(좀비 패치 ①②③ + CS 25 보강 + CS 26 신규) + 오늘 할 일(CS 27·빌드 에러·의심2 패치·PIE 검증) - CS 27 — 메모리 단편화 신규 작성 —
raw/cs-notion/27_memory_fragmentation.md(10:41). 외부/내부 단편화 → 측정 → Compaction·Paging·Buddy·Slab·Pool·Arena → ptmalloc·jemalloc·tcmalloc 비교 → 언리얼FMemory패턴 → 페이지 폴트 26번 회귀. 16개 섹션, 정책대로 예상 문제 슬롯은 비워둠 - 외부 프로젝트 develop 빌드 에러 해결 — 5/18 중단됐던 빌드 에러를 잡고 cpp 3개(WeaponComponent·HitReactComponent) + 테스트맵 2개(
Content/{Combat,Level}/TestMap1.umap) 빌드 통과 - 외부 프로젝트 커밋 + 푸시 —
81a57f1 fix(combat): 좀비 라그돌·히트스톱 안정성 패치 + 테스트맵(17:32). 4 files changed, 106 insertions(+), 16 deletions(-). 브랜치feat/combat-hit-damage-system - PR #34 머지 → develop 병합 —
e0e6358 Merge pull request #34(17:34). 좀비 라그돌·히트스톱 안정성 패치가 develop에 정식 반영. 같은 시각 PR #32(feat/Player무기 에셋), PR #33(feat/system-HUDUI 마무리)도 함께 머지 - Bootcamp-TIL 커밋 흐름 정정 — TIL·코드카타 4개 파일을 두 커밋으로 만들었다가 외부 프로젝트가 우선이라는 판단으로
reset --mixed로 되돌림. working tree 보존되어 내일 다시 정리 가능
미해결 — 히트스톱 의심2 패치(HitStopTimers 멤버 맵) 미적용, PIE 검증 미완료, 24번 floating_point 정밀도 보강·graphify CS 그래프 풀 빌드·Bootcamp-TIL 미커밋 4개(이월).
작업 환경
- 외부 프로젝트 —
D:\Unreal\8th-Team11-CH3-Project(Ch3 팀플, 발표 2026-05-26 잔여 6일) - 수정 파일(외부) — 어제 작업한 cpp 2개 + 테스트맵 2개
Source/NBC_Ch3_TeamProject/Private/Combat/WeaponComponent.cppSource/NBC_Ch3_TeamProject/Private/Combat/HitReactComponent.cppContent/Combat/TestMap1.umapContent/Level/TestMap1.umap
- CS 자료(본 레포) —
raw/cs-notion/27_memory_fragmentation.md신규 (66KB, 16개 섹션) - PR — 외부
#34 feat/combat-hit-damage-system → develop머지 완료
오전 — 5/18 TIL 정리 마무리·스크럼·CS 27 신규
1. 5/18 TIL 정리 완료
5/18 세션이 develop 빌드 에러로 중단되면서 그날 분량 TIL은 오늘 오전에 정리됐다. 좀비 라그돌 본 폴백(Hips/Spine 루트성 본에서 자식 본 6단계 탐색), HitReact ExcludedBones 양쪽 표준 9개 확장(Mixamo + UE5), ApplyHitStop의 캡슐 ECC_Pawn 응답 일시 Ignore + 원래 응답 복원 패턴, CS 25 꼬리질문 4건(공간지역성·vector 지역성·캐시 라인 64B·Intel VTune 흐름), CS 26 페이지 폴트(3종 분류·present bit·TLB miss 경계·언리얼 Asset Streaming)까지 한 파일에 묶었다.
오늘 작업과의 연결 — 5/18 TIL의 “다음 단계” 섹션이 오늘 스크럼의 “오늘 할 일”로 그대로 들어왔다. 인계 노트(scrum/2026-05-18_히트스톱_인계.md)에 의심 3가지와 의심2 패치 코드를 미리 정리해둔 게 오늘 컨텍스트 복원 비용을 크게 줄였다.
2. 2026-05-19 스크럼 작성
11:25에 마무리. 구조 ——
- 어제 한 일 — Ch3 좀비 사격 후속 버그 ①②③ + CS 25 꼬리질문 4건 + CS 26 페이지 폴트 신규
- 미해결 / 이월 — 히트스톱 효과 약함(의심2 유력)·뒤 좀비 push 잔여·develop 빌드 에러·24번 정밀도 보강
- 오늘 할 일 — CS 27 신규 작성·빌드 에러 해결·의심2 패치(
HitStopTimers멤버 맵)·PIE 진단·의심1/3 검증 - 보류·게이트 — 히트스톱·push·빌드 에러·발표 잔여 7일·NBC_Master Step 2 승인 대기·블로그 마이그레이션·graphify 일괄 반영
스크럼 작성 시 가장 신경 쓴 부분 — “해결 중” 상태 표시(🟡). 완전히 풀린 것도 아니고 손을 놓은 것도 아닌 항목을 명확히 구분해두면 다음 세션 진입이 매끄럽다.
3. CS 27 — 메모리 단편화 신규 작성
raw/cs-notion/27_memory_fragmentation.md 신규. 10:41 작성, 66KB, 16개 섹션. 다음 모의면접 주제 — “메모리 단편화(Memory Fragmentation)에 대해 설명하고 해결 방법을 말해보세요”. 26번 페이지 폴트와 자연스럽게 이어지는 흐름을 의도했다.
작성 정책에 따라 예상 문제 슬롯은 비워둠(메모리 정책 — 강사가 수업 후 내주므로 빈 슬롯으로 대기). 이론·답변·꼬리질문 대비까지만.
목차 골격 ——
- 학습 영역(03·11·26번과의 교차점)
- 모의면접 답변(외부/내부 단편화 정의 → 원인 → 측정 → 해법)
- 꼬리질문 대비 — 외부/내부 차이·Buddy·Slab·Pool/Arena/Frame·ptmalloc/jemalloc/tcmalloc 비교
- 게임 엔진 패턴 — UE5
FMemory백엔드·FMallocBinned2·FMallocTBB·콘솔 메모리 제약 - 26번 회귀 — 단편화 누적이 곧 페이지 폴트 빈도 증가·워킹 셋 증가·콘솔 OOM
본문은 27번 파일 자체에 자세히 있으니 여기서는 핵심 인사이트만 따로 묶었다.
오후 — 외부 프로젝트 빌드 에러 해결 + PR #34 머지
4. develop 빌드 에러 해결
5/18 세션 중단의 직접 원인. 어제 인계 노트의 “재시작 후 첫 메시지 흐름”대로 빌드 에러 로그부터 잡고, 어제 작업한 cpp 2개(WeaponComponent·HitReactComponent)와 테스트맵 2개의 컴파일·쿡 통과 확인.
빌드 통과 후 PIE에서 빠르게 회귀 체크 — 좀비 라그돌은 가슴/옆구리 사격 시 자식 본으로 안전하게 분기(눕는 현상 없음), 단발 사격 시 캡슐 응답 Ignore로 뒤 좀비 push도 차단. 의심2 패치(HitStopTimers 멤버 맵)는 아직 안 들어간 상태라 연사 시 슬로우 약화는 잔여 — 내일 별도 PR로 진행하기로.
5. 외부 프로젝트 커밋 + 푸시
1
2
3
4
81a57f1 fix(combat): 좀비 라그돌·히트스톱 안정성 패치 + 테스트맵
4 files changed, 106 insertions(+), 16 deletions(-)
브랜치: feat/combat-hit-damage-system
시각: 17:32
커밋 범위 ——
| 파일 | 변경 |
|---|---|
Source/.../Private/Combat/WeaponComponent.cpp | UnsafeRootBones 자식 본 6단계 폴백 + 캡슐 ECC_Pawn 일시 Ignore |
Source/.../Private/Combat/HitReactComponent.cpp | ExcludedBones 9개 본(Mixamo + UE5) 확장 |
Content/Combat/TestMap1.umap | 테스트맵 — 좀비 다수 배치 |
Content/Level/TestMap1.umap | 테스트맵 — 사격 라인 검증용 |
헤더(.h) 변경 없이 cpp만 손댄 게 빌드 시간·리뷰 부담을 크게 줄였다. 의심2 패치는 헤더(TMap<TWeakObjectPtr<AActor>, FTimerHandle> 멤버 추가)가 따라가야 해서 별도 PR로 분리.
6. PR #34 머지 — develop 정식 진입
1
2
e0e6358 Merge pull request #34 from feat/combat-hit-damage-system
시각: 17:34
PR #34 머지로 좀비 라그돌·히트스톱 안정성 패치가 develop에 정식 반영. 같은 시각 다른 팀원 작업도 함께 들어왔다 ——
- PR #32 —
feat/Player: 무기 에셋 추가 - PR #33 —
feat/system-HUD: UI 작업 마무리
세 PR이 같은 시각에 들어가면서 develop이 한 번에 갱신. 내일 의심2 패치 진행 전에 develop pull → feat/combat-hit-damage-system rebase 필요. PR #32·#33의 변경분이 Combat·HUD 인터페이스에 영향 없을지 먼저 확인하고 머지 충돌 잡고 시작.
Bootcamp-TIL 커밋 흐름 정정 — reset –mixed로 되돌리기
오후 작업 사이에 Bootcamp-TIL 저장소에 두 커밋이 만들어졌다 ——
1
2
edc3904 (5/15 오후·저녁 분량 코드카타 + TIL 일부)
b6f5dc4 (5/18 TIL — 604줄)
그런데 외부 프로젝트의 PR #34 머지가 오늘의 진짜 우선순위라 — Bootcamp-TIL은 외부 작업이 develop에 들어간 뒤에 정리하는 게 흐름상 맞다. 사용자 요청으로 두 커밋을 git reset --mixed HEAD~2로 되돌려 working tree에 보존.
--mixed vs --hard의 차이 ——
| 옵션 | 인덱스 | working tree | 안전성 |
|---|---|---|---|
--soft | 보존 | 보존 | 가장 안전 — 다시 커밋만 하면 됨 |
--mixed(기본) | 리셋 | 보존 | 변경분 보존 + 재스테이지 필요 |
--hard | 리셋 | 삭제 | 변경분까지 날아감 — 금지 패턴 |
--mixed로 되돌렸으니 4개 파일(5월/2026-05-15.md modified + 5월/2026-05-18.md untracked + 코드카타 cpp 2개 untracked)은 working tree에 그대로 살아 있다. 내일 외부 작업 정리 후 다시 스테이징해 커밋하면 됨.
오늘의 워크플로우 교훈 ——
- 여러 저장소 동시 작업 시 우선순위 명확히 — 외부 프로젝트 PR 머지가 우선이면 Bootcamp-TIL 커밋은 그 뒤에. 거꾸로 만들면 외부 변경분과 TIL 기록이 시간상 어긋난다
- 되돌릴 땐
--mixed가 기본 안전판 —--hard는 정말 변경분까지 버릴 때만. 일반 정정은--mixed로 working tree만 보존 - 커밋은 PR 머지 이벤트와 같은 날짜에 — TIL이 PR 머지를 기록하는 거니까 머지 후에 TIL 커밋이 들어가는 순서가 자연스러움
CS 27 핵심 인사이트 — 26번과의 연결
자세한 본문은 raw/cs-notion/27_memory_fragmentation.md에 있고, 여기는 작성 중 새로 정리된 통찰만 압축.
외부 단편화 vs 내부 단편화 — 누가 못 쓰는 공간을 만드는가
| 항목 | 외부 단편화 | 내부 단편화 |
|---|---|---|
| 위치 | 블록과 블록 사이 | 한 블록 내부 |
| 누구의 공간? | 누구의 것도 아님(free인데 작아서 못 씀) | 이미 어떤 요청자에게 할당됨(여백) |
| 원인 | 가변 크기 할당·해제 반복 | 크기 클래스·정렬 padding |
| 해법 방향 | 합치거나(Compaction) 페이지로 추상화(Paging) | 크기 클래스 세분화·전용 풀(Slab·Pool) |
| 비유 | 주차장에 차들이 띄엄띄엄 — 큰 차가 못 들어옴 | 소형차가 대형 칸 점유 — 옆 공간 낭비 |
이 둘은 trade-off 관계. Paging은 외부 단편화를 구조적으로 없애지만 페이지 단위 정렬로 내부 단편화를 만든다. Slab은 내부 단편화를 거의 0으로 줄이지만 클래스별 메타데이터·캐시 오버헤드가 따라온다. “단편화 0인 할당기”는 존재하지 않고, 워크로드에 맞춰 한쪽 비용을 받아들이는 선택이 본질.
Paging이 외부 단편화를 구조적으로 없앤다는 발상
26번에서 “페이지 폴트는 매핑이 없을 때 OS 개입”이라고 했는데, 27번을 쓰면서 다시 보니 Paging의 본질적 동기가 외부 단편화 해결이라는 게 명확해졌다. 가변 크기 가상 영역을 고정 크기(4KB) 페이지로 잘게 쪼개 물리 메모리에 매핑하는 순간, 물리 단위의 외부 단편화는 정의상 사라진다 — 모든 페이지 프레임이 같은 크기니까 어떤 free 프레임이든 어떤 페이지든 받을 수 있다.
그 대가가 ——
- 페이지 테이블·TLB 비용(25·26번에서 이미 다룸)
- 페이지 단위 내부 단편화 — 4097바이트만 쓰는 영역도 페이지 2개(8KB) 점유. 마지막 페이지의 4095바이트는 못 씀
- 페이지 폴트의 빈도 — demand paging은 첫 접근 시 매번 minor fault
26번과 27번을 묶어서 보면 — “OS는 외부 단편화를 Paging으로 죽이는 대신, 페이지 폴트와 페이지 단위 내부 단편화라는 새 비용을 받아들였다.” 이건 면접에서 두 주제를 잇는 한 줄로 쓰기 좋다.
Buddy System — 빠른 합병의 대가는 50% 내부 단편화
Linux 커널의 페이지 프레임 할당기. 2의 거듭제곱 크기 블록만 다루고 인접 buddy와 자동 분할·병합(coalescing).
핵심 메커니즘 — buddy 주소는 XOR로 계산 ——
1
2
크기 128B 블록의 buddy 주소 = 자기 주소 XOR 128
크기 256B 블록의 buddy 주소 = 자기 주소 XOR 256
이 XOR 트릭 덕분에 해제 시 buddy가 free인지 O(1)로 확인하고, free면 즉시 합쳐 더 큰 블록을 만든다. 재귀적으로 위로 올라가며 합치니까 외부 단편화 완화가 자동.
대가는 최악 50% 내부 단편화 ——
| 요청 | 할당 | 낭비 |
|---|---|---|
| 65바이트 | 128바이트 | 63바이트(48%) |
| 129바이트 | 256바이트 | 127바이트(49%) |
| 1025바이트 | 2048바이트 | 1023바이트(49%) |
요청이 2의 거듭제곱 바로 위면 거의 절반이 버려진다. 그래서 Linux는 buddy 위에 Slab Allocator를 한 층 더 얹어 — buddy가 큰 페이지 청크를 주면 slab이 그걸 같은 크기 객체 슬롯으로 잘게 쪼개 내부 단편화를 회수한다. 두 알고리즘이 계층을 이루는 게 핵심.
언리얼 FMemory와 게임 메모리 패턴
UE5의 FMemory는 추상화 계층. 백엔드로 ——
FMallocBinned2— 크기 클래스별 bin 기반, 기본값FMallocTBB— Intel TBB(스레드 친화)FMallocJemalloc— 일부 플랫폼
게임 엔진이 범용 malloc을 거의 안 쓰고 패턴화된 할당기를 쓰는 이유는 단편화 회피 ——
| 패턴 | 동작 | 단편화 회피 메커니즘 |
|---|---|---|
| Frame Allocator(Linear) | 한 프레임 동안만 쓰고 프레임 끝에 통째로 reset | 개별 free 없음 → 단편화 발생 불가 |
| Pool Allocator | 같은 크기·짧은 수명 객체(총알·파티클) N개 미리 잡고 재사용 | 같은 크기만 다루니 외부 단편화 0 |
| Arena Allocator | 범위 시작에 큰 블록 하나, 범위 끝에 한 번에 free | 개별 free 없음 → 단편화 누적 불가 |
세 패턴의 공통점 — “개별 free를 안 한다”. free가 없으면 외부 단편화 자체가 발생할 수 없다는 게 핵심 통찰. 단편화는 “할당/해제 패턴이 정해진 시간 축을 따라 흩어질 때” 생기는 부산물이라, 그 시간 축을 제거(프레임·범위·풀)하면 단편화도 같이 사라진다.
콘솔(PS5·XSX)은 swap이 사실상 없어 단편화 누적 = OOM 크래시. 그래서 콘솔 인증(TRC/XR) 통과를 위해서는 메모리 풀과 워킹 셋 관리가 필수. PC만 만들 때 안 보이던 비용이 콘솔 포팅에서 첫 OOM 크래시로 드러나는 게 흔한 패턴이라고 정리.
미해결 / 내일(5/20) 우선순위
- ⬜ 히트스톱 의심2 패치 —
HitStopTimers멤버 맵 도입 —WeaponComponent.h에TMap<TWeakObjectPtr<AActor>, FTimerHandle> HitStopTimers멤버 추가,ApplyHitStop진입부에 기존 타이머ClearTimer, 지역FTimerHandle→ 멤버 맵 저장 전환, 복귀 람다에서Remove(WeakHit). 헤더 변경 동반이라 별도 PR로 진행 - ⬜ PIE 검증 — 연사 시 0.15초 슬로우가 온전히 유지되는지 + 다발 push 차단 동시 검증. 진단 로그(
[HitStop] %s scale=%.2f dur=%.2fs)로 호출 여부·스케일·duration 확인 - ⬜ 의심1·3 잔여 검증 — BP 인스턴스 오버라이드(
Weapon | Hit Stop카테고리에서 노란색 reset 화살표 확인) / 좀비 AnimBP의Get World Delta Seconds노드 사용 여부 검색 - ⬜ 24번 floating_point 정밀도 파트 답변 보강 — 가수 23비트 → 7자리 십진 정밀도($\log_{10}(2^{23}) \approx 6.92$), ULP, machine epsilon 한 묶음 정리 (이월)
- ⬜ graphify CS 그래프 풀 빌드 — 26·27번 신규 반영. 27번은 cluster-only로만 갱신된 상태라
graphify update .1회로 풀 빌드 필요 - ⬜ Bootcamp-TIL 미커밋 4개 정리 —
5월/2026-05-15.md(modified, 5/15 오후·저녁 분량) +5월/2026-05-18.md(untracked) + 코드카타100zun/2026-05-15-1.cpp·2026-05-15-2.cpp(untracked). reset 이후 working tree에 보존 중 — 본 TIL과 함께 한 묶음으로 커밋 - ⬜ develop pull → feat/combat-hit-damage-system rebase — PR #32(
feat/Player)·#33(feat/system-HUD)이 함께 들어가 인터페이스 변화 가능성. 의심2 패치 시작 전에 충돌 검증 - ⬜ Ch3 발표 잔여 6일 — 피격 VFX · 처형 트리거 · 카메라 셰이크 두 번째 + 무기 분기 통합 (이월)
- ⬜ NBC_Master Step 2 진입 결정 — 사용자 승인 게이트 (이월)
오늘 배운 것 정리
- CS 26·27번을 묶는 한 문장 — “OS는 외부 단편화를 Paging으로 죽이는 대신, 페이지 폴트와 페이지 단위 내부 단편화를 받아들였다” — 26번 페이지 폴트와 27번 메모리 단편화는 별개 주제처럼 보이지만, Paging이라는 메커니즘이 두 주제를 가로지른다. 외부 단편화를 구조적으로 없애는 대가가 페이지 폴트와 페이지 단위 내부 단편화. 두 비용은 trade-off가 아니라 같은 설계 결정의 양면. 면접에서 두 주제를 잇는 흐름으로 사용 가능
- Buddy + Slab의 계층 구조 — 각자의 약점을 위에서 보완 — Buddy는 빠른 합병 대신 최악 50% 내부 단편화. Slab은 내부 단편화 0이지만 같은 크기 객체에만 효과. Linux는 Buddy가 페이지 청크를 주고 Slab이 그걸 객체 슬롯으로 쪼개는 계층을 두어 두 약점을 동시에 잡는다. “단편화 0인 단일 할당기는 없다. 계층화로 약점을 상쇄한다”가 메모리 관리의 핵심 패턴
- 게임 엔진 메모리 패턴의 공통 트릭 — “개별 free를 안 한다” — Frame Allocator(매 프레임 reset), Pool Allocator(같은 크기 재사용), Arena Allocator(범위 끝에 한 번에 free). 세 패턴 모두 개별 free 호출 자체를 없애 단편화가 발생할 시간 축을 제거. 단편화는 “할당·해제가 시간 축을 따라 흩어질 때” 생기는 부산물이라는 시각 변화
- 콘솔의 단편화 비용은 PC와 차원이 다르다 — swap이 없다 — PC는 단편화로 큰 블록 부족 → 새 페이지를 OS에서 받음 → 워킹 셋 증가 → 캐시 미스↑·페이지 폴트↑ 정도. 콘솔(PS5·XSX)은 swap이 사실상 없어 단편화 누적이 곧 OOM 크래시. 콘솔 인증(TRC/XR)은 메모리 풀과 워킹 셋 관리가 필수. PC만 만들 때 안 보이는 비용이 콘솔 포팅에서 첫 OOM으로 터지는 게 전형
- 워크플로우 정정의 안전 패턴 —
git reset --mixed— 두 저장소 동시 작업에서 우선순위가 바뀐 걸 깨달았을 때,--mixed로 인덱스만 리셋하고 working tree 변경분은 보존.--hard는 변경분까지 날아가서 금지. 일반 정정 케이스에서는 항상--mixed가 기본 안전판. 외부 프로젝트 PR이 우선이면 본 레포 커밋은 그 뒤에 — 시간 순서가 어긋나면 TIL이 PR 머지를 정확히 기록하지 못함 - 5/18 인계 노트의 가치 — 컨텍스트 복원 비용을 크게 줄였다 — 어제 빌드 에러로 세션이 중단됐을 때 인계 노트(
scrum/2026-05-18_히트스톱_인계.md)에 의심 3가지·의심2 패치 코드·재시작 첫 메시지 흐름·관련 파일 경로까지 정리해둔 게 오늘 오후 빌드 에러 해결과 PR 머지를 그대로 진행 가능하게 함. 세션이 깨질 가능성이 있을 땐 인계 노트가 다음 세션의 시작점이 된다는 패턴