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
모딩 기초

이벤트 버스 — Mod / NeoForge 두 가지 차이

NeoForge 26의 Mod EventBus와 NeoForge EventBus 두 가지를 구분하고 어느 이벤트가 어느 버스에 가는지, 등록 방식 4가지를 학습합니다.

이벤트 버스 — Mod / NeoForge 두 가지 차이

NeoForge 26에는 이벤트 버스가 두 개 있습니다. 처음 모딩을 시작하면 가장 많이 헷갈리는 부분이 바로 이 두 버스의 구분입니다. 어느 이벤트를 어느 버스에 등록해야 하는지 잘못 파악하면 이벤트 핸들러가 아예 호출되지 않습니다.

이 챕터에서는 두 버스의 역할 차이, 각 버스에 속하는 이벤트 목록, 그리고 등록 방식 4가지를 모두 다룹니다.


두 버스 한눈에 비교

항목Mod EventBusNeoForge EventBus
접근 방법생성자 인자 IEventBus modEventBusNeoForge.EVENT_BUS 정적 필드
이벤트 시점라이프사이클 (모드 로딩·초기화)런타임 (게임 진행 중)
대표 이벤트FMLClientSetupEvent, RegisterEventServerStartingEvent, EntityJoinLevelEvent
주요 용도모드 초기화, 레지스트리 등록게임 중 동작 처리, 플레이어 상호작용
버스 결정 방식이벤트 타입에 따라 자동이벤트 타입에 따라 자동

두 버스는 완전히 분리된 채널입니다. Mod EventBus에 등록한 핸들러는 NeoForge EventBus 이벤트를 받지 못하고, 반대도 마찬가지입니다. NeoForge 26.1.2부터는 핸들러가 어느 버스에 등록될지 이벤트 타입을 보고 자동으로 결정되므로, 더 이상 @EventBusSubscriber에 버스를 직접 지정하지 않습니다.


어느 이벤트가 어느 버스인가

Mod EventBus 이벤트 (라이프사이클)

모드가 로딩되는 동안에만 발생하는 이벤트들입니다. 게임이 완전히 시작된 뒤에는 다시 발생하지 않습니다.

이벤트설명
FMLCommonSetupEvent서버·클라이언트 공통 초기화
FMLClientSetupEvent클라이언트 전용 초기화 (렌더러, 키 바인딩 등)
RegisterEvent레지스트리 직접 등록 (DeferredRegister 미사용 시)
BuildCreativeModeTabContentsEvent크리에이티브 탭에 아이템 추가
RegisterColorHandlersEvent블록·아이템 색상 핸들러 등록

NeoForge EventBus 이벤트 (런타임)

게임이 실행되는 동안 반복적으로 발생하는 이벤트들입니다.

이벤트설명
ServerStartingEvent서버(월드) 시작 직전
ServerStoppingEvent서버 종료 직전
EntityJoinLevelEvent엔티티가 월드에 스폰될 때
PlayerInteractEvent플레이어가 블록·아이템과 상호작용할 때
LivingHurtEvent생물체가 피해를 받을 때
RegisterCommandsEvent명령어 등록

이벤트 흐름 다이어그램

두 버스가 어떻게 분기되는지 시각적으로 확인하세요.

다이어그램 렌더링 중…

등록 방식 4가지

NeoForge 26에서 이벤트 핸들러를 등록하는 방법은 4가지입니다. 상황에 따라 적합한 방식이 다릅니다.

방식 1. 생성자에서 addListener 직접 호출

가장 명시적인 방법입니다. ExampleMod.java 생성자에서 볼 수 있는 패턴입니다.

// ExampleMod.java
public ExampleMod(IEventBus modEventBus, ModContainer modContainer) {
    // Mod EventBus — 라이프사이클 이벤트
    modEventBus.addListener(this::commonSetup);
    modEventBus.addListener(this::addCreative);
 
    // DeferredRegister도 modEventBus에 연결
    BLOCKS.register(modEventBus);
    ITEMS.register(modEventBus);
    CREATIVE_MODE_TABS.register(modEventBus);
 
    // NeoForge EventBus — 런타임 이벤트
    // this 클래스에 @SubscribeEvent 메서드가 있을 때만 필요
    NeoForge.EVENT_BUS.register(this);
}
 
private void commonSetup(FMLCommonSetupEvent event) {
    LOGGER.info("공통 초기화 완료");
}
 
private void addCreative(BuildCreativeModeTabContentsEvent event) {
    if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) {
        event.accept(EXAMPLE_BLOCK_ITEM);
    }
}

examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java 68~89번째 줄이 이 패턴의 실제 구현입니다.

방식 2. @EventBusSubscriber + @SubscribeEvent 어노테이션

별도 클래스에 이벤트 핸들러를 모아두고 싶을 때 사용합니다. FML이 자동으로 등록해줍니다.

// ModEvents.java — Mod EventBus 이벤트 전용 클래스
// FMLCommonSetupEvent·FMLClientSetupEvent는 Mod EventBus 이벤트이므로 자동으로 Mod 버스에 등록됩니다.
@EventBusSubscriber(modid = ExampleMod.MODID)
public class ModEvents {
 
    @SubscribeEvent
    public static void onCommonSetup(FMLCommonSetupEvent event) {
        ExampleMod.LOGGER.info("공통 셋업 이벤트 처리");
    }
 
    @SubscribeEvent
    public static void onClientSetup(FMLClientSetupEvent event) {
        // 클라이언트 전용 초기화
    }
}
// GameEvents.java — NeoForge EventBus 이벤트 전용 클래스
// ServerStartingEvent·EntityJoinLevelEvent는 NeoForge EventBus 이벤트이므로 자동으로 NeoForge 버스에 등록됩니다.
@EventBusSubscriber(modid = ExampleMod.MODID)
public class GameEvents {
 
    @SubscribeEvent
    public static void onServerStarting(ServerStartingEvent event) {
        ExampleMod.LOGGER.info("서버 시작!");
    }
 
    @SubscribeEvent
    public static void onEntityJoin(EntityJoinLevelEvent event) {
        // 엔티티 스폰 처리
    }
}

@SubscribeEvent 메서드는 반드시 public static이어야 합니다. 인스턴스 메서드로 만들면 동작하지 않습니다. NeoForge 26.1.2부터는 bus 속성이 삭제되어, 핸들러가 받는 이벤트 타입을 보고 어느 버스에 등록될지 자동으로 결정됩니다(net.neoforged.fml.common.EventBusSubscriber).

방식 3. NeoForge.EVENT_BUS.register(인스턴스)

인스턴스 메서드로 핸들러를 작성하고 싶을 때 사용합니다. ExampleMod.java의 onServerStarting이 이 방식으로 동작합니다.

// ExampleMod.java 생성자에서
NeoForge.EVENT_BUS.register(this);
 
// 같은 클래스 안에서 @SubscribeEvent 인스턴스 메서드
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event) {
    LOGGER.info("서버 시작 이벤트 수신");
}

또는 별도 핸들러 클래스를 만들어 등록할 수도 있습니다.

// 생성자에서
NeoForge.EVENT_BUS.register(new GameEventHandler());
 
// GameEventHandler.java
public class GameEventHandler {
    @SubscribeEvent
    public void onPlayerInteract(PlayerInteractEvent event) {
        // 플레이어 상호작용 처리
    }
}

방식 4. addListener 람다·메서드 참조 (동적)

타입 추론이 명확하고 람다로 간결하게 작성하고 싶을 때 사용합니다.

// 생성자에서 — NeoForge EventBus에 동적 등록
NeoForge.EVENT_BUS.addListener(GameEventHandler::onPlayerLogin);
NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> {
    LOGGER.info("서버 시작: {}", e.getServer().name());
});
 
// GameEventHandler.java
public class GameEventHandler {
    public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
        // 플레이어 로그인 처리
    }
}

방식 선택 가이드

상황권장 방식
메인 모드 클래스에서 라이프사이클 처리방식 1 (addListener)
이벤트 핸들러를 별도 클래스로 분리방식 2 (@EventBusSubscriber)
인스턴스 상태가 필요한 핸들러방식 3 (register(인스턴스))
조건부·동적 등록방식 4 (addListener 람다)

안티패턴 주의

⚠️ 잘못된 버스 등록

ServerStartingEvent는 NeoForge EventBus만 받습니다. modEventBus에 등록하면 호출되지 않습니다.

// ❌ 잘못됨 — modEventBus는 ServerStartingEvent를 받지 않음
modEventBus.addListener(this::onServerStarting);

해결: NeoForge.EVENT_BUS.addListener(this::onServerStarting);

⚠️ 삭제된 bus 속성을 계속 사용

NeoForge 26.1.2부터 @EventBusSubscriber의 bus 속성과 EventBusSubscriber.Bus enum이 삭제되었습니다. 핸들러가 받는 이벤트 타입을 보고 Mod 버스인지 NeoForge 버스인지 자동으로 결정됩니다. 클라이언트 전용으로 제한하려면 value = Dist.CLIENT만 지정합니다.

// ❌ 잘못됨 — bus 속성과 Bus enum은 26.1.2에서 삭제됨 (컴파일 실패)
@EventBusSubscriber(modid = "examplemod", bus = EventBusSubscriber.Bus.MOD)
public class ModEvents {
    @SubscribeEvent
    public static void onClientSetup(FMLClientSetupEvent event) { ... }
}
 
// ✅ 올바름 — bus 미지정, 이벤트 타입으로 자동 결정. 클라이언트 전용이면 value = Dist.CLIENT
@EventBusSubscriber(modid = "examplemod", value = Dist.CLIENT)
public class ModEvents {
    @SubscribeEvent
    public static void onClientSetup(FMLClientSetupEvent event) { ... }
}

⚠️ @SubscribeEvent 메서드를 static으로 만들지 않음

@EventBusSubscriber로 자동 등록할 때 핸들러 메서드는 반드시 public static이어야 합니다.

// ❌ 잘못됨 — static 누락
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event) { ... }
 
// ✅ 올바름
@SubscribeEvent
public static void onServerStarting(ServerStartingEvent event) { ... }

실습: ExampleMod.java 분석

examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java를 열고 생성자를 확인하세요.

public ExampleMod(IEventBus modEventBus, ModContainer modContainer) {
    modEventBus.addListener(this::commonSetup);      // Mod Bus — 라이프사이클
    BLOCKS.register(modEventBus);                     // Mod Bus — 레지스트리
    ITEMS.register(modEventBus);                      // Mod Bus — 레지스트리
    CREATIVE_MODE_TABS.register(modEventBus);         // Mod Bus — 레지스트리
    NeoForge.EVENT_BUS.register(this);               // NeoForge Bus — 런타임
    modEventBus.addListener(this::addCreative);       // Mod Bus — 크리에이티브 탭
    modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
}

NeoForge.EVENT_BUS.register(this)가 있기 때문에 아래 @SubscribeEvent 메서드가 동작합니다.

@SubscribeEvent
public void onServerStarting(ServerStartingEvent event) {
    LOGGER.info("HELLO from server starting");
}

이 메서드를 modEventBus.addListener(this::onServerStarting)으로 바꾸면 서버 시작 시 로그가 출력되지 않습니다. 직접 바꿔보고 확인해보세요.


정리

  • NeoForge 26에는 Mod EventBus(라이프사이클)와 NeoForge EventBus(런타임) 두 개가 있습니다.
  • 이벤트가 어느 버스에 속하는지 모를 때는 NeoForge 공식 문서나 이벤트 클래스의 Javadoc을 확인하세요.
  • 등록 방식 4가지 중 프로젝트 구조에 맞는 것을 선택하되, 한 프로젝트 안에서 일관성을 유지하세요.
  • 다음 챕터에서는 DeferredRegister를 사용해 아이템과 블록을 실제로 등록하는 방법을 다룹니다.

@Mod 클래스와 생성자 패턴

NeoForge 26의 @Mod 어노테이션과 생성자 시그니처(IEventBus, ModContainer)를 이해하고 mod-id 매칭, 이벤트 버스 등록 방법을 학습합니다.

Registry와 DeferredRegister 개념

Minecraft 레지스트리 시스템 개요와 NeoForge의 DeferredRegister 사용 패턴(register, Holder)을 학습합니다.

On this page

이벤트 버스 — Mod / NeoForge 두 가지 차이두 버스 한눈에 비교어느 이벤트가 어느 버스인가Mod EventBus 이벤트 (라이프사이클)NeoForge EventBus 이벤트 (런타임)이벤트 흐름 다이어그램등록 방식 4가지방식 1. 생성자에서 addListener 직접 호출방식 2. @EventBusSubscriber + @SubscribeEvent 어노테이션방식 3. NeoForge.EVENT_BUS.register(인스턴스)방식 4. addListener 람다·메서드 참조 (동적)방식 선택 가이드안티패턴 주의실습: ExampleMod.java 분석정리
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