나만의 작은 도서관
[TIL][C++] 250529 MMO 서버 개발 28일차: 대용량 파일을 Github에 Push하기 위한 방법: LFS, 언리얼에서의 캐스팅 본문
Today I Learn
[TIL][C++] 250529 MMO 서버 개발 28일차: 대용량 파일을 Github에 Push하기 위한 방법: LFS, 언리얼에서의 캐스팅
pledge24 2025. 5. 29. 23:17주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
github은 100MB 이상의 파일을 push 할 수 없다.
- 기본적으로 git은 여러 개의 작은 소스 코드 파일들을 추적하기 위한 VCS(Version Control System)이므로, 과하게 큰 파일을 추적하는 것은 소스 코드가 아닐 가능성이 높다.
- 위와 같은 이유에서인지 Github에서는 100MB 이상의 파일을 Push 할 수 없게 제한을 두고 있는데, 이 제한 때문에 자원(Resource)에 해당하는 파일들이 너무 크면 용량 제한에 걸려 못 올리는 경우가 생긴다.
- 예시로, 언리얼 프로젝트에서 사용한 NPC, 몬스터의 에셋들은. uasset이라는 확장자 파일로 존재하게 되는데, 이러한 에셋들의 용량은 100MB를 넘어가는 아득히 큰 용량을 가진다. 에셋이 없는 게임은 말이 안 되기 때문에 이 에셋들을 Github에 올려야 하지만 위와 같은 제약 때문에 올리지 못하게 된다.
대용량 파일을 Github에 Push 하기 위한 방법: LFS
- 그렇다고 Github에 100MB 이상의 대용량 파일을 올릴 수 없는 건 아니다. Github에 대용량 파일을 올리는 대표적인 방법으로 LFS가 있다.
- LFS는 Large File Storage의 약자로, 이름에서 알 수 있듯 큰 파일을 저장하는 저장소이다.
- LFS의 원리는 간단하다. 대용량 파일을 잘게 조각내어 Github에 올릴 수 있는 크기로 재가공할 뿐이다.
- git에서는 이러한 LFS 기능을 지원하기 때문에, git lfs와 같은 명령어를 통해 사용할 수 있다.
LFS로 대용량 파일 관리하기
- 현재 디렉터리(레포지토리)에 lfs를 적용한다.
git lfs install
- lfs로 관리할 파일을 track한다.
- 주의할 점은 추적할 파일을 git add로 추가하기 전에, git lfs track을 먼저 설정해야 한다. 만약, 이미 add를 하여 git이 해당 파일을 추적하고 있다면, git rm —cache로 파일 추적을 해제해하고 git lfs track을 해야 한다.
// 1) git이 추적하지 않는 파일인 경우(git add를 안한 파일인 경우)
git lfs track <file-path>
git add <file-path>
// 2) git이 이미 추적하는 파일인 경우(git add를 했던 파일인 경우)
git rm --cached <file-path>
git lfs track <file-path>
git add <file-path>
- 파일을 위 명령어를 통해 lfs에 등록했다면, 해당 파일의 내용이 원래의 contents가 아니라, lfs pointer로 변경된다. 또한, lfs로 트래킹 하는 파일에 대한 정보는. gitattributes를 통해 관리가 되어 이 변경 사항을 꼭 add 해야 한다
LFS 관리 목록 확인하기
git lfs ls-files
LFS를 subdir에 대해서 적용하기
git rm -r --cached "toy_data/**/*"
git lfs track toy_data/**/*
이런 식으로 꼭 --cached에서는 큰따옴표로 감싸은 뒤 **을 사용해야 하고, 반면 lfs에서는 큰따옴표 없이 적용해야 한다.
LFS 관리 해제하기
- . gitattributes의 맨 마지막 라인을 보면 어떤 규칙으로 파일들이 lfs로 관리되고 있는지 알 수 있다. 즉, 새로운 파일을 lfs의 추적 대상으로 등록/해제할 때마다. gitattribute가 변경된다.
- 만약 어떠한 이유로(추적대상이 아닌데 해버린 경우 등) 파일을 LFS 관리 대상에서 제외하고 싶다면 아래 명령어를 입력한다.
git lfs untrack <file-path> // .gitattribute에서 수동 삭제와 같은 효과
- 문제는 untrack을 해도 lfs 관리 대상으로 등록한 파일은 lfs-pointer로 변경된 상태로 존재하기 때문에, 원래의 contents 형식으로 변환해 주는 작업이 추가로 필요하다. 따라서, lfs가 아닌 일반 git에서 관리하는 파일 형태로 관리하기 위해 해당 파일의 추적을 지우고 다시 추가하는 작업을 해준다.
git rm --cached <file-path>
git add <file-path>
LFS가 관리하는 파일 다운로드하기
- lfs가 관리하는 파일들을 다운로드하고 싶다면 아래의 명령어를 사용한다.
git lfs pull // lfs 포인터를 사용해 lfs가 관리하는 대용량 파일들을 다운받는다.
LFS의 문제점: 과금적 요소
- 사용해 본 적은 없어서 정확한 사실 여부는 확인하지 않았지만, 알아본 결과, LFS는 과금적 요소가 있기 때문에 돈을 내기 싫은 상황이라면 LFS가 항상 좋은 선택지가 되지는 않는다.
- 실제 현업이나 큰 프로젝트에서 사용할게 아니라면 과금적 요소는 항상 원하지 않기 때문에 간단하게 포폴이나 팀 프로젝트를 하는 입장이라면 LFS 도입에 의한 과금 발생은 거부감이 들 수밖에 없다.
다른 대안은 없을까?
- LFS를 사용하지 않는 방향으로 언리얼 에셋 등의 대용량 파일들을 관리하고 싶다면, 클라우드를 사용하는 방법도 있다.
- 위에서도 언급했지만 자원(Resource)에 해당하는 파일들에서 대용량 파일들이 대부분 존재하기 때문에, 자원 파일들만 따로 한 폴더로 관리해서 협의하에 수동으로 클라우드에서 관리하면 된다.
- 언리얼을 예로 들자면, 언리얼을 자원에 해당하는 폴더인 Contents가 있으므로, Contents를. gitignore에 등록하여 해당 폴더의 변경사항을 무시하고(이미 추적 대상이라면 gir rm —cached Contents/ 명령어 먼저 실행), 그 폴더만 따로 클라우드에 박아 넣고 쓰면 된다.
언리얼 캐스팅
- 언제나 늘 그랬듯, 언리얼은 C++ 표준 기능들을 곧이곧대로 쓰지 않고, 본인들 입맛에 맞게 사용하기 위해 다른 방식으로 사용한다. 오늘 알아볼 내용은 캐스팅이다.
주요 캐스팅 유형
- Safe Cast(안전한 캐스팅): 가장 일반적으로 사용되는 방식으로, 객체가 특정 클래스의 인스턴스인지 확인한 수 캐스팅을 수행한다. 실패할 경우 null을 반환한다.
- Class Cast(클래스 캐스팅): 특정 클래스로 직접 캐스팅하는 방식. 확실한 경우에만 사용해야 하며, 실패 시 크래시가 발생할 수 있습니다.
블루프린트에서의 활용법
- 블루프린트에서 캐스팅을 하려면 CastTo 노드를 사용하면 된다. 캐스팅 성공 시, “Cast Success” 핀이 실행되고, 반대로 실패하면 “Cast Failed” 핀이 실행된다.
- 예를 들어, Actor를 Pawn으로 캐스팅하거나 Component를 특정 컴포넌트 타입으로 캐스팅할 때 사용한다.
C++에서의 사용법
- Cast <>() 함수를 사용
- Cast <AMyActor>(SomeObject) - 안전한 캐스팅
- 런타임에 타입 검사를 수행하고 실패 시 nullptr를 반환
- 항상 안전성 검사를 수행하기 때문에 약간의 오버헤드가 존재한다(dynamic_cast와 같은 이유)
- Release / Debug 모드 관계없이 동일한 성능
- CastChecked <AMyActor>(SomeObject) - 체크된 캐스팅 (디버그 모드에서 실패 시 assertion)
- 디버그 모드: Cast <>()와 동일하게 타입 검사 + 실패 시 assertion
- 릴리즈 모드: 타입 검사 생략 → 직접 캐스팅 수행(static_cast)
왜 언리얼을 캐스팅을 따로 만들었을까?
- 리플렉션 시스템 통합
- 성능 최적화
- dynamic_cast는 표준 RTTI를 사용하기 때문에 상대적으로 느림
- 언리얼의 Cast는 UClass 시스템을 통해 최적화된 타입 검사 수행을 한다.
- 컴파일 타임에 더 많은 정보를 활용하여 런타임 비용이 감소