신비한 개발사전

.jar에 기본 Manifest 속성이 없습니다 문제의 원인과 해결 방법 본문

Java

.jar에 기본 Manifest 속성이 없습니다 문제의 원인과 해결 방법

jbilee 2025. 12. 26. 23:41

간단한 콘솔 기반 자바 애플리케이션을 빌드해서 java -jar로 실행하려고 했을 때 일어난 일이다.

 

"./build/libs/java-janggi-1.0-SNAPSHOT.jar에 기본 Manifest 속성이 없습니다." 오류 메세지가 뜨면서 애플리케이션이 실행되지 않았다. 이 한 줄 외에 다른 정보는 없었다.

 

무엇이 문제였나?

우리가 코딩한 자바 애플리케이션은 JVM이 실행해준다. 이때 JVM은 애플리케이션의 entrypoint, 즉 진입점을 알아야 한다. 어느 클래스에 구현된 main() 함수를 실행시켜야 이 애플리케이션이 우리가 설계한 대로 동작할지 JVM은 알 수 없기 때문에, 우리가 명시적으로 알려줘야 한다. 그리고 그 방법은 이정표 역할을 하는 manifest 파일(MANIFEST.MF)에 Main-Class 속성을 추가하는 것이다.

 

생각해보니 난 그동안 IDE의 인터페이스를 통해서만 애플리케이션을 실행시켰었다. Entrypoint 역할을 하는 main() 함수가 어떤 클래스에 있는지 개발자인 나는 가시적으로 알고 있으니, 편의상 늘 IDE에 의존해 애플리케이션을 실행해왔던 것이다.

나는 할 수 있지만, JVM은 하지 못하는 조작

 

기본적으로 새 프로젝트를 시작할 때 manifest 파일에 Main-Class 속성이 자동으로 설정되진 않기 때문에, 개발자가 직접 빌드 파일을 손봐야 한다.

 

Gradle로 entrypoint 설정하기

Gradle로 빌드할 경우 두가지 방식으로 애플리케이션의 entrypoint를 지정할 수 있다.

 

1. application 플러그인

build.gradle 파일에 다음과 같이 'application' 플러그인을 추가하고, 속성으로 mainClass를 추가해준다. 이때 mainClass에는 main() 함수를 가진 클래스의 전체 패키지 경로를 전달한다. (해결 방안과 무관한 내용은 생략)

plugins {
    id 'java'
    id 'application'
}

application {
    mainClass = 'janggi.Application'
}

 

2. manifest 속성

build.gradle 파일에 jar.manifest 속성을 통해 entrypoint를 설정할 수도 있다. 이렇게 설정할 경우 'application' 플러그인이 필요 없다.

jar {
    manifest {
        attributes(
                'Main-Class': 'janggi.Application'
        )
    }
}

 

참고로 Kotlin DSL을 사용한다면 문법이 약간 다르다:

tasks {
    jar {
        manifest {
            attributes["Main-Class"] = "janggi.Application"
        }
    }
}

 

결과 확인

Entrypoint를 설정하기 전과 후의 manifest 파일을 비교해 봤다. 빌드한 .jar 파일에서 jar xf 명령어로 manifest 파일만 추출해 내용물을 읽을 수 있다.

 

Main-Class 설정 전:

Entrypoint 설정이 없는 MANIFEST.MF 파일

 

아무것도 없는 상태에서는 MANIFEST.MF 파일에 버전 정보 한 줄만 들어있는 것을 확인할 수 있다.

 

Main-Class 설정 후:

Entrypoint 설정 후 추출한 MANIFEST.MF 파일

 

Entrypoint를 지정하고 나니 "Main-Class: janggi.Application"이 추가됐다.

 

 

추가로 문득 Spring에서는 이렇게 entrypoint를 설정하지 않고도 java -jar을 실행하는 데에 문제가 없었다는 점이 떠올라서, Spring 프레임워크로 빌드한 애플리케이션의 manifest 파일을 확인해 봤다.

Spring은 프레임워크가 Main-Class를 대신 설정해준다. 차이점이라면 콘솔 애플리케이션과 달리 Main-Class에 JarLauncher가 들어가고, @SpringBootApplication 애노테이션이 달려있는 클래스가 Start-Class라는 속성에 들어간다.

 

 

참고:

'Java' 카테고리의 다른 글

JUnit Jupiter와 Platform의 의존성 충돌  (0) 2025.09.02
객체지향 프로그래밍의 4가지 특징  (0) 2024.10.12