웹 개발을 하다보면, 서버에서 문자열을 가공하여 넘겨줘야하는 상황들이 발생한다.
예를 들면, 오늘 날짜를 simpleDateFormat
을 활용하여 yyyy/MM/dd
로 넘겨주거나 DecimalFormat
을 이용하여 금액에 천단위 콤마를 찍어주는 일 등이 있다.
이를 사용자가 지정한 문자열 포맷으로 간편하게 변환해주는 공통메서드를 개발했다.
기본적으로 String.format()
과 유사한 기능을 제공하지만, 다양한 데이터 유형에 대해 사용자 지정 형식 문자열을 지원하여 더 유연하게 동작한다.
또한, 어떤문자열이든 원하는 대로 형식을 지정하여 변환된 문자열을 리턴받을 수 있기에 개발생산성에도 좋은 영향을 줄 것이다.
public static String Format(String format, Object... params) {
if (StringUtil.isEmpty(format)) {
return "";
}
// 정규 표현식을 사용하여 {index:format} 형태의 패턴을 찾기
Pattern pattern = Pattern.compile("\\{(\\d+)(:[^\\}]+)?\\}");
Matcher matcher = pattern.matcher(format);
// 인덱스에 따른 값과 기본 포맷을 저장할 맵 초기화
Map<Integer, Object> paramValueMap = new HashMap<>();
Map<Integer, java.text.Format> paramFormatMap = new HashMap<>();
// 파라미터 값과 기본 포맷 지정
for (int i = 0; i < params.length; i++) {
Object obj = params[i];
paramValueMap.put(i, obj);
paramFormatMap.put(i, obj != null ? getFormatter(obj, null) : null);
}
// 패턴을 찾으면서 형식화된 문자열로 대체
StringBuilder result = new StringBuilder(format);
while (matcher.find()) {
String patternString = matcher.group();
int idx = Convert.toInteger(matcher.group(1));
String idxFormat = matcher.group(2);
Object val = paramValueMap.get(idx);
java.text.Format formatter = (idxFormat != null && StringUtil.isNotEmpty(idxFormat.substring(1)))
? getFormatter(val, idxFormat.substring(1))
: paramFormatMap.get(idx);
// 대체할 값 설정 및 형식에 맞게 대체
Object replaceValue = (val == null ? "null" : val);
String replacement = (replaceValue != null && formatter != null)
? formatter.format(replaceValue)
: Convert.toString(replaceValue);
// 패턴을 형식화된 값으로 대체
int start = result.indexOf(patternString);
if (start != -1) {
result.replace(start, start + patternString.length(), replacement);
}
}
return result.toString();
}
private static java.text.Format getFormatter(Object value, String formatExp) {
if (value == null) return null;
String className = value.getClass().getName();
switch (className) {
case "java.util.Date":
return StringUtil.isNotEmpty(formatExp) ? new java.text.SimpleDateFormat(formatExp) : new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
case "java.lang.Integer":
return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat("###,##0");
case "java.lang.Double":
case "java.lang.Float":
case "java.lang.Long":
case "java.math.BigDecimal":
return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat("###,##0.##");
default:
return null;
}
}
String formattedString = Format("Hello, {0}!", "World");
System.out.println(formattedString);
// 출력: "Hello, World!"
String formattedString = Format("The price of {0} is {1:###,##0.00} dollars.", "Apple", 1234.5);
System.out.println(formattedString);
// 출력: "The price of Apple is 1,234.50 dollars."
Date now = new Date();
String formattedString = Format("Today's date is {0:yyyy-MM-dd}.", now);
System.out.println(formattedString);
// 출력: "Today's date is 2024-09-12." (현재 날짜에 따라 달라질 수 있음)
String formattedString = Format("Count: {0}, Total: {1}", 100, 1234.567);
System.out.println(formattedString);
// 출력: "Count: 100, Total: 1,234.57"
String formattedString = Format("Boolean: {0}, String: {1}, Integer: {2}, Double: {3:0.00}", true, "Test", 12345, 3.14159);
System.out.println(formattedString);
// 출력: "Boolean: true, String: Test, Integer: 12,345, Double: 3.14"
String formattedString = Format("Name: {0}, Age: {1}", null, 30);
System.out.println(formattedString);
// 출력: "Name: null, Age: 30"
String formattedString = Format("Sales: {0:$###,##0.00}", 1234567.89);
System.out.println(formattedString);
// 출력: "Sales: $1,234,567.89"
입력 문자열 Format이 비어 있거나 null인 경우, 이를 빈 문자열로 설정합니다.
if (StringUtil.isEmpty(format)) {
return "";
}
정규 표현식 (\\{(\\d+)(:[^\\}]+)?\\})
을 사용하여 형식 문자열에서 자리 표시자를 식별합니다. 이는 {0}
, {1:yyyy-MM-dd}
와 같은 패턴을 매칭합니다.
{(\\d+)(:[^\\}]+)?}
: 중괄호 {}
안의 숫자와 선택적인 형식 문자열을 매칭합니다. \\d+
: 숫자 (0
, 1
, 2
등)(:[^\\}]+)?
: 선택적인 형식 문자열 (:yyyy-MM-dd
등)Matcher
객체를 생성하여 입력 Format
문자열에서 이러한 패턴을 찾습니다.
Pattern pattern = Pattern.compile("\\{(\\d+)(:[^\\}]+)?\\}");
Matcher matcher = pattern.matcher(format);
paramValueMap
과 paramFormatMap
이라는 두 개의 HashMap
객체를 생성하여 매개변수 값과 해당하는 형식 포맷터를 저장합니다.
Map<Integer, Object> paramValueMap = new HashMap<>();
Map<Integer, java.text.Format> paramFormatMap = new HashMap<>();
제공된 매개변수 (params
) 각각에 대해 반복하여 다음을 수행합니다:
paramValueMap
에 저장합니다.getFormatter
메서드를 사용하여 매개변수에 대한 적절한 포맷터를 결정하고, 이를 paramFormatMap에 저장합니다.for (int i = 0; i < params.length; i++) {
Object obj = params[i];
paramValueMap.put(i, obj);
paramFormatMap.put(i, obj != null ? getFormatter(obj, null) : null);
}
형식 문자열에서 자리 표시자가 있는 동안 다음을 수행합니다
패턴 문자열 ({index[:format]}
)을 추출합니다.
Convert.toInteger(matcher.group(1))
을 통해 자리 표시자의 인덱스를 가져옵니다.
자리 표시자에 해당하는 형식 문자열 (idxFormat
)을 가져옵니다.
paramValueMap
에서 인덱스에 해당하는 값을 가져옵니다.
형식 문자열이 존재하면 이를 기반으로 포맷터를 설정하고, 그렇지 않으면 기존에 설정된 기본 포맷터를 사용합니다.
while (matcher.find()) {
String patternString = matcher.group();
int idx = Convert.toInteger(matcher.group(1));
String idxFormat = matcher.group(2);
Object val = paramValueMap.get(idx);
java.text.Format formatter = (idxFormat != null && StringUtil.isNotEmpty(idxFormat.substring(1)))
? getFormatter(val, idxFormat.substring(1))
: paramFormatMap.get(idx);
자리 표시자에 해당하는 값을 포맷터를 사용하여 형식화한 후, 형식 문자열의 자리 표시자를 실제 값으로 대체합니다. 포맷터가 없으면 기본 toString()
을 사용하여 값을 문자열로 변환합니다.
Object replaceValue = (val == null ? "null" : val);
String replacement = (replaceValue != null && formatter != null)
? formatter.format(replaceValue)
: Convert.toString(replaceValue);
int start = result.indexOf(patternString);
if (start != -1) {
result.replace(start, start + patternString.length(), replacement);
}
}
형식화된 최종 문자열을 반환합니다.
return result.toString();
getFormatter
메서드는 값의 데이터 유형과 형식 문자열 (formatExp
)에 따라 적절한 포맷터 객체를 생성합니다.
Date
객체는 SimpleDateFormat
을 사용하고, Integer
, Double
, Float
, Long
등의 숫자 타입은 DecimalFormat
을 사용합니다.문자열
과 boolean
의 경우, 추가적인 포맷터는 필요하지 않으므로 기본값
으로 처리됩니다.private static java.text.Format getFormatter(Object value, String formatExp) {
if (value == null) return null;
String className = value.getClass().getName();
switch (className) {
case "java.util.Date":
return StringUtil.isNotEmpty(formatExp) ? new java.text.SimpleDateFormat(formatExp) : new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
case "java.lang.Integer":
return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat("###,##0");
case "java.lang.Double":
case "java.lang.Float":
case "java.lang.Long":
case "java.math.BigDecimal":
return StringUtil.isNotEmpty(formatExp) ? new DecimalFormat(formatExp) : new DecimalFormat("###,##0.##");
default:
return null;
}
}
다양한 데이터 유형과 형식화를 지원하기 위해 만들었습니다. 사용자 정의 형식 문자열을 쉽게 처리할 수 있도록 합니다. Java에서 문자열 포맷팅을 보다 유연하게 관리하고 싶다면, 이와 같은 사용자 정의 함수를 활용해 보세요!