NeoForge 26.1 Docs
  • 문서
  • 노트
NeoForge 26.1
NeoForge 26.1
v26.1
학습 진도
0 / 77 챕터 완료방문 0
@Mod 클래스와 생성자 패턴이벤트 버스 — Mod / NeoForge 두 가지 차이Registry와 DeferredRegister 개념ModConfigSpec과 설정 GUISLF4J Logger와 로그 레벨클라이언트/서버 분리와 Dist모드 내 다국어 — lang/<locale>.json
모딩 기초

클라이언트/서버 분리와 Dist

Minecraft의 물리적/논리적 사이드 개념과 NeoForge의 Dist enum, @OnlyIn 어노테이션, ExampleModClient 분리 패턴을 학습합니다.

클라이언트/서버 분리와 Dist

Minecraft 모딩에서 가장 흔한 크래시 원인 중 하나는 "서버에서 클라이언트 코드를 호출하는 것"입니다. NoClassDefFoundError, NullPointerException, 서버 시작 실패. 이 챕터에서는 왜 이런 일이 생기는지, 그리고 NeoForge가 어떻게 이 문제를 구조적으로 해결하는지 배웁니다.


1. 물리적 사이드 vs 논리적 사이드

"사이드(side)"라는 단어가 Minecraft 문서에서 두 가지 의미로 쓰입니다. 혼동하기 쉬우니 먼저 정리합니다.

물리적 사이드

실제로 실행되는 프로세스 기준입니다.

물리적 사이드실행 파일포함 내용
물리적 클라이언트minecraft.exe / java -jar minecraft.jar렌더링 엔진 + 게임 로직
물리적 서버server.jar게임 로직만 (렌더링 없음)

물리적 서버 JAR에는 Minecraft 클래스 자체가 없습니다. net.minecraft.client.* 패키지 전체가 빠져 있습니다. 그래서 서버에서 Minecraft.getInstance()를 호출하면 NoClassDefFoundError가 납니다.

논리적 사이드

단일 플레이어(싱글플레이)를 생각해 보세요. 프로세스는 하나지만 내부적으로는 논리 서버와 논리 클라이언트가 동시에 돌아갑니다.

단일 플레이어 프로세스 (물리적 클라이언트)
├── 논리 클라이언트  ← 렌더링, 입력 처리
└── 논리 서버       ← 게임 로직, 엔티티 틱

멀티플레이에서는 논리 서버가 별도 프로세스(물리적 서버)로 분리됩니다.

ℹ️ 왜 구분이 중요한가?
모드 코드는 양쪽에서 로드됩니다. 물리적 클라이언트에서는 클라이언트 코드가 안전하지만, 물리적 서버에서는 클라이언트 전용 클래스가 아예 없습니다. 이 차이를 무시하면 서버 크래시가 납니다.


2. Dist enum

NeoForge는 물리적 사이드를 Dist enum으로 표현합니다.

// net.neoforged.api.distmarker.Dist
public enum Dist {
    CLIENT,           // 물리적 클라이언트 (minecraft.exe)
    DEDICATED_SERVER  // 물리적 서버 (server.jar)
}

현재 실행 환경을 확인하려면:

import net.neoforged.fml.loading.FMLEnvironment;
 
if (FMLEnvironment.dist.isClient()) {
    // 클라이언트 전용 코드
}
if (FMLEnvironment.dist.isDedicatedServer()) {
    // 서버 전용 코드
}

3. ExampleModClient.java 분리 패턴

NeoForge 템플릿이 제시하는 정석 패턴입니다. 클라이언트 전용 코드를 별도 클래스로 완전히 분리합니다.

// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleModClient.java
package com.example.examplemod;
 
import net.minecraft.client.Minecraft;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.gui.ConfigurationScreen;
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
 
// 이 클래스는 Dedicated Server에서 로드되지 않습니다.
@Mod(value = ExampleMod.MODID, dist = Dist.CLIENT)
@EventBusSubscriber(modid = ExampleMod.MODID, value = Dist.CLIENT)
public class ExampleModClient {
    public ExampleModClient(ModContainer container) {
        // 설정 화면 등록 — 클라이언트 전용
        container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new);
    }
 
    @SubscribeEvent
    static void onClientSetup(FMLClientSetupEvent event) {
        // 클라이언트 전용 초기화
        ExampleMod.LOGGER.info("HELLO FROM CLIENT SETUP");
        ExampleMod.LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName());
    }
}

핵심은 두 어노테이션입니다.

어노테이션역할
@Mod(value = MODID, dist = Dist.CLIENT)FML이 이 클래스를 클라이언트에서만 인스턴스화
@EventBusSubscriber(value = Dist.CLIENT)이벤트 핸들러를 클라이언트 이벤트 버스에만 등록

4. 코드 분리 기준

어떤 코드를 어디에 두어야 할지 판단 기준입니다.

클라이언트 전용 (ExampleModClient.java)

// 이 코드들은 ExampleModClient.java 안에서만 안전합니다
Minecraft.getInstance()           // 클라이언트 싱글톤
BlockEntityRenderer               // 블록 엔티티 렌더러 등록
EntityRenderer                    // 엔티티 렌더러 등록
Screen                            // GUI 화면 클래스
SoundEngine                       // 사운드 재생
KeyMapping                        // 키 바인딩 등록

양쪽 모두 안전 (ExampleMod.java)

// 이 코드들은 ExampleMod.java에서 안전합니다
DeferredRegister                  // 아이템/블록 등록
IEventBus.addListener()           // 공통 이벤트 등록
ServerStartingEvent               // 서버 이벤트
FMLCommonSetupEvent               // 공통 초기화

5. 코드 흐름 다이어그램

다이어그램 렌더링 중…

물리적 서버에서는 ExampleModClient.java 자체가 로드되지 않습니다. @Mod(dist = Dist.CLIENT) 덕분에 FML이 서버 환경에서 이 클래스를 완전히 건너뜁니다.


6. @OnlyIn vs DistExecutor vs FMLEnvironment

세 가지 방법이 있지만 권장 순서가 다릅니다.

@OnlyIn(Dist.CLIENT) — 비권장

@OnlyIn(Dist.CLIENT)
public void clientOnlyMethod() {
    // 컴파일 타임 힌트일 뿐, 런타임 보호 없음
}

@OnlyIn은 Mixin 환경에서 주로 쓰이는 어노테이션입니다. 모드 코드에서는 런타임 보호를 제공하지 않으므로 사용을 피하세요.

DistExecutor — 레거시

// 구식 패턴 — 현재는 @Mod(dist=) 방식이 더 명확
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
    // 클라이언트 전용 코드
});

NeoForge 26에서는 @Mod(dist = Dist.CLIENT) 클래스 분리 패턴이 더 권장됩니다.

FMLEnvironment.dist — 런타임 조건 분기

if (FMLEnvironment.dist.isClient()) {
    // 클라이언트 전용 코드
}

간단한 조건 분기에는 쓸 수 있지만, 클라이언트 전용 클래스를 참조하는 코드는 이 방법으로도 보호되지 않습니다. 클래스 로더가 조건 분기와 무관하게 클래스를 로드하려 시도하기 때문입니다.

✅ 권장: 클라이언트 전용 코드는 ExampleModClient.java처럼 별도 클래스로 분리하고 @Mod(dist = Dist.CLIENT)를 붙이세요.


7. 안티패턴: 서버에서 Minecraft.getInstance() 호출

⚠️ 서버에서 Minecraft.getInstance() 호출

// ❌ 잘못됨 — Dedicated Server에서 NoClassDefFoundError
public void onServerStarting(ServerStartingEvent e) {
    Minecraft mc = Minecraft.getInstance();  // 서버에 Minecraft 클래스 없음
}

클라이언트 전용 코드는 반드시 ExampleModClient.java (Dist.CLIENT) 안에 분리해야 합니다. ExampleMod.java의 onServerStarting은 서버에서도 실행되므로 여기서 Minecraft를 참조하면 크래시가 납니다.


정리

개념핵심
물리적 클라이언트minecraft.exe — 렌더링 + 게임 로직
물리적 서버server.jar — 게임 로직만, net.minecraft.client.* 없음
Dist.CLIENT물리적 클라이언트 환경
Dist.DEDICATED_SERVER물리적 서버 환경
@Mod(dist = Dist.CLIENT)해당 클래스를 클라이언트에서만 로드
ExampleModClient.java클라이언트 전용 코드의 정석 분리 위치

다음 챕터에서는 NeoForge의 레지스트리 시스템과 DeferredRegister를 자세히 살펴봅니다.

SLF4J Logger와 로그 레벨

NeoForge 26에서 사용하는 SLF4J Logger와 LogUtils.getLogger() 패턴, 로그 레벨(trace/debug/info/warn/error)을 학습합니다.

모드 내 다국어 — lang/<locale>.json

NeoForge 26 모드의 다국어 시스템(lang/en_us.json, ko_kr.json)과 Component.translatable() 사용법, Minecraft 게임의 언어 설정에 따른 자동 적용 방법을 학습합니다.

On this page

클라이언트/서버 분리와 Dist1. 물리적 사이드 vs 논리적 사이드물리적 사이드논리적 사이드2. Dist enum3. ExampleModClient.java 분리 패턴4. 코드 분리 기준클라이언트 전용 (ExampleModClient.java)양쪽 모두 안전 (ExampleMod.java)5. 코드 흐름 다이어그램6. @OnlyIn vs DistExecutor vs FMLEnvironment@OnlyIn(Dist.CLIENT) — 비권장DistExecutor — 레거시FMLEnvironment.dist — 런타임 조건 분기7. 안티패턴: 서버에서 Minecraft.getInstance() 호출정리
NeoForge 26.1 Docs

NeoForge 26.1 모딩 개발 문서 사이트

GitHubDiscord

문서

  • 문서
  • 노트

GitHub

  • GitHub
  • Discord

© 2026 NeoForge 26.1 Docs. 콘텐츠는 MIT 라이선스로 제공됩니다.

Built with Next.js · Tailwind CSS · shadcn/ui