멀티 모듈 기반의 Kotlin 프로젝트(Server/Client/Share)를 구성하고, 각 모듈에 com.github.johnrengelman.shadow 플러그인을 적용하여 Fat JAR 형태로 빌드했다.
빌드는 성공했지만, 실행 시 아래 오류가 발생했다.
C:\ java -jar .\Server\build\libs\Server-1.0-SNAPSHOT.jar
.\Server\build\libs\Server-1.0-SNAPSHOT.jar에 기본 Manifest 속성이 없습니다.
이번 글에서는 아래 사항들을 실무 관점에서 정리한다.
- 🎯 왜 이 문제가 발생했는지
- 🎯 무엇을 잘못 설정했는지
- 🎯 어떻게 수정하면 되는지
1. 문제 원인
Kotlin/Gradle에서 Shadow 플러그인을 적용하면 Gradle은 아래 두 가지 JAR을 생성한다.
| 종류 | 설명 |
| jar | 일반 JAR (의존성 포함 X) |
| shadowJar | Fat JAR (의존성 포함 O) |
문제는 다음과 같은 구성 때문이었다.
tasks.jar {
manifest {
attributes["Main-Class"] = "com.chat.server.ServerKt"
}
}
이 설정은 기본 JAR에만 적용된다.
하지만 실제 실행은 Fat JAR(= shadowJar)을 사용해야 하고, Shadow 플러그인은 기본 jar의 manifest 정보를 자동으로 shadowJar로 가져오지 않는다.
shadowJar에는 Main-Class가 없고, jar에는 있지만 실행은 shadowJar로 해야 하니 오류가 발생한 것.
이게 문제의 원인이었다.
2. 해결 방법 — shadowJar에 Main-Class를 명시한다
정답은 매우 명확하다.
Manifest 설정을 shadowJar에 직접 선언해줘야 한다.
아래처럼 Server 모듈에 적용한다.
tasks.shadowJar {
manifest {
attributes["Main-Class"] = "com.chat.server.ServerKt"
}
archiveFileName.set("Server.jar")
}
Client 모듈도 동일하게 적용:
tasks.shadowJar {
manifest {
attributes["Main-Class"] = "com.chat.server.ClientKt"
}
archiveFileName.set("Client.jar")
}

3. 실행 확인
shadowJar 빌드 후 생성되는 파일은 다음과 같다.
Server/build/libs/Server.jar
정상 실행:
java -jar Server.jar
그리고 Manifest 확인:
jar xf Server.jar META-INF/MANIFEST.MF
내용은 다음과 같아야 한다.
Main-Class: com.chat.server.ServerKt
4. 정리
이번 문제의 핵심은 다음 한 줄로 요약된다.
Gradle의 jar와 shadowJar는 완전히 별개의 태스크이며, jar에 설정한 manifest는 shadowJar에 자동 반영되지 않는다.
따라서 Shadow 플러그인을 사용하는 프로젝트에서는 반드시 shadowJar 블록에 직접 Main-Class를 포함해야 한다.
추가로, 일반 JAR를 사용하지 않는다면 아래처럼 jar 태스크를 비활성화하면 더 깔끔하다.
tasks.jar { enabled = false }
5. 마무리
멀티모듈 환경에서 ShadowJar를 사용할 때 흔히 겪는 실수로, 다음 세 가지만 기억하면 된다.
- jar와 shadowJar는 다른 JAR이다
- 실행 가능한 JAR은 shadowJar이다
- Main-Class는 shadowJar 태스크에 직접 넣어야 한다
이제 Server/Client 모두 Fat JAR로 정상 실행된다.


참고 출처
- https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow
-
Powered By. ChatGPT
'Backend' 카테고리의 다른 글
| [Backend] HTTP 발전의 역사 (1) | 2025.12.08 |
|---|---|
| [Backend] JVM - Primitive type과 Reference type (1) | 2025.12.02 |
| [Backend] Kotlin tips (0) | 2025.11.24 |
| [Backend] Jackson 기반 직렬화·역직렬화의 원리와 적용 절차 (0) | 2025.11.17 |
| [Backend] Slf4J 와 Logback 로깅 도입 가이드 (0) | 2025.11.17 |