Spring Expression Language
1. 개요
1. 개요
Spring Expression Language는 Spring 프레임워크에서 사용되는 강력한 표현 언어이다. 이는 런타임 시 객체 그래프를 조회하고 조작할 수 있게 해주는 통합 언어로 정의된다. Spring 프레임워크 3.0에서 최초로 등장했으며, Spring 프레임워크 개발팀에 의해 개발되었다.
주요 용도는 매우 다양하다. 가장 기본적으로는 XML 기반 및 어노테이션 기반 빈 정의에서 복잡한 값을 설정하는 데 사용된다. 또한 Spring Security에서 메서드 수준과 웹 요청 수준의 보안을 제어하는 표현식을 작성할 때 핵심 역할을 한다. Thymeleaf나 JSP와 같은 뷰 템플릿 엔진 내에서 동적 콘텐츠를 생성하는 데에도 활용된다. 특히 @Value 어노테이션과 결합하여 프로퍼티 파일이나 환경 변수의 값을 의존성 주입하는 용도로 널리 쓰인다.
이 언어는 자바 플랫폼을 기반으로 하며, 기존의 표현 언어 개념을 Spring 생태계에 깊이 통합한 형태이다. 이를 통해 개발자는 구성 파일이나 어노테이션 속에 간결하면서도 표현력 있는 로직을 포함시킬 수 있어, 애플리케이션의 유연성과 설정 능력을 크게 향상시킬 수 있다.
2. 기본 구문
2. 기본 구문
2.1. 리터럴 표현식
2.1. 리터럴 표현식
리터럴 표현식은 가장 기본적인 Spring Expression Language 구성 요소로, 문자열, 숫자, 불리언 값, null과 같은 고정된 값을 직접 표현하는 데 사용된다. 이 표현식들은 평가 결과가 항상 동일한 상수 값을 반환하며, 런타임에 다른 객체나 프로퍼티를 참조하지 않는다. 주로 구성 파일에서 하드코딩된 값을 설정하거나, 다른 복잡한 표현식 내에서 상수 부분을 정의할 때 활용된다.
주요 리터럴 타입으로는 문자열, 숫자, 불리언, null이 있다. 문자열 리터럴은 작은따옴표(')로 감싸서 표현하며, 예를 들어 'Hello World'와 같다. 숫자 리터럴은 정수(42), 실수(3.14159), 과학적 표기법(6.022E23)을 지원한다. 불리언 값은 true와 false로 직접 표현하며, null 리터럴은 해당 키워드를 사용한다.
이러한 리터럴 표현식은 XML 기반 빈(Spring) 정의나 @Value 어노테이션에서 기본값을 할당하는 간단한 용도로 자주 사용된다. 또한, 비교 연산자나 산술 연산자의 피연산자로 사용되거나, 템플릿 표현식에서 고정 텍스트와 결합되어 동적인 문자열을 생성하는 데에도 쓰인다. 리터럴은 표현식 평가의 기초를 이루며, 다른 모든 복잡한 표현식도 궁극적으로는 이러한 기본 값들을 조합하고 연산한 결과를 도출한다.
2.2. 속성 및 메서드 참조
2.2. 속성 및 메서드 참조
2.3. 연산자
2.3. 연산자
Spring Expression Language는 다양한 연산자를 지원하여 복잡한 조건 평가와 계산을 수행할 수 있다. 연산자는 크게 산술 연산자, 관계 연산자, 논리 연산자, 할당 연산자, 타입 연산자, Elvis 연산자, 삼항 연산자, 정규식 매칭 연산자 등으로 구분된다.
산술 연산자로는 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/), 나머지(%), 거듭제곱(^)이 있다. 관계 연산자에는 동등(==), 비동등(!=), 보다 작음(<), 보다 큼(>), 작거나 같음(<=), 크거나 같음(>=)이 포함되며, 논리 연산자로는 AND(and 또는 &&), OR(or 또는 ||), NOT(! 또는 not)을 사용할 수 있다. 할당 연산자(=)는 표현식 내에서 변수에 값을 할당하는 데 사용된다.
특히 유용한 연산자로는 객체의 타입을 확인하는 instanceof 연산자와, 자바의 instanceof 및 캐스팅을 결합한 T(...) 연산자가 있다. 또한, 널 포인터 예외를 방지하기 위한 Elvis 연산자(?:)는 왼쪽 피연산자가 null이 아닌 경우 그 값을, null인 경우 오른쪽 피연산자의 값을 반환한다. 조건에 따른 값을 선택하는 삼항 연산자(?:)와 문자열 패턴 매칭을 위한 정규식 매칭 연산자(matches)도 제공된다. 이러한 연산자들은 XML 구성 파일이나 어노테이션 내의 표현식에서 유연하게 결합되어 사용된다.
3. 주요 기능
3. 주요 기능
3.1. Bean 참조
3.1. Bean 참조
3.2. 컬렉션 처리
3.2. 컬렉션 처리
컬렉션과 배열을 효율적으로 처리하는 기능은 Spring Expression Language의 주요 강점 중 하나이다. 이 기능을 통해 개발자는 복잡한 자바 코드 없이도 리스트, 맵, 인덱스 등을 간결한 표현식으로 조회하고 조작할 수 있다.
컬렉션 처리를 위한 구문은 직관적이다. 리스트나 배열의 특정 요소에 접근할 때는 대괄호([]) 안에 인덱스를 명시한다. 예를 들어, list[0]은 리스트의 첫 번째 요소를 가리킨다. 맵의 경우, 키를 사용하여 map['key']와 같이 값을 조회할 수 있다. 또한 점(.) 표기법을 사용하여 컬렉션 내 객체의 속성이나 메서드에 접근하는 것도 가능하다.
연산 유형 | 예시 표현식 | 설명 |
|---|---|---|
리스트/배열 인덱싱 |
|
|
맵 키 참조 |
|
|
컬렉션 프로젝션 |
| 컬렉션의 각 요소에서 |
컬렉션 선택 |
| 컬렉션에서 |
프로젝션(!)과 선택(?) 연산자는 특히 강력한 기능을 제공한다. 프로젝션 연산자는 컬렉션의 각 요소에 대해 지정된 표현식을 평가한 결과를 모아 새로운 컬렉션을 반환한다. 선택 연산자는 주어진 조건을 만족하는 요소들만을 걸러내는 필터 역할을 한다. 이러한 연산자들은 중첩하여 사용할 수 있어, 복잡한 데이터 변환과 질의를 단일 표현식으로 처리할 수 있게 해준다.
3.3. 템플릿 표현식
3.3. 템플릿 표현식
템플릿 표현식은 Spring Expression Language의 핵심 기능 중 하나로, 리터럴 텍스트와 하나 이상의 평가 블록을 혼합하여 복잡한 문자열을 구성하는 데 사용된다. 이 기능은 특히 Thymeleaf나 JSP와 같은 뷰 템플릿 엔진에서 동적으로 콘텐츠를 생성할 때 유용하게 활용된다. 기본적인 템플릿 표현식은 #{...} 구문으로 감싸진 평가 블록을 문자열 리터럴 내에 삽입하는 형태를 가진다. 예를 들어, #{'Hello, ' + name}과 같은 표현식에서 'Hello, '는 리터럴 문자열이고 name은 평가되어 값이 삽입되는 변수이다.
이 방식의 주요 장점은 정적인 텍스트와 동적인 SpEL 표현식을 자연스럽게 결합할 수 있다는 점이다. Spring Framework 3.0부터 도입된 이 기능은 XML 기반 설정 파일이나 Java Configuration 클래스 내에서도 사용할 수 있다. 예를 들어, 빈의 프로퍼티 값을 설정할 때 다른 빈의 메서드 호출 결과와 고정 문자열을 조합하여 할당하는 등의 작업이 가능해진다. 이를 통해 설정 파일의 가독성과 유지보수성을 높일 수 있다.
템플릿 표현식은 내부적으로 ParserContext와 TemplateParserContext 객체를 사용하여 파싱된다. 기본 평가 블록 구분자로 #{와 }를 사용하며, 필요에 따라 사용자 정의 구분자를 지정할 수도 있다. 이 메커니즘은 복잡한 문자열 조작이나 메시지 포맷팅, 국제화 지원을 위한 메시지 소스 조회 등 다양한 시나리오에 적용된다. 특히 Spring MVC 애플리케이션에서 뷰 레이어와 컨트롤러 간 데이터를 표시할 때 강력한 표현력을 제공한다.
4. 사용 예시
4. 사용 예시
4.1. XML 구성에서의 사용
4.1. XML 구성에서의 사용
Spring Expression Language는 Spring 프레임워크의 XML 기반 빈 정의 파일에서 속성 값이나 생성자 인자를 동적으로 설정하는 데 널리 사용된다. 이는 애플리케이션 컨텍스트가 초기화될 때 빈의 프로퍼티에 정적인 값 대신 런타임에 평가되는 표현식 결과를 주입할 수 있게 해준다. 주로 <property> 태그의 value 속성이나 <constructor-arg> 태그 내부에서 #{표현식} 구문을 사용하여 적용한다.
예를 들어, 다른 빈의 프로퍼티를 참조하거나 시스템 프로퍼티, 환경 변수를 기반으로 값을 계산하여 설정할 수 있다. 하나의 빈의 id를 다른 빈의 프로퍼티 값으로 할당하거나, 수학적 연산을 수행한 결과를 주입하는 것이 가능하다. 이를 통해 XML 구성의 정적 한계를 넘어 유연하고 조건부로 빈을 구성할 수 있는 능력을 제공한다.
또한, 컬렉션 타입의 프로퍼티를 구성할 때 SpEL의 강력한 기능을 활용할 수 있다. XML 내에서 리스트나 맵과 같은 컬렉션을 정의할 때, SpEL 표현식을 사용하면 리스트의 특정 요소를 필터링하거나 정렬한 결과를 바로 주입할 수 있다. 이는 복잡한 초기 데이터 구성을 XML 파일 자체에서 선언적으로 처리할 수 있게 만든다.
이러한 XML 구성에서의 SpEL 사용은 스프링 부트가 등장하며 어노테이션 기반 구성이 보편화되기 전, 전통적인 Spring Framework 애플리케이션에서 설정의 동적성을 높이는 핵심 수단이었다. 현재도 레거시 시스템의 유지보수나 특정한 XML 구성 요구사항이 있는 경우에 유용하게 활용되고 있다.
4.2. 어노테이션 기반 구성에서의 사용
4.2. 어노테이션 기반 구성에서의 사용
Spring Expression Language는 Spring 프레임워크의 어노테이션 기반 구성에서도 널리 활용된다. 특히 @Value 어노테이션과 함께 사용되어 프로퍼티 파일이나 환경 변수의 값을 의존성 주입받거나, 복잡한 표현식을 통해 동적으로 값을 계산하여 주입하는 데 유용하다. 이를 통해 XML 구성 없이도 자바 코드 내에서 유연한 구성이 가능해진다.
예를 들어, @Value("${database.url}")과 같이 SpEL을 사용하지 않고도 프로퍼티 플레이스홀더를 통해 간단한 값을 주입할 수 있다. 그러나 SpEL을 활용하면 @Value("#{systemProperties['user.home']}")과 같이 시스템 프로퍼티에 접근하거나, @Value("#{myService.calculateDefaultValue()}")와 같이 다른 빈의 메서드를 호출한 결과를 주입받는 등 더욱 강력한 표현이 가능하다.
또한 Spring Security에서는 메서드 수준의 보안을 적용할 때 @PreAuthorize, @PostAuthorize 등의 어노테이션과 함께 SpEL을 사용한다. 예를 들어 @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")와 같은 표현식을 작성하여, 인가 로직을 선언적으로 정의할 수 있다. 이는 접근 제어 로직을 비즈니스 로직과 분리시키는 데 기여한다.
이처럼 어노테이션 기반 구성에서 SpEL은 설정 값의 동적 주입부터 복잡한 보안 표현식 평가에 이르기까지 다양한 시나리오에서 코드의 표현력과 유연성을 크게 향상시킨다.
5. 보안 고려사항
5. 보안 고려사항
SpEL은 강력한 기능을 제공하는 만큼, 잘못된 사용 시 심각한 보안 취약점을 초래할 수 있다. 특히 사용자 입력과 같은 신뢰할 수 없는 데이터를 SpEL 표현식에 직접 삽입할 경우, 임의 코드 실행 공격의 표적이 될 수 있다. 이는 애플리케이션의 보안을 크게 위협한다.
가장 중요한 보안 원칙은 신뢰할 수 없는 데이터를 SpEL 평가 컨텍스트에 직접 노출시키지 않는 것이다. 예를 들어, 사용자가 입력한 문자열을 StandardEvaluationContext를 통해 평가하도록 하면, 공격자는 시스템 명령어 실행이나 민감한 데이터 접근과 같은 악의적인 표현식을 삽입할 수 있다. 따라서 SpEL을 사용할 때는 반드시 SimpleEvaluationContext와 같이 기능이 제한된 평가 컨텍스트를 사용하는 것이 권장된다. SimpleEvaluationContext는 리플렉션과 같은 위험한 기능에 대한 접근을 차단하여 보안성을 높인다.
Spring 프레임워크의 다른 모듈, 특히 Spring Security와 통합하여 사용할 때도 주의가 필요하다. SpEL은 메서드 보안이나 웹 보안 표현식에서 조건을 정의하는 데 널리 사용된다. 이 경우 표현식이 올바르게 구성되고, 필요한 권한 검사 로직이 포함되어 있는지 철저히 검증해야 한다. 또한, SpEL 표현식을 구성 파일이나 데이터베이스와 같은 외부 저장소에서 동적으로 로드하는 패턴은 보안 리스크를 크게 증가시키므로, 가능하면 피하거나 엄격한 검증 절차를 도입해야 한다.
6. 여담
6. 여담
Spring Expression Language는 Spring Framework 생태계 내에서 표현식 평가를 위한 사실상의 표준으로 자리 잡았다. 그 유연성과 강력함 덕분에 프레임워크의 다양한 모듈과 서드파티 템플릿 엔진에서 널리 채택되어 사용되고 있다.
이 표현 언어의 설계 철학은 자바 개발자에게 친숙한 구문을 제공하는 데 있다. 따라서 기존 표현 언어(EL)나 객체 지향 프로그래밍 언어에 익숙한 개발자라면 비교적 쉽게 접근하고 활용할 수 있다. 또한, 타 표현 언어와의 통합을 고려한 확장성 있는 구조를 가지고 있어, 필요에 따라 평가 컨텍스트나 기능을 커스터마이징할 수 있다.
Spring Expression Language의 성공은 단순히 기술적 우수성뿐만 아니라, Spring Security의 보안 표현식이나 Thymeleaf의 템플릿 처리와 같이 프레임워크의 핵심 기능과 깊게 통합되었다는 점에 기인한다. 이를 통해 개발자는 일관된 방식으로 구성, 보안, 뷰 레이어 전반에 걸쳐 동적인 로직을 표현할 수 있게 되었다.
