[Project Arc] 멀티플레이 환경에서 Dynamic Level Streaming 실패 이유 정리

개발자 김선호·2025년 11월 20일

Project CM + Project Arc

목록 보기
11/25

Room 단위로 서브 레벨을 스트리밍하여 맵을 동적으로 구성하는 기능을 구현하는 과정에서, 싱글 플레이와 리슨 서버 환경에서는 정상적으로 동작하지만, 두 번째 클라이언트 접속 시 MissingLevelPackage 오류와 함께 접속이 끊어지는 문제가 발생했습니다. 이 TIL에서는 해당 문제가 발생한 원인과, 언리얼 엔진의 레벨 스트리밍·네트워크 동기화 구조를 정리하여, 앞으로 같은 실수를 반복하지 않기 위한 기록을 남기고자 합니다.


발생한 현상 요약

  • 리슨 서버에서 StreamingLevelName = L_StreamingTest2 설정 후 LoadLevelInstance 계열 로직으로 레벨 인스턴스 생성

  • 리슨 서버 단독 플레이에서는 정상적으로 방이 원하는 위치에 생성

  • 두 번째 클라이언트가 접속하면 다음과 같은 로그 출력 후 연결 끊김

    • ServerUpdateLevelVisibility() ignored non-existant package
    • MissingLevelPackage
    • Your connection to the host has been lost.
  • 결과적으로, 서버 입장에서는 스트리밍이 성공한 것처럼 보이나, 새로 접속한 클라이언트 기준에서는 해당 패키지를 찾지 못해 접속이 강제로 종료되는 상황 발생


근본 원인 1: ULevelStreamingDynamic 이름 비결정성

  • 문제가 된 코드에서 사용한 것은 ULevelStreamingDynamic / LoadLevelInstance 계열 API
  • LoadLevelInstance 내부 구현
    • MakeUniqueObjectName(..., TEXT("_LevelInstance")) 로 스트리밍 레벨 인스턴스 이름 자동 생성
    • _LevelInstance_135, _LevelInstance_396 같이 접미사 숫자가 내부 카운터 기반으로 붙음
  • 서버/클라이언트 각각이 같은 맵 이름으로 LoadLevelInstance를 호출해도
    • 서버: L_StreamingTest2_LevelInstance_135
    • 클라: L_StreamingTest2_LevelInstance_396
    • 이런 식으로 서로 다른 이름의 패키지 생성
  • 결과적으로 서버가 가진 스트리밍 패키지 이름과 클라이언트가 가진 패키지 이름이 일치하지 않음
    • 서버는 ..._135를 visibility 업데이트 대상으로 삼지만
    • 클라이언트 월드에는 ..._135가 존재하지 않음 → ignored non-existant package 로그 출력

근본 원인 2: 동적 스트리밍 레벨 정보는 Replication 되지 않음

  • 언리얼 기본 네트워크 동기화는 액터 / 컴포넌트 / 프로퍼티에 초점
  • ULevelStreamingDynamic 으로 런타임에 새 스트리밍 레벨을 로드하는 행위 자체
    • 서버에서 생성했다고 해서 해당 스트리밍 레벨 정보가 클라이언트로 자동 복제되지 않음
  • 서버 기준 흐름
    • 서버: Dynamic Streaming Level 생성 및 로드, visible 상태로 전환
    • 서버: ServerUpdateLevelVisibility 에서 해당 패키지를 클라에게도 보여주려고 시도
  • 클라이언트 기준 상황
    • 클라이언트: 해당 이름의 스트리밍 레벨 인스턴스를 가진 적이 없음
    • 네트워크를 통해 "이 이름의 스트리밍 레벨을 같이 로드하자" 라는 정보가 구조적으로 내려오지 않음
  • 따라서 서버와 클라의 스트리밍 레벨 목록이 서로 일치하지 않는 상태에서 visibility 동기화가 수행됨
    • 없는 패키지에 대해 visibility 업데이트를 시도 → MissingLevelPackage 로 연결 종료

근본 원인 3: 호출 타이밍·월드 상태의 비결정성

  • LoadLevelInstance 는 다음 요소들에 의존하는 비결정적(Non-Deterministic) 특성 존재
    • 오브젝트 이름 카운터
    • GC 및 기존 객체 존재 여부
    • 호출 시점, 호출 순서
  • 서버와 클라이언트가 같은 프레임에 같은 코드 경로를 탄다 하더라도
    • 내부적으로 생성되는 LevelInstance 이름, 로딩 완료 시점, 스트리밍 레벨 배열에 들어가는 순서는 항상 일치한다고 보장할 수 없음
  • 특히 리슨 서버 환경에서
    • 서버와 첫 번째 클라이언트는 같은 프로세스 내에서 이상 없이 보이지만
    • 두 번째 클라이언트는 이미 월드 상태가 바뀐 뒤 접속하기 때문에, 기존에 서버에서 만든 Dynamic Level과 맞출 수 있는 정보가 더더욱 부족

멀티플레이에서 Dynamic Level Instance를 쓰기 어려운 이유 정리

  • ULevelStreamingDynamic / LoadLevelInstance 기반 구조는 멀티플레이에서 다음 이유로 부적합
    • 인스턴스 이름이 자동 생성 → 서버/클라 이름 불일치
    • 동적으로 추가한 스트리밍 레벨 목록이 replication 되지 않음
    • 기존 네트워크 레벨 로딩/전환 파이프라인과 분리된 별도 경로로 동작
    • 접속 중인 플레이어별로 월드 상태가 달라질 수 있어, 새로 접속한 플레이어가 기존 동적 레벨 상태를 재현하기 어려움
  • 따라서 "서버가 동적으로 레벨 인스턴스를 만든 뒤, 그걸 그대로 클라이언트에게 복제하는 구조"는 언리얼 기본 기능만으로는 성립하지 않음

이번 프로젝트에서 드러난 오해 정리

  • 오해 1
    • "서버에서만 맵 스트리밍을 하면, 그 지형 정보가 자동으로 클라이언트에게 복제된다"
    • 실제로는 동적 스트리밍 레벨 자체는 복제가 안 되고, 고정된 맵 전환/스트리밍에만 네트워크 지원이 붙어 있음
  • 오해 2
    • "서버와 클라이언트가 같은 함수를 호출하면, 같은 LevelInstance 이름으로 생성될 것이다"
    • 실제로는 MakeUniqueObjectName 특성상 이름이 달라질 수밖에 없고, 호출 순서/개수 차이로 완전히 다른 결과가 나옴
  • 오해 3
    • "에러 로그에 나오는 패키지 이름만 맞춰주면 된다"
    • 실제로는 이름만의 문제가 아니라, 스트리밍 레벨의 생성·레이아웃·오너십 전체가 네트워크 파이프라인 밖에 있어서 구조적으로 어긋나 있음

앞으로의 설계 방향 메모

  • 방 단위 맵 생성이 필요할 때 고려할 선택지
    • 고정 Streaming Level 여러 개를 에디터에 등록한 뒤, 서버/클라 모두 같은 이름으로 Stream In/Out
    • 완전히 동적인 던전은 "레벨 스트리밍" 대신 "액터 스폰 + 랜덤 시드 동기화" 패턴 적용
    • 장기적으로는 World Partition 기반으로 전환하고 Data Layer를 사용해 가시성/로딩 제어
  • 특히 멀티플레이를 고려하는 시스템에서는
    • "서버가 만든 레벨이 그대로 클라에 복제된다"는 가정을 버리고
    • "항상 서버/클라가 같은 규칙으로 월드를 재구성한다"는 관점에서 설계하는 편이 안전함

마치며

이번 문제는 단순히 코드 버그라기보다, 언리얼 엔진의 레벨 스트리밍과 네트워크 동기화가 어떻게 설계되어 있는지 충분히 이해하지 못한 상태에서 ULevelStreamingDynamic을 멀티플레이 환경에 그대로 적용한 데에서 비롯된 것이었습니다. 서버와 클라이언트가 동일한 레벨 인스턴스를 공유한다고 가정하고 구현했으나, 실제로는 인스턴스 이름 생성, 스트리밍 레벨 목록 관리, 패키지 로딩 흐름이 네트워크와 분리되어 있어, 새로 접속한 클라이언트가 동적으로 생성된 레벨을 찾지 못하는 상황이 발생했습니다.

이 TIL을 통해, 멀티플레이에서 레벨을 다루는 방법은 기존 싱글 플레이/에디터 중심 사고방식과는 다르게 접근해야 한다는 점을 다시 한 번 정리하게 되었습니다. 앞으로는 월드 파티션, 고정 스트리밍 레벨, 액터 기반 동적 생성 등 언리얼이 제공하는 네트워크 친화적 구조를 우선적으로 고려하여 설계하고, 특히 "서버가 만든 것을 클라에 그대로 복제한다"는 직관적인 기대 대신, "서버와 클라가 합의된 규칙에 따라 동일한 월드를 재구성한다"는 관점에서 시스템을 구성하고자 합니다.

profile
프로젝트 진행 과정을 주로 업로드합니다

0개의 댓글