개발에 AtoZ까지

[에러] Name for argument of type [java.lang.String] not specified — Spring Boot 3.2부터 파라미터 이름이 사라진 이유 본문

백엔드

[에러] Name for argument of type [java.lang.String] not specified — Spring Boot 3.2부터 파라미터 이름이 사라진 이유

AtoZ 개발자 2026. 7. 28. 00:05
반응형

🚨 버전만 올렸는데 컨트롤러가 통째로 죽었습니다

안녕하세요 😀

버전만 올렸는데 컨트롤러가 통째로 500을 뱉는 경험, 한 번쯤 하셨을 겁니다. Spring Boot를 3.1에서 3.2 이상으로 올린 직후에 특히 자주 나옵니다. 로그를 열어 보면 이런 문장이 찍혀 있습니다.

java.lang.IllegalArgumentException:
Name for argument of type [java.lang.String] not specified,
and parameter name information not available via reflection.
Ensure that the compiler uses the '-parameters' flag.

황당한 건 컨트롤러 코드에는 파라미터 이름이 멀쩡히 적혀 있다는 점입니다. @RequestParam String keyword처럼 평범하게 쓴 코드가 갑자기 문제가 됩니다. 그래서 컨트롤러를 아무리 들여다봐도 원인이 안 보입니다.

결론부터 말씀드리면 원인은 코드가 아니라 컴파일 옵션입니다. 이 글을 다 읽으시면 네 가지가 정리됩니다. 왜 이름이 사라졌는지, Maven·Gradle·Kotlin·IDE에서 각각 어디를 고치는지, 고친 게 진짜 적용됐는지 확인하는 방법, 그리고 전송 방식별로 파라미터를 받는 2026년 기준 정리입니다.

이 글은 제가 직접 마이그레이션한 기록이 아니라, Spring 공식 릴리스 노트·레퍼런스 문서와 프레임워크 소스를 기준으로 정리한 내용입니다. 확인일은 2026년 7월 28일이고, 근거 링크는 글 마지막에 모아 두었습니다.


🔍 이름이 사라진 진짜 이유

에러 메시지가 이미 -parameters 플래그를 쓰라고 알려 주긴 합니다. 문제는 그 플래그를 어디에 넣어야 하는지, 그리고 왜 어제까지는 없어도 됐는지를 안 알려 준다는 점입니다.

자바 컴파일러는 파라미터 이름을 리플렉션이 읽을 수 있는 자리에 기본적으로 남기지 않습니다. 디버깅용 정보에는 이름이 남지만, 그건 표준 리플렉션 API가 들여다보는 자리가 아닙니다. 그래서 옵션 없이 컴파일하면 실행 중에는 arg0, arg1처럼만 보입니다.

그런데 Spring은 파라미터 이름이 필요합니다. @RequestParam String keyword에서 괄호 안에 이름을 안 적으면, 변수명 keyword를 쿼리스트링 키로 써야 하니까요.

그래서 예전 Spring은 이름을 알아내려고 클래스 파일의 디버그 정보를 직접 뜯어봤습니다. 그 역할을 하던 게 LocalVariableTableParameterNameDiscoverer라는 클래스입니다. 쉽게 말하면, 리플렉션이 못 보는 디버그 영역까지 들어가 원래 변수명을 주워 오던 장치입니다.

이 장치가 Spring Framework 6.1에서 제거됐습니다. 릴리스 노트의 표현은 이렇습니다.

LocalVariableTableParameterNameDiscoverer has been removed in 6.1. Consequently, code within the Spring Framework and Spring portfolio frameworks no longer attempts to deduce parameter names by parsing bytecode.

Spring Framework 6.1을 쓰는 버전이 바로 Spring Boot 3.2입니다. 그래서 3.2로 올리는 순간부터 이 에러가 터지기 시작합니다. Spring Boot 3.2 릴리스 노트도 같은 이야기를 합니다.

The version of Spring Framework used by Spring Boot 3.2 no longer attempts to deduce parameter names by parsing bytecode. If you experience issues with dependency injection or property binding, you should double check that you are compiling with the -parameters option.

이제 Spring은 자바 표준 리플렉션으로만 이름을 읽습니다. 그리고 자바 표준 리플렉션이 파라미터 이름을 읽으려면, 컴파일할 때 -parameters 옵션이 켜져 있어야 합니다. 이 옵션은 자바 8부터 있던 기능인데, 기본값이 꺼짐이라 그동안 신경 쓸 일이 없었습니다.

정리하면 이렇습니다. 예전에는 우회로가 있어서 옵션을 안 켜도 굴러갔고, 지금은 그 우회로가 없어졌습니다. 그래서 원래부터 켰어야 할 옵션이 안 켜져 있었다는 사실이 이제야 드러난 것에 가깝습니다.


⚠️ 이 에러가 컨트롤러에서만 나는 게 아닙니다

메시지가 @RequestParam에서 처음 터지다 보니 웹 문제로 착각하기 쉽습니다. 하지만 파라미터 이름을 쓰는 곳은 더 많습니다.

Spring Framework 6.1 릴리스 노트는 영향 범위를 의존성 주입, 프로퍼티 바인딩, SpEL 등으로 안내합니다. 여기서 SpEL은 Spring이 쓰는 표현식 문법으로, 애너테이션 안에 #파라미터명 같은 식을 적을 때 쓰입니다. 실제로 아래 자리들이 같이 깨질 수 있습니다.

깨지는 자리 증상
@RequestParam, @PathVariable, @RequestHeader, @CookieValue 요청 시 IllegalArgumentException, 응답 500
같은 타입 빈이 여러 개라 이름으로 구분하던 주입 후보를 못 좁혀 컨텍스트 로딩 실패
@ConfigurationProperties 생성자 바인딩 프로퍼티 값이 전부 null
SpEL에서 파라미터 이름을 참조하는 표현식 표현식 평가 실패
@Cacheable처럼 파라미터 이름으로 키를 만드는 자리 캐시 키 생성 실패

그래서 컨트롤러 한 곳만 고치는 방식으로는 끝나지 않습니다. 빌드 전체에 옵션을 켜는 게 정답입니다.


🛠️ Maven·Gradle·Kotlin·IDE에서 고치는 방법

Maven을 쓰는 경우

가장 흔한 원인은 spring-boot-starter-parent상속하지 않은 프로젝트입니다. Spring Boot Maven 플러그인 문서를 보면, 부모 POM이 제공하는 기본값 목록에 "Compilation with -parameters"가 들어 있습니다. 즉 부모를 상속했다면 이미 켜져 있습니다.

부모 대신 BOM만 dependencyManagement로 가져다 쓰는 구조라면 직접 켜야 합니다. 아래를 pom.xml에 넣으시면 됩니다.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <parameters>true</parameters>
      </configuration>
    </plugin>
  </plugins>
</build>

maven-compiler-plugin 설정을 이미 직접 잡아 둔 프로젝트도 위험합니다. 부모의 설정을 덮어쓰면서 <parameters>만 빠지는 일이 자주 생깁니다.

Gradle을 쓰는 경우

Spring Boot Gradle 플러그인 문서에는 이런 문장이 있습니다.

Configures any JavaCompile tasks to use the -parameters compiler argument.

Spring Boot 플러그인과 java 플러그인을 같이 쓰고 있다면 이미 켜져 있다는 뜻입니다. 문제는 Spring Boot 플러그인을 안 붙인 모듈입니다. 멀티모듈 프로젝트에서 도메인 모듈이나 공통 모듈에만 에러가 나는 경우가 여기에 해당합니다.

그런 모듈에는 아래를 넣습니다.

tasks.withType(JavaCompile).configureEach {
    options.compilerArgs.add('-parameters')
}

Kotlin DSL(build.gradle.kts)이라면 이렇게 씁니다.

tasks.withType<JavaCompile>().configureEach {
    options.compilerArgs.add("-parameters")
}

Groovy 소스가 섞여 있으면 GroovyCompile 쪽에도 parameters 옵션을 켜야 한다고 릴리스 노트가 안내합니다.

Kotlin으로 쓴 프로젝트라면

Kotlin은 자바가 아니라 Kotlin 컴파일러가 클래스를 만듭니다. 그래서 -parameters가 아니라 Kotlin 쪽 옵션인 -java-parameters 를 켜야 합니다. 릴리스 노트에도 "With the Kotlin compiler, use the -java-parameters flag."라고 못 박혀 있습니다.

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-java-parameters")
    }
}

자바와 Kotlin을 같이 쓰는 프로젝트라면 두 옵션을 모두 켜야 합니다. 한쪽만 켜면 그쪽 소스만 멀쩡하고 나머지에서 같은 에러가 계속 납니다.

IDE로 직접 빌드하는 경우

여기서 많이 헤맵니다. 빌드 스크립트는 멀쩡한데 IDE에서 실행하면 계속 에러가 나는 상황입니다.

IDE가 Gradle이나 Maven에 빌드를 위임하지 않고 자체 컴파일러로 클래스를 만들면, 빌드 스크립트의 옵션이 적용되지 않습니다. Spring Framework 6.1 릴리스 노트도 IntelliJ IDEA·Eclipse·VSCode에서 별도 설정이 필요할 수 있다고 명시합니다.

가장 확실한 해결은 빌드를 IDE가 아니라 Gradle/Maven에 위임하도록 바꾸는 것입니다. 그게 어렵다면 IDE의 자바 컴파일러 설정에 -parameters를 추가하면 됩니다.


✅ 진짜 적용됐는지 확인하는 방법

옵션을 넣고 나면 "이게 실제로 먹었나"가 궁금해집니다. 빌드가 성공했다고 적용된 게 아니라서, 클래스 파일을 직접 확인하는 편이 빠릅니다.

방법 1. 클래스 파일을 열어 봅니다.

javap -v build/classes/java/main/com/example/MyController.class | grep -A5 MethodParameters

MethodParameters 항목이 보이고 그 아래에 keyword 같은 실제 이름이 있으면 성공입니다. 아무것도 안 나오면 옵션이 안 먹은 겁니다. Maven이라면 경로를 target/classes/...로 바꾸시면 됩니다.

방법 2. 애플리케이션 안에서 코드로 확인합니다.

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNameCheck {
    public static void main(String[] args) throws Exception {
        Method m = MyController.class.getDeclaredMethod("search", String.class);
        for (Parameter p : m.getParameters()) {
            System.out.println(p.getName() + " / isNamePresent=" + p.isNamePresent());
        }
    }
}

isNamePresent=true이고 이름이 arg0이 아니라 실제 변수명으로 찍히면 정상입니다. arg0 / isNamePresent=false가 나오면 아직 안 켜진 상태입니다.


🧩 고쳤는데도 같은 에러가 계속 난다면

옵션을 넣었는데도 그대로인 경우가 있습니다. 확인 순서를 정해 두면 시간을 아낄 수 있습니다.

첫째, 이전 빌드 결과가 남아 있는 경우입니다. 옵션을 바꿔도 이미 컴파일된 클래스는 다시 만들어지지 않을 수 있습니다. ./gradlew clean build 또는 mvn clean package로 전체를 다시 만들어 보시면 됩니다.

둘째, 문제가 내 모듈이 아니라 의존하는 라이브러리인 경우입니다. 외부 JAR이 -parameters 없이 빌드됐다면 내 빌드 설정으로는 해결되지 않습니다. 이럴 때는 해당 지점에 이름을 명시적으로 적는 게 유일한 방법입니다.

// 이름을 직접 적으면 컴파일 옵션과 무관하게 동작합니다
@GetMapping("/search")
public List<Item> search(@RequestParam("keyword") String keyword) { ... }

셋째, 멀티모듈에서 일부 모듈만 빠진 경우입니다. 루트에만 설정을 넣고 하위 모듈에 적용이 안 된 구성이 흔합니다. 에러가 나는 클래스가 어느 모듈 소속인지부터 확인하시는 게 좋습니다.

넷째, Java Agent가 이름을 지우고 있는 경우입니다. 이건 잘 안 알려진 경우인데, 릴리스 노트에 따로 주의가 적혀 있습니다. APM이나 모니터링 도구처럼 실행 시점에 바이트코드를 건드리는 에이전트를 붙이면, 런타임에 파라미터 이름 정보가 사라질 수 있습니다.

Note that if you are using Java Agents in your deployment, such agents can modify the bytecode and lose the "parameters" information at runtime. This has been fixed in JDK 19 and might get backported to oder JDK versions.

즉 빌드는 멀쩡한데 운영 환경에서만 터지는 상황이 생길 수 있습니다. 로컬에서 재현이 안 되고 서버에서만 500이 난다면 이쪽을 의심해 보시면 됩니다. 릴리스 노트는 이 문제를 겪는다면 최신 JDK로 올리는 걸 권합니다.

다섯째, 메시지 문구가 조금 다른 경우입니다. Spring Boot 3.2.0 초기에 올라온 이슈들에는 아래 문구도 보입니다.

Name for argument of type [java.util.UUID] not specified,
and parameter name information not found in class file either.

문구는 달라도 원인은 같습니다. 현재 프레임워크 소스에 들어 있는 문구는 ... not available via reflection. Ensure that the compiler uses the '-parameters' flag. 쪽입니다.


📮 겸사겸사 정리하는 전송 방식별 파라미터 받기

이 에러를 고치다 보면 "그래서 어떤 상황에 뭘 써야 하지"가 다시 헷갈립니다. 2021년에 정리했던 전송방식에 따른 Parameter 받는 방법 글을 2026년 기준으로 다시 세워 봤습니다.

클라이언트가 보내는 방식 Content-Type 받는 방법
쿼리스트링 (/search?keyword=강남) 없음 @RequestParam String keyword
경로에 값이 박힌 형태 (/items/42) 없음 @PathVariable Long id
HTML 폼 전송 (POST) application/x-www-form-urlencoded @RequestParam 또는 @ModelAttribute ItemForm form
JSON 본문 application/json @RequestBody ItemRequest req
파일 업로드 multipart/form-data @RequestParam MultipartFile file 또는 @RequestPart
헤더 값 해당 없음 @RequestHeader("X-Api-Key") String key

몇 가지 헷갈리기 쉬운 지점만 덧붙입니다.

애너테이션을 안 붙이면 타입으로 결정됩니다. Spring MVC 레퍼런스는 이렇게 설명합니다. 문자열이나 숫자 같은 단순 타입이면 @RequestParam으로, 그 밖에는 @ModelAttribute로 해석합니다. 그래서 애너테이션을 생략한 코드도 파라미터 이름에 의존하게 되고, 이번 에러의 사정권에 들어옵니다.

@RequestBody@ModelAttribute는 목적이 다릅니다. JSON 본문은 메시지 컨버터가 객체로 바꿔 주고, 폼 데이터는 데이터 바인딩으로 채워집니다. JSON을 보내면서 @ModelAttribute를 쓰면 값이 안 들어옵니다.

PUT·PATCH로 폼 데이터를 보내면 값이 비어 있을 수 있습니다. 이건 Spring이 아니라 Servlet 규격 문제입니다. Spring 레퍼런스의 설명은 이렇습니다.

The Servlet API requires ServletRequest.getParameter*() methods to support form field access only for HTTP POST.

그래서 Spring은 FormContentFilter로 PUT·PATCH·DELETE의 application/x-www-form-urlencoded 요청을 감싸 줍니다. Spring Boot에서는 spring.mvc.formcontent.filter.enabled가 기본값 true라 자동으로 켜져 있습니다. 이 값을 꺼 둔 프로젝트에서 PUT 폼 전송이 안 되는 사례가 여기서 나옵니다.


📋 업그레이드 전에 훑어 볼 체크리스트

  • ☐ 빌드 도구에 -parameters가 켜져 있는가 (Maven <parameters>true</parameters> / Gradle options.compilerArgs)
  • spring-boot-starter-parent를 안 쓰는 프로젝트인가
  • maven-compiler-plugin 설정을 직접 덮어쓰면서 <parameters>를 빠뜨리지 않았는가
  • ☐ 멀티모듈이라면 모든 모듈에 적용됐는가
  • ☐ Kotlin 소스가 있다면 -java-parameters도 같이 켰는가
  • ☐ IDE가 자체 컴파일러로 빌드하고 있지는 않은가
  • javap -vMethodParameters가 보이는가
  • @ConfigurationProperties 생성자 바인딩 값이 null로 안 들어오는가
  • ☐ 외부 라이브러리에서 나는 에러라면 이름을 명시적으로 적었는가
  • ☐ 로컬은 되는데 서버만 실패한다면 Java Agent와 JDK 버전을 확인했는가

🧭 이런 경우에 이 방법을 쓰시면 됩니다

바로 -parameters를 켜시면 되는 경우는 내가 빌드를 통제할 수 있는 프로젝트입니다. 설정 두세 줄이면 끝나고, 애너테이션을 전부 손볼 필요가 없습니다.

이름을 하나씩 명시하는 편이 나은 경우도 있습니다. 빌드 설정을 바꾸기 어려운 조직이거나, 문제 지점이 외부 라이브러리에 있는 경우입니다. 다만 이건 그 지점만 막는 방식이라, 나중에 다른 곳에서 또 터질 수 있습니다.

버전을 당분간 올리지 않기로 한 팀이라면 이 에러 자체를 만나지 않습니다. 다만 그건 문제를 해결한 게 아니라 미룬 것입니다. 2026년 7월 기준으로 Spring Boot 3.5의 오픈소스 지원은 2026년 6월 30일에 끝났고, 최신 GA는 2026년 6월 10일에 나온 4.1입니다. 상용 연장 지원을 따로 받는 조직이 아니라면, 언젠가는 넘어와야 하는 구간입니다. 그때 막히는 지점이 결국 컴파일 옵션 한 줄이라는 점만 기억해 두시면 됩니다.


➡️ 다음 글에서 이어서

파라미터를 제대로 받게 만들었다면, 그다음 관문은 에러가 났을 때 무엇을 내려보낼 것인가입니다. 예전에 정리했던 HTTP Status Code 제어 글도 지금은 손볼 데가 생겼습니다. Spring Boot 3부터는 ProblemDetail이라는 표준 에러 응답 형식이 프레임워크에 들어와 있기 때문입니다.

다음 글에서는 그 ProblemDetail로 상태 코드와 에러 본문을 함께 설계하는 방법을 정리하겠습니다.


📚 참고 자료

  1. Spring Framework 6.1 Release Notes — Parameter Name Retention
  2. Spring Boot 3.2 Release Notes — Parameter Name Discovery
  3. Spring MVC 레퍼런스 — Method Arguments
  4. Spring Framework 레퍼런스 — Filters (FormContentFilter)
  5. Spring Boot Gradle Plugin — Reacting to Other Plugins
  6. Spring Boot Maven Plugin — Using the Plugin
  7. Spring Boot 릴리스·지원 종료 일정 (endoflife.date)
반응형
Comments