본문 바로가기

Java

JS는 Template Literal, python은 f-string, 그럼 Java는?

반응형

1. 들어가며

최근 개발 언어에서 문자열 포맷팅을 위한 새로운 기능들이 추가되고 있습니다. 예전에는 문자열을 결합하기 위해서 + 연산자나 String.format()과 같은 메서드를 사용했지만, 이제는 새로운 기능들을 통해 간단하게 문자열을 포맷할 수 있게 되었습니다.

 

Python에서는 f-string이라고 불리는 새로운 포맷팅 기법이 추가되었고, Javascript에서는 Template Literal이라는 새로운 문자열 표기법이 등장했습니다. 그렇다면 Java에서는 어떤 기능을 제공할까요? 이번 포스트에서는 Java에서의 문자열 포맷팅 기법인 Text Block과 printf에 대해 알아보도록 하겠습니다.

2. 자바, 자바스크립트, 파이썬 문자열 포맷팅 비교

Javascript와 Python은 각각 Template Literal과 f-string이라는 문자열 포맷팅 기능을 지원합니다. 하지만 Java는 이러한 문자열 포맷팅 기능을 제공하지 않습니다. 대신, Java는 기존에 제공하던 printf를 사용하여 문자열을 포맷팅합니다.

 

이에 대한 이유는 Java의 설계 철학과 관련이 있습니다. Java는 초기부터 안정성과 보안에 중점을 둔 언어로 설계되었습니다. 따라서, 문자열 포맷팅 기능을 추가하더라도 안정성과 보안 문제가 발생하지 않도록 하는 것이 중요하다고 생각했습니다. 이에 따라 Java는 printf를 사용하는 것을 권장하고 있습니다.

 

하지만 Java 13(13버전에서는 Preview이고, 실제로는 JDK15)부터는 Text Block이라는 새로운 기능을 도입하여, 좀 더 간편하게 문자열을 다룰 수 있도록 하였습니다. 이를 사용하여 printf와 유사한 기능을 수행할 수 있습니다. 이제 다음 예문을 통해 Text Block에 대해 자세히 알아보겠습니다.

3. Template Literal, f-string, Text Block 예문 비교

다음 예문들은 Javascript, Python, Java에서 긴 문자열을 포맷팅하는데에 특화된 기능을 비교한 코드들 입니다.

 

Javascript의 Template Literal(Template String)

const name = 'John Doe';
const age = 30;

const message = `My name is ${name} and I'm ${age} years old.`;

console.log(message);
// 출력 결과: My name is John Doe and I'm 30 years old.

 

Python의 f-string

name = 'John Doe'
age = 30

message = f'My name is {name} and I\'m {age} years old.'

print(message)
# 출력 결과: My name is John Doe and I'm 30 years old.

 

Java의 Text Block

String name = "John Doe";
int age = 30;

String message = """
        My name is %s and I'm %d years old.
        """.formatted(name, age);

System.out.println(message);
// 출력 결과: My name is John Doe and I'm 30 years old

4. Java의 Text Block과 printf()

이전에도 언급했듯이, Java에서는 Text Block 외에도 문자열을 다루는 다양한 방법이 있습니다. 그 중에서도 가장 전통적이고 많이 사용되는 방법은 printf()를 사용하는 것입니다.

 

printf()는 C언어에서 유래된 함수로, 서식 문자열과 매개변수를 이용하여 문자열을 생성하는 방법입니다. Java에서도 printf() 함수를 제공하며, % 기호와 조합하여 문자열을 생성할 수 있습니다.

 

예를 들어, 다음은 printf() 함수를 사용하여 문자열을 생성하는 예시입니다.

String name = "Alice";
int age = 25;
System.out.printf("My name is %s and I am %d years old.", name, age);

이 코드는 다음과 같은 출력을 생성합니다.

My name is Alice and I am 25 years old.

printf() 함수에서 %s는 문자열을, %d는 정수를 나타내며, 순서대로 매개변수로 전달된 name과 age 변수의 값이 출력됩니다.

 

printf() 함수를 사용하면 문자열을 더 세밀하게 제어할 수 있습니다. 예를 들어, 다음은 %10s와 %5d 서식 문자열을 사용하여 출력할 필드의 너비를 지정하는 방법입니다.

String name = "Alice";
int age = 25;
System.out.printf("|%10s|%5d|", name, age);

위 코드는 다음과 같은 출력을 생성합니다.

|     Alice|   25|

첫 번째 %10s에서 10은 출력 필드의 너비를 나타내며, Alice 문자열이 10칸의 공간에서 출력됩니다. 두 번째 %5d에서 5는 정수 필드의 너비를 나타내며, 25는 5칸의 공간에서 출력됩니다.

 

printf() 함수는 문자열을 제어할 수 있는 다양한 서식 문자열을 제공합니다. %f는 실수, %c는 문자, %b는 불리언 값 등을 출력할 수 있습니다. 더 자세한 정보는 Java 문서에서 확인할 수 있습니다.

 

하지만 printf() 함수는 Text Block보다 번거롭고 코드도 길어집니다. 또한, 잘못된 사용으로 인한 버그가 발생할 가능성도 있습니다. 그러므로 Text Block은 Java 13부터 추가된 기능으로, 문자열을 다룰 때 가독성과 유지보수성을 높일 수 있는 좋은 대안으로 사용할 수 있습니다.

5. 결론과 Java에서의 다른 대안(StringBuilder, StringBuffer)

이번 포스트에서는 Template Literal, f-string, Text Block의 예시를 비교해보고, Text Block과 printf를 비교해보았습니다. 각 기능마다 장단점이 있지만, Text Block은 복잡한 문자열을 표현할 때 유용하며 가독성이 좋다는 장점이 있습니다. 또한, printf와 비교했을 때 문자열 결합 시 새로운 객체를 생성하지 않기 때문에 성능 면에서도 유리합니다.

 

하지만, 자바에서 String을 "+"를 이용해 결합하는 것은 새로운 문자열 객체를 생성하는 단점이 있습니다. 예를 들어, 아래와 같은 코드를 실행했을 때 총 4개의 문자열 객체가 생성됩니다.

String firstName = "John";
String lastName = "Doe";
String fullName = firstName + " " + lastName;
System.out.println(fullName);

위 코드에서 "John"과 "Doe"는 각각 하나의 문자열 객체로 생성되고, " "와 fullName 역시 각각 하나의 문자열 객체로 생성됩니다. 이렇게 문자열을 결합할 때마다 새로운 문자열 객체를 생성하면, 메모리 사용량이 많아질 뿐만 아니라 성능에도 영향을 미칩니다.

 

따라서 자바에서 문자열을 결합할 때는 Text Block을 사용하거나, printf를 사용하는 것이 성능 면에서 더 유리합니다. 또한, 가독성도 좋아지기 때문에 코드의 유지보수에도 도움이 됩니다.

 

마지막으로 Java에서 사용할 수 있는 방법이 한 가지 더 있습니다. Java에서는 String의 concat() 메서드가 문자열을 결합할 때마다 새로운 String 객체를 생성하므로 성능에 문제가 생길 수 있습니다. 이를 해결하기 위해 Java에서는 StringBuilder와 StringBuffer 클래스를 제공합니다.

 

StringBuilder와 StringBuffer는 모두 가변적인 문자열을 다루는 클래스입니다. 두 클래스 모두 append() 메서드를 사용하여 문자열을 추가할 수 있으며, toString() 메서드를 사용하여 String 객체로 변환할 수 있습니다.

 

StringBuilder와 StringBuffer의 차이점은 스레드 안전 여부입니다. StringBuffer는 스레드 안전(thread-safe)하도록 설계되어 있어, 멀티 스레드 환경에서 안전하게 사용할 수 있습니다. 하지만 이로 인해 성능이 떨어질 수 있습니다. 반면 StringBuilder는 스레드 안전하지 않지만, 단일 스레드 환경에서 더 빠르게 동작할 수 있습니다.

 

따라서 멀티 스레드 환경에서는 StringBuffer를 사용하고, 단일 스레드 환경에서는 StringBuilder를 사용하는 것이 좋습니다.

 

예를 들어, 위에서 예시로 들었던 문자열 결합을 StringBuilder를 사용하여 다음과 같이 구현할 수 있습니다.

StringBuilder sb = new StringBuilder();
sb.append("My name is ");
sb.append(name);
sb.append(" and I am ");
sb.append(age);
sb.append(" years old.");
String result = sb.toString();

StringBuilder를 사용하면 String을 결합할 때마다 객체를 생성하는 것이 아니기 때문에 성능에 이점이 있습니다. 하지만, 이와 같은 코드를 작성할 때는 append() 메서드를 여러 번 호출해야 하기 때문에 코드가 복잡해질 수 있습니다. 이런 경우, Java 13(정확히는 Java 15)에서 추가된 Text Block 기능을 사용하는 것이 더 간결하고 가독성이 높을 수 있습니다.

 

물론, 성능과 가독성 모두 고려해야 하기 때문에 어떤 방법이 좋은지는 상황에 따라 다를 수 있습니다.

혹시나 싶어 SpEL(Spring Expression Language)과 비슷한 기능들에 대해서도 알아봤지만 파싱하는 과정이 더 번거롭고 파싱되는 시점이 런타임이기에 원래 원했던 용도로 Text Block안에서 쓰는 것은 맞지 않다는 결론이 났습니다. 스프링에서 SpelExpressionParser와 @Value을 사용하여 작성할 수 있는데. 나중에 혹시 기회가 되면 다뤄볼까 합니다.