[Unity] Netcode for GameObjects(NGO) (1)

조경민·2026년 4월 20일

NetworkManager

  • NGO의 핵심 진입점
  • 네트워크 세션의 시작 · 종료, 플레이어 스폰, 씬 관리, Transport 연결을 총괄
  • 씬에 반드시 하나만 존재해야함
    • 일반적으로 DontDestroyOnLoad 오브젝트로 유지
  • 배치
    • Hierarchy 에 빈 GameObject 를 생성 -> Add Component > NetworkManager, UnityTransport 추가

Host / Client / Server 실행 모드

모드설명사용 시점
HostServer + Client 를 동시에 실행. 자신이 방장이자 참가자가 됨.기본 방식
Client이미 실행 중인 서버(또는 Host)에 접속하는 참가자방 참가자의 실행 모드
Server플레이어 없이 게임 상태만 관리전용 서버(Dedicated Server) 환경에서 사용
  • Host는 서버와 클라이언트를 겸하므로 NetworkManager.Singleton.IsServerIsClient 가 동시에 true를 반환
  • 서버 / 순수 클라이언트 분기
    • "서버에서만 실행" 코드는 if (IsServer) 로 분기 (Host 도 포함)
    • "Host 가 아닌 순수 클라이언트에서만 실행" 코드가 필요하면 if (IsClient && !IsServer) 로 분기

시작 · 종료 API

// 호스트(서버 + 클라이언트)로 시작
NetworkManager.Singleton.StartHost();

// 클라이언트로 시작 (다른 Host 에 접속)
NetworkManager.Singleton.StartClient();

// 전용 서버로 시작
NetworkManager.Singleton.StartServer();

// 네트워크 종료
NetworkManager.Singleton.Shutdown();

주요 연결 콜백

콜백발생 시점
OnClientConnectedCallback클라이언트가 접속 직후 (서버와 클라이언트 양쪽에서 호출)
OnClientDisconnectCallback클라이언트 연결이 끊긴 직후
OnServerStarted서버가 시작되었을 때 (Host / Server 측에서 호출)

예제 코드

using UnityEngine;
using UnityEngine.UI;
using Unity.Netcode;

public class NetworkBootstrap : MonoBehaviour
{
    [SerializeField] private Button _startHostButton;
    [SerializeField] private Button _startClientButton;
    [SerializeField] private Button _disconnectButton;

    private bool _isCallbacksBound;

    private void OnEnable()
    {
        BindNetworkCallbacks();
        BindButtonEvents();
    }

    private void OnDisable()
    {
        UnbindNetworkCallbacks();
        UnbindButtonEvents();
    }

    private void BindButtonEvents()
    {
        _startHostButton.onClick.AddListener(StartHost);
        _startClientButton.onClick.AddListener(StartClient);
        _disconnectButton.onClick.AddListener(Disconnect);
    }

    private void UnbindButtonEvents()
    {
        _startHostButton.onClick.RemoveListener(StartHost);
        _startClientButton.onClick.RemoveListener(StartClient);
        _disconnectButton.onClick.RemoveListener(Disconnect);
    }

    private void BindNetworkCallbacks()
    {
        if (_isCallbacksBound) return;
        if (NetworkManager.Singleton == null) return;

        NetworkManager.Singleton.OnClientConnectedCallback  += OnClientConnected;
        NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnect;
        NetworkManager.Singleton.OnServerStarted            += OnServerStarted;
        _isCallbacksBound = true;
    }

    private void UnbindNetworkCallbacks()
    {
        if (!_isCallbacksBound) return;
        if (NetworkManager.Singleton == null) return;

        NetworkManager.Singleton.OnClientConnectedCallback  -= OnClientConnected;
        NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnect;
        NetworkManager.Singleton.OnServerStarted            -= OnServerStarted;
        _isCallbacksBound = false;
    }

    private void StartHost()   => NetworkManager.Singleton.StartHost();
    private void StartClient() => NetworkManager.Singleton.StartClient();
    private void Disconnect()  => NetworkManager.Singleton.Shutdown();

    private void OnClientConnected(ulong clientId)  => Debug.Log($"<color=green>[Network] 접속: {clientId}</color>");
    private void OnClientDisconnect(ulong clientId) => Debug.Log($"<color=red>[Network] 해제: {clientId}</color>");
    private void OnServerStarted()                  => Debug.Log("<color=green>[Network] 서버 시작</color>");
}

NetworkObject

  • NGO에서 네트워크를 통해 동기화되는 오브젝트의 기본 단위
  • NetworkTransform · NetworkVariable · ServerRpcNetworkObject 컴포넌트가 붙은 GameObject 위에서만 동작
    -> 네트워크 동기화가 필요한 GameObject에는 가장 먼저 NetworkObject 컴포넌트를 붙임

NetworkBehaviour

  • MonoBehaviour 대신 NetworkBehaviour 를 상속하면 그 스크립트 안에서 IsOwner, IsServer, ServerRpc 같은 네트워크 기능을 사용할 수 있음.
public class PlayerController : NetworkBehaviour   // MonoBehaviour 가 아님
{
    public override void OnNetworkSpawn()
    {
        if (!IsOwner) return;
        // 소유자 전용 초기화
    }
}
  • NetworkBehaviour가 붙은 GameObject에는 반드시 NetworkObject 컴포넌트도 함께 있어야 함.

네트워크 라이프사이클

  • NetworkBehaviour 는 일반 MonoBehaviour 라이프사이클 위에 네트워크 전용 라이프사이클이 추가됨.
    👉 Awake → OnEnable → OnNetworkSpawn(이 시점부터 IsOwner / IsServer 등이 유효) → Start → Update

  • 네트워크 속성에 의존하는 초기화는 반드시 OnNetworkSpawn() 에서 수행

Ownership

속성질문대표 용도
IsOwner"이 오브젝트 를 내가 소유하는가?"입력 처리, 권위 있는 상태 변경 허용
IsLocalPlayer"이 오브젝트가 내 플레이어 캐릭터 인가?"로컬 카메라 추적, 내 HP 바 표시
IsServer"이 피어(peer)가 서버인가?"서버 권위 연산 (데미지 계산 등)
  • 소유권 기반으로 제한 → IsOwner
  • "내 캐릭터인지" 판별 → IsLocalPlayer
  • 서버 전용 연산 → IsServer

소유자만 입력을 처리하는 패턴

using UnityEngine;
using Unity.Netcode;

public class PlayerController : NetworkBehaviour
{
    [SerializeField] private float _moveSpeed = 5f;

    public override void OnNetworkSpawn()
    {
        if (!IsOwner) return;
        // 소유자 전용 입력 바인딩 등 초기화
    }

    private void Update()
    {
        if (!IsOwner) return;

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0f, v) * _moveSpeed * Time.deltaTime;
        transform.position += move;
    }
}

NetworkTransform

  • 오브젝트의 Position · Rotation · Scale 을 네트워크로 자동 동기화하는 컴포넌트

  • 권한이 있는 피어가 로컬에서 transform을 변경하면, NetworkTransform이 변경값을 다른 모든 피어에 자동으로 전파

  • 네트워크 지연으로 인해 위치 갱신이 불연속적으로 수신될 수 있기 때문에, NetworkTransform은 수신된 위치 값을 보간(Interpolate) 해서 부드럽게 표현

  • 주요 설정

    항목설명
    Authority ModeServer (기본) 또는 Owner. 플레이어 캐릭터는 Owner 로 설정하는 것이 일반적
    Sync Position X / Y / Z축별로 위치 동기화 여부를 개별 설정. 2D 게임이라면 불필요한 축을 끌 수 있음.
    Sync Rotation X / Y / Z축별 회전 동기화 여부. 기본값은 모두 활성화
    Sync Scale스케일 동기화 여부. 대부분의 게임에서 필요 없으므로 꺼 두는 것이 트래픽 절약에 좋음.
    Interpolate수신 데이터를 부드럽게 보간해 표시. 기본값 true 를 유지하는 것을 권장.
    In Local Spacetrue 면 World Space 가 아닌 부모 기준 Local Space 로 동기화. 차량 탑승 같은 부모-자식 구조에 사용

Authority 모델

  • Authority(권한) -> "누가 데이터를 변경할 수 있는가" 를 결정하는 모델

Server Authority (기본값)

  • 서버가 최종 위치를 결정
  • 클라이언트는 입력을 서버에 보내고, 서버가 처리한 결과를 모든 클라이언트에 전파
  • 장점
    • 치팅 방지에 유리 (서버가 모든 결과를 검증)
    • 모든 클라이언트가 동일한 상태를 보장
  • 단점
    • 입력 후 결과 반영까지 왕복 지연 시간만큼 기다려야 함
    • 서버 부하 증가

Owner Authority

  • 소유자 클라이언트가 직접 위치를 변경하고, 서버를 거쳐 다른 클라이언트에 전파
  • 장점
    • 입력 즉시 반영 (지연 체감 없음)
    • 서버 부하 감소
  • 단점
    • 클라이언트가 데이터를 조작할 수 있음 (치팅 가능)
    • 클라이언트 간 일시적 불일치 발생 가능

게임 장르별 권장 설정

장르권장 Authority이유
경쟁 FPS / PvPServer Authority치팅 방지 필수
협동 PvEOwner Authority치팅 위험 낮음, 반응성 우선
캐주얼 · 파티 게임Owner Authority구현 간단, 지연 체감 최소화
MMORPGServer Authority아이템 · 경제 시스템 서버 검증 필수

Teleport — 보간 없이 즉시 이동

  • 보간을 건너뛰고 즉시 위치를 이동
  • 리스폰, 씬 전환 후 스폰 포인트 배치, 순간이동 등에 사용
// 권한이 있는 피어에서만 호출 가능
// (Server 모드면 서버, Owner 모드면 소유자)
var netTransform = GetComponent<NetworkTransform>();
netTransform.Teleport(spawnPoint.position, Quaternion.identity, Vector3.one);
  • 일반 transform.position 대입과의 차이
    • 일반적으로 transform.position += ... 같은 변경은 NetworkTransform이 감지해 자동으로 동기화.
    • 그러나 큰 거리를 한 번에 이동시킬 때는 보간 때문에 캐릭터가 미끄러지듯 움직여 보임. 리스폰 · 텔레포트처럼 시각적으로 즉시 이동해야 할 때는 반드시 Teleport() 를 사용.
profile
안녕하세요

0개의 댓글