들어가며
Java에서 동적 문자열을 생성하는 것은 개발자들이 매일 마주하는 과제입니다. JEP 430에서 제안된 문자열 템플릿(String Templates)은 이 문제를 새로운 관점에서 해결합니다.
기존 방식의 한계
Java는 문자열 조합을 위해 여러 접근법을 제공했지만, 각각 명확한 단점이 존재했습니다.
연산자 결합 방식
플러스 기호를 활용한 단순 연결은 짧은 문자열에는 적합하지만, 변수가 많아질수록 가독성이 급격히 저하됩니다.
가변 객체 활용
StringBuilder나 StringBuffer는 내부 조작에는 유연하지만, 단순한 메시지 생성에까지 보일러플레이트 코드를 요구합니다.
형식 지정 방식
String buildWeatherNotice(String condition, double value, String metric) {
return String.format("현재 날씨는 %s이며, 온도는 %.1f%s입니다", condition, value, metric);
}
인덱스 기반 매핑은 순서 변경 시 오류를 유발하며, 유지보수 과정에서 실수가 발생하기 쉽습니다.
메시지 형식 방식
String buildWithMessageFormat(String condition, double value, String metric) {
return MessageFormat.format("현재 날씨는 {0}이며, 온도는 {1,number}{2}입니다", condition, value, metric);
}
숫자 인덱스는 직관성이 떨어지고, 문법이 복잡하여 현대적인 개발 흐름과 맞지 않습니다.
템플릿 표현식의 구조
새로운 템플릿 시스템은 세 가지 핵심 요소로 구성됩니다:
- 처리기(Processor): 템플릿 평가를 담당하는 객체
- 템플릿 본문: 텍스트와 삽입식이 혼합된 구조
- 구분점(.): 처리기와 템플릿을 연결하는 연산자
내장 처리기 활용
STR 처리기: 기본 보간
가장 단순한 형태로, 표현식을 문자열로 직접 치환합니다.
String describeWeather(String skyState, int degrees, String scale) {
return STR."하늘 상태는 \{skyState}이고, 기온은 \{degrees}\{scale}입니다";
}
String buildJsonResponse(String skyState, int degrees, String scale) {
return STR."""
{
"condition": "\{skyState}",
"temperature": \{degrees},
"unit": "\{scale}"
}
""";
}
String withMethodCalls() {
return STR."체감 온도는 \{fetchApparentTemp()}도, 습도는 \{readHumidity()}%입니다";
}
FMT 처리기: 형식화된 보간
java.util.Formatter 문법을 삽입식에 결합하여 세밀한 출력 제어가 가능합니다.
String preciseReport(String skyState, float exactTemp, String scale) {
return FMT."""
{
"sky": "%-10s\{skyState}",
"temp": "%+6.2f\{exactTemp}",
"scale": "%s\{scale}"
}
""";
}
평가 메커니즘 이해
템플릿 표현식은 내부적으로 두 단계로 분리되어 실행됩니다:
// 직접 작성
STR."현재 기온은 \{current}도입니다";
// 컴파일러 변환 (개념적)
StringTemplate fragment = RAW."현재 기온은 \{current}도입니다";
return STR.process(fragment);
이 구조 덕분에 처리기는 원본 템플릿 구조에 접근할 수 있어, 필요에 따라 이스케이프 처리나 검증 로직을 주입할 수 있습니다.
보안적 강점
다른 언어의 문자열 보간과 비교할 때, Java의 템플릿은 처리기가 중간에서 개입하는 구조로 인해 자동 이스케이프나 구조적 검증이 가능합니다. 이는 SQL 인젝션이나 XSS 같은 취약점을 사전에 방지하는 데 기여합니다.
사용자 정의 처리기
개발자는 StringTemplate.Processor 인터페이스를 구현하여 도메인 특화 처리기를 만들 수 있습니다. 예를 들어 SQL 쿼리용 처리기는 파라미터를 PreparedStatement 형태로 안전하게 변환할 수 있습니다.