도입
프로그래밍 언어에는 사람이 붙이는 이름과 언어 자체가 이미 사용하는 단어가 함께 존재합니다. 예를 들어 Java에서 class, if, return, public 같은 단어는 개발자가 임의로 이름으로 쓰기보다, 컴파일러가 특정 문법 의미로 해석해야 합니다.
이처럼 언어 문법을 위해 미리 확보해 둔 단어를 보통 예약어라고 부릅니다. Java 공식 명세에서는 특히 reserved keyword 와 contextual keyword 를 구분해서 설명합니다.
그래서 예약어를 이해한다는 것은 단순히 금지된 단어 목록을 외우는 일이 아니라, 컴파일러가 소스 코드를 어떤 토큰으로 나누고 어떤 문맥에서 어떤 단어를 특별하게 해석하는지 이해하는 일에 가깝습니다.
필요성
초보 단계에서는 예약어를 단순히 “변수명으로 쓰면 안 되는 단어” 정도로 이해해도 됩니다. 하지만 코드가 복잡해질수록 예약어는 더 깊은 의미를 갖습니다.
예를 들어 var 는 모든 위치에서 완전한 예약어처럼 동작하지는 않지만, 지역 변수 타입 추론 문맥에서는 특별한 의미를 갖습니다. record, sealed, permits 역시 특정 문법 문맥에서만 키워드처럼 해석됩니다.
즉 예약어를 정확히 이해하면 컴파일 오류의 원인을 더 빨리 찾을 수 있고, 새 Java 문법이 기존 코드와 충돌하지 않도록 설계되는 방식도 더 잘 이해할 수 있습니다.
- 변수명, 클래스명, 메서드명에서 사용할 수 없는 이름을 피할 수 있음
- 컴파일러가 소스 코드를 토큰화하는 방식을 이해할 수 있음
var,record,sealed같은 문맥 키워드를 정확히 구분할 수 있음- Java 버전 변화에 따라 새 문법이 기존 식별자와 어떻게 충돌을 피하는지 이해할 수 있음
정의
Java의 식별자는 변수, 메서드, 클래스, 패키지, 모듈 같은 프로그램 요소에 붙이는 이름입니다. 하지만 식별자는 reserved keyword, boolean literal, null literal 과 같은 철자를 가질 수 없습니다.
반면 contextual keyword 는 더 유연합니다. 어떤 문맥에서는 키워드로 해석되고, 다른 문맥에서는 일반 식별자처럼 해석될 수 있습니다. Java가 새 문법을 추가하면서 기존 코드와의 충돌을 줄이기 위해 이런 방식을 자주 사용합니다.
따라서 “예약어”라는 말만으로는 부족하고, Java에서는 항상 금지되는 reserved keyword 와 문맥에 따라 달라지는 contextual keyword 를 나누어 보는 편이 정확합니다.
"예약어의 핵심은 단어 목록 자체가 아니라
그 단어가 어떤 문맥에서 식별자가 아니라 문법 토큰으로 해석되는지에 있습니다."
핵심 원리
Java 컴파일러는 소스 코드를 바로 의미 단위로 읽지 않습니다. 먼저 입력 문자를 토큰으로 나누는 lexical translation 과정을 거칩니다. 이때 토큰은 식별자, 키워드, 리터럴, 구분자, 연산자 등으로 구분됩니다.
예를 들어 static void 처럼 공백으로 나뉜 두 단어는 각각 키워드로 해석됩니다. 하지만 staticvoid 처럼 붙어 있으면 하나의 식별자로 해석됩니다.
또 record 는 record class 선언 문맥에서는 contextual keyword 가 되지만, 모든 위치에서 무조건 reserved keyword 처럼 동작하지는 않습니다. 이처럼 Java는 문맥 키워드를 통해 새 문법을 추가하면서도 기존 식별자 사용과의 충돌을 줄입니다.
Java Lexical Flow
1) 소스 코드 문자를 읽는다
2) 공백과 주석을 기준으로 토큰을 구분한다
3) 토큰을 identifier, keyword, literal, separator, operator 등으로 분류한다
4) reserved keyword 는 식별자로 사용할 수 없다
5) contextual keyword 는 문맥에 따라 키워드 또는 일반 토큰으로 해석된다
분류
| 구분 | 의미 | 실무 해석 |
|---|---|---|
| Reserved Keyword | 언어가 예약한 키워드 | 일반 식별자로 사용할 수 없음 |
| Contextual Keyword | 특정 문맥에서만 키워드로 해석되는 단어 | 문맥 밖에서는 일반 식별자처럼 보일 수 있음 |
| Identifier | 개발자가 붙이는 이름 | 변수명, 메서드명, 클래스명 등에 사용 |
| Boolean Literal | true, false |
키워드가 아니라 boolean 값 리터럴 |
| Null Literal | null |
키워드가 아니라 null 값 리터럴 |
Reserved Keyword
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_
const 와 goto 는 현재 Java에서 실제 문법 기능으로 쓰이지 않지만, 예약되어 있습니다. C/C++ 계열 개발자가 실수로 이 단어를 사용했을 때 더 명확한 오류를 주기 위한 성격도 있습니다.
strictfp 는 JLS 기준으로 obsolete 이며 새 코드에서는 사용하지 않는 편이 좋습니다. 또한 _ 는 키워드로 취급되므로 일반적인 단일 문자 식별자로 쓸 수 없습니다.
Contextual Keyword
exports opens requires uses yield
module permits sealed var
non-sealed provides to when
open record transitive with
예를 들어 var 는 지역 변수 타입 추론이나 람다 파라미터 타입 문맥에서 특별하게 해석됩니다. 하지만 모든 위치에서 전통적인 reserved keyword처럼 동작하는 것은 아닙니다.
record 는 record class 선언 문맥에서, sealed, non-sealed, permits 는 sealed class/interface 선언 문맥에서 특별한 의미를 갖습니다.
즉 contextual keyword 는 “예약어가 아닌 것처럼 아무 데나 쓸 수 있다”가 아니라, 문맥에 따라 키워드가 될 수 있으므로 이름으로 사용할 때 더 조심해야 하는 단어라고 보는 편이 좋습니다.
식별자 규칙
Java 식별자는 길이 제한이 없고, 첫 문자는 Java letter 여야 하며, 이후에는 Java letter 또는 Java digit 이 올 수 있습니다. 여기서 Java letter 와 Java digit 은 단순 ASCII 알파벳과 숫자만을 뜻하지 않고, Unicode 기반의 더 넓은 문자 집합을 포함합니다.
다만 실무에서는 가독성과 협업을 위해 ASCII 알파벳, 숫자, 의미 있는 단어 조합을 쓰는 경우가 많습니다. $ 는 Java letter 에 포함되지만, JLS는 기계적으로 생성된 코드나 레거시 접근 같은 특수 상황이 아니라면 사용을 피하는 편을 권장하는 방향으로 설명합니다.
// 가능
int count = 10;
String userName = "kim";
int MAX_VALUE = 100;
// 불가능
int class = 1; // compile-time error
int return = 2; // compile-time error
int null = 3; // compile-time error
true, false, null 은 키워드가 아니지만 식별자로 사용할 수 없습니다. 따라서 “키워드 목록에 없으니 이름으로 가능하다”라고 단순하게 판단하면 안 됩니다.패턴 1. 일반 예약어는 이름으로 쓰지 않는다
예를 들어 class, interface, return, new, private 같은 단어는 이미 Java 문법에서 강한 의미를 갖습니다. 따라서 이런 이름을 개발자가 다른 의미로 재사용할 수 없습니다.
이 규칙은 단순합니다. 문제는 Java 버전이 올라가면서 새 문법이 추가될 때입니다. 이때 Java는 기존 코드와의 충돌을 줄이기 위해 완전 예약어보다 문맥 키워드를 도입하는 경우가 많습니다.
// 잘못된 예
class return { }
int if = 10;
String new = "value";
// 좋은 예
class ReturnPolicy { }
int condition = 10;
String createdValue = "value";
패턴 2. 문맥 키워드는 위치에 따라 다르게 본다
var 는 대표적인 예입니다. 지역 변수 선언에서 타입 위치에 오면 타입 추론을 의미합니다. 하지만 모든 위치에서 reserved keyword 처럼 취급되는 것은 아닙니다.
다만 JLS는 permits, record, sealed, var, yield 를 TypeIdentifier 로 사용할 수 없다고 정의합니다. 그래서 클래스명이나 인터페이스명 같은 타입 이름으로는 사용할 수 없습니다.
// var 는 지역 변수 타입 추론 문맥에서 특별한 의미를 가짐
var name = "kim";
// 타입 이름으로는 사용할 수 없음
// class var { } // compile-time error
// class record { } // compile-time error
// class sealed { } // compile-time error
패턴 3. 리터럴과 키워드를 구분한다
true, false, null 은 키워드 목록에 넣어 외우기 쉽지만, Java 명세상으로는 각각 boolean literal 과 null literal 이다많은 입문 자료에서는 편의상 true, false, null 을 예약어 목록 근처에서 설명합니다. 실무적으로는 “이름으로 쓸 수 없다”는 점에서 비슷하게 느껴지기 때문입니다.
하지만 JLS 기준으로 true 와 false 는 boolean literal 이고, null 은 null literal 입니다. 즉 키워드가 아니라 리터럴 토큰으로 분류됩니다.
따라서 문법을 정확히 설명할 때는 “예약어처럼 이름으로 쓸 수 없지만, 분류상 키워드는 아니다”라고 구분하는 편이 좋습니다.
true, false, null 은 식별자로 쓸 수 없지만, 명세상으로는 각각 literal 입니다.한계와 주의점
첫째, 예약어 목록은 Java 버전에 따라 달라질 수 있습니다. 특히 새 문법이 추가되면 contextual keyword 가 늘거나 특정 위치에서 식별자로 쓸 수 없는 단어가 늘 수 있습니다.
둘째, 문맥 키워드는 “항상 금지”와 “항상 허용” 사이에 있습니다. 이 때문에 코드 예제만 보고 var 나 record 를 어디서든 이름으로 쓸 수 있다고 판단하면 안 됩니다.
셋째, _ 는 예전에는 식별자로 보일 수 있었지만 현재 Java에서는 키워드로 분류됩니다. 단, 특정 선언 위치에서 이름 대신 사용되는 언어 기능과 연결될 수 있으므로 일반 변수명처럼 쓰면 안 됩니다.
- reserved keyword 는 일반 식별자로 사용할 수 없음
- contextual keyword 는 문맥에 따라 해석이 달라질 수 있음
true,false,null은 키워드가 아니라 리터럴임const,goto는 사용되지 않지만 예약되어 있음strictfp는 obsolete 이므로 새 코드에서 사용하지 않는 편이 좋음
자주 하는 실수
true,false,null을 keyword 라고 단정함var,record,sealed를 모든 위치에서 완전한 예약어처럼 생각함- 문맥 키워드를 이름으로 쓸 수 있는 예시만 보고 타입 이름에도 쓸 수 있다고 착각함
const,goto가 실제 기능은 없으니 변수명으로 가능하다고 생각함_를 일반 변수명처럼 사용하려고 함- 공백이 빠져
publicstatic처럼 하나의 식별자로 토큰화되는 문제를 놓침
실무 루틴
- 변수명과 클래스명은 예약어와 비슷한 단어보다 의미 있는 도메인 용어로 작성한다.
- 타입 이름에는
var,record,sealed,permits,yield같은 제한을 특히 주의한다. true,false,null은 키워드는 아니지만 이름으로 사용할 수 없다고 기억한다.- 새 Java 버전으로 마이그레이션할 때 contextual keyword 충돌 가능성을 확인한다.
- 예약어와 비슷한 이름이 필요하면 접두사·접미사보다 더 명확한 용어로 바꾼다.
- 컴파일 오류 메시지를 단순 문법 오류로 넘기지 말고 토큰화와 식별자 규칙 관점에서 본다.
디버깅
true, false, null 처럼 키워드는 아니지만 식별자로 금지되는 리터럴인지 본다.점검 체크리스트
- 이 단어는 reserved keyword 인가
- contextual keyword 인가
- TypeIdentifier 로 사용할 수 없는 단어인가
- true / false / null 같은 literal 인가
- 공백이 빠져 토큰이 잘못 붙었는가
- Java 버전 업그레이드 후 새 문법과 충돌했는가
요약
- ✅ 예약어는 언어가 문법 의미를 위해 미리 점유한 단어다.
- ✅ Java SE 26 기준 reserved keyword 는 51개다.
- ✅ Reserved keyword 는 일반 식별자로 사용할 수 없다.
- ✅ Contextual keyword 는 특정 문맥에서만 키워드처럼 해석된다.
- ✅ Java SE 26 기준 contextual keyword 는 17개다.
- ✅
true,false는 keyword 가 아니라 boolean literal 이다. - ✅
null은 keyword 가 아니라 null literal 이다. - ✅
const,goto는 사용되지 않지만 예약되어 있다. - ✅
strictfp는 obsolete 이므로 새 코드에서는 사용하지 않는 편이 좋다. - ✅
var,record,sealed같은 단어는 문맥 제한을 함께 봐야 한다.