모드를 개발하다 보면 "지금 이 코드가 실행되고 있나?", "이 값이 맞나?" 같은 질문이 끊임없이 생깁니다. System.out.println으로 확인하고 싶은 충동이 들지만, NeoForge 환경에서는 그 방법이 통하지 않습니다. 로그 파일에 남지 않고, 필터링도 안 되고, 멀티스레드 환경에서 출력 순서도 보장되지 않습니다.
NeoForge는 SLF4J를 로깅 facade로 사용합니다. 코드에서는 SLF4J API만 호출하고, 실제 출력은 Log4j2가 처리합니다. 이 챕터에서는 LogUtils.getLogger() 패턴과 다섯 가지 로그 레벨을 익힙니다.
import com.mojang.logging.LogUtils;import org.slf4j.Logger;@Mod(ExampleMod.MODID)public class ExampleMod { public static final String MODID = "examplemod"; // SLF4J Logger — LogUtils.getLogger()가 호출 클래스 이름을 자동으로 사용 public static final Logger LOGGER = LogUtils.getLogger(); // ...}
LogUtils.getLogger()는 Mojang이 제공하는 유틸리티로, 호출한 클래스의 이름을 자동으로 logger 이름으로 씁니다. 결과적으로 로그에 [ExampleMod] 같은 prefix가 붙어서 어느 클래스에서 찍힌 로그인지 바로 알 수 있습니다.
static final로 선언하는 이유는 두 가지입니다. 클래스당 하나만 만들어 재사용하고, 어디서든 ExampleMod.LOGGER로 접근할 수 있게 합니다.
// ❌ 이렇게 하면 안 됩니다System.out.println("debug: " + value);// - 로그 파일에 남지 않음// - 로그 레벨 필터링 불가// - 멀티스레드에서 출력 순서 보장 안 됨// - 배포 전 일일이 찾아서 지워야 함// ✅ 이렇게 하세요LOGGER.debug("debug: {}", value);
⚠️ Apache Log4j 직접 import 금지
// ❌ Log4j2 직접 사용import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;private static final Logger LOGGER = LogManager.getLogger();// ✅ SLF4J + LogUtils 사용import com.mojang.logging.LogUtils;import org.slf4j.Logger;public static final Logger LOGGER = LogUtils.getLogger();
NeoForge는 SLF4J를 표준으로 씁니다. Log4j2를 직접 import하면 구현체 교체 시 코드를 전부 바꿔야 합니다.