void와 Void의 차이점 알아보기

2025-09-03study, java

spring으로 서버 개발을 하면서 맨 앞 대소문자 여부만 다르고 스펠링은 같은 타입들이 몇 개 있었다. 작업 일정에 밀려서 확실하게 설명할 수 있을 정도로는 알지 못한 채 그냥 사용했었는데, 이번 기회에 정리해보려고 한다.

🍮 TL;DR

  • void: 리턴 타입이 없다는 의미의 키워드
  • Void: 클래스(객체화 불가), 제네릭/Reflection 등에서 사용됨

1. void

void에 대해 알고 가야할 점 한 가지,

일단 void는 리턴 타입이 아닌 메서드가 반환 값이 없다는걸 나타내는 키워드다.

이미지 설명

나랑 같은 사람이 이미 13년 전에 질문 글을 올렸는데 😋 자바 공식 문서를 보면 void is not a type 이라고 명시되어 있었다.

반환 값이 없기 때문에 return null을 해도, return 0을 해도 ... 전부 에러가 발생한다. (💢 반환값 없다며!!! 근데 왜 있는데!!!)

2. Void

그럼 void와 Void의 차이점은 뭘까? 일단 기존 코드에서 void -> Void로 바꿔봤더니 아래와 같은 에러가 떴다.

이미지 설명

리턴 값이 없다면서 에러가 뜨는데, 처음 드는 생각은 💭넌 그래도 일단 void 잖아...? 였다. 궁금해져서 코드를 뜯어봤는데,

package java.lang;
 
/**
 * The {@code Void} class is an uninstantiable placeholder class to hold a
 * reference to the {@code Class} object representing the Java keyword
 * void.
 */
public final
class Void {
 
    /**
     * The {@code Class} object representing the pseudo-type corresponding to
     * the keyword {@code void}.
     */
    @SuppressWarnings("unchecked")
    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
 
    /*
     * The Void class cannot be instantiated.
     */
    private Void() {}
}

여기서 주목해야 할 부분은 The Void class cannot be instantiated., Void 클래스는 인스턴스화 될 수 없다는 부분이다. Java의 타입 시스템에서 모든 참조 타입 변수 -클래스도 참조 타입이다- 는 두 가지 값을 가질 수 있는데, 하나는 그 타입의 인스턴스, 다른 하나는 null (아무 객체도 참조하지 않음) 이다. 따라서 Void를 반환하는 메서드는 null을 반환할 수밖에 없다.

일단 리턴 값이 없으면 오류가 발생하는 이유는 알았는데, 그럼 Void는 어디에서 왜 쓰이는걸까?

Void는 인스턴스화 될 수 없는 placeholder class다. 말 그대로 자리만 차지하는 클래스인데, 지금이야 제네릭에서 잘 쓰인다지만 제네릭 이전 버전에서부터 생긴 이유가 궁금했다.

java.lang.Void 클래스는 JDK 1.1 버전에서 추가되었는데, 이 때의 주 목적은 Reflection을 지원하기 위해서였다. 예시로 java.lang.reflect.Method.getReturnType() 라는 메서드가 있는데, 이 메서드는 특정 메서드의 반환 타입을 java.lang.Class 객체로 반환한다.

    /**
     * Returns a {@code Class} object that represents the formal return type
     * of the method represented by this {@code Method} object.
     *
     * @return the return type for the method this object represents
     */
    public Class<?> getReturnType() {
        return returnType;
    }

리턴 타입이 String인 메서드라면 getReturnType() 호출 시 String.class를 리턴하고, 리턴 타입이 int라면 Integer.TYPE을 반환한다. 만약 리턴 타입이 void라면...? void.class라기엔 void는 반환 값이 없는 상태를 나타내는 키워드이므로 객체화될 수 없다.

이런 상황에서 일관되게 Class 객체를 반환하기 위해 void를 나타내는 Class 객체로 Void가 탄생!

이후 제네릭이 탄생하고, 제네릭의 타입 파라미터는 반드시 객체 타입을 요구하기 때문에 리턴 타입이 없을 때 아래와 같이 Void 클래스를 사용한다.

// ApiResponse<T> : API 응답을 감싸는 커스텀 제네릭 클래스
public class ApiResponse<T> {
	private boolean success;
	private int code;
	private String message;
	private T data;
...
}
 
// ApiResponse<T>는 항상 제네릭 타입을 요구한다.
// 하지만 여기서는 반환할 데이터가 없으므로 Void를 타입으로 사용한다.
@PostMapping("/send-code")
  @Operation(summary = "인증 번호 발송", description = "비밀번호 재설정을 위한 인증 번호를 문자로 발송합니다.")
  public ResponseEntity<ApiResponse<Void>> sendVerificationCode(
      @Valid @RequestBody SendVerificationCodeRequestDTO request) {
    userService.sendVerificationCode(request);
    ApiResponse<Void> response = ApiResponse.ok("인증 번호 발송 성공");
 
    return ResponseEntity.ok(response);
  }