개발을 하다 보면, 메서드나 서비스로 전달된 파라미터 값을 확인해야 할 일이 자주 있다.
간단한 경우에는 System.out.println()으로 출력할 수 있지만, 다음과 같은 상황에서는 조금 더 복잡해진다.
이럴 때 어떤 객체든 문자열로 변환하여 구조와 값을 확인할 수 있는 공통 toString 기능이 있다면 개발 생산성을 크게 높일 수 있을거라 생각해 공통 기능을 개발해보았다!
String, Number, Boolean 등 단순 객체는 toString()을 사용.
Array(배열), List, Map, Set, DTO 등 복합 객체는 각 형태에 맞게 처리.
배열(Array) 출력 지원:배열의 각 요소를 순회하여 문자열로 변환하고, [value1, value2] 형식으로 출력.
컬렉션(Collection) 출력 지원: List, Set 등 Collection 타입은 각 요소를 순회하여 문자열로 변환.
맵(Map) 출력 지원:Map 타입은 각 키-값 쌍을 순회하여 key: value 형식으로 변환.
DTO 출력 지원: 객체의 모든 필드를 반영하여 필드명=값 형태로 출력.
Reflection을 사용하여 private 필드도 포함.
→ 자바 리플렉션(Reflection) API를 활용해 객체의 private 접근 제한자를 무시하고, 해당 필드의 값을 읽을 수 있게 처리
public static String toString(Object obj) {
if(obj == null) {
return "null";
}
if(obj instanceof String || obj instanceof Number || obj instanceof Boolean || obj instanceof Character) {
return obj.toString();
} else if(obj.getClass().isArray()) {
int length = Array.getLength(obj);
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < length; i++) {
sb.append(Array.get(obj, i));
if(i < length -1) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
} else if(obj instanceof Collection) {
Collection<?> collection = (Collection<?>) obj;
StringBuilder sb = new StringBuilder("[");
for (Object item : collection) {
sb.append(toString(item)).append(",");
}
if(!collection.isEmpty()) {
sb.setLength(sb.length() -2); // 마지막 콤마 제거
}
sb.append("]");
return sb.toString();
} else if(obj instanceof Map) {
Map<?, ?> map = (Map<?, ?>) obj;
StringBuilder sb = new StringBuilder("{");
for (Map.Entry<?, ?> entry : map.entrySet()) {
sb.append(toString(entry.getKey()))
.append(" : ")
.append(toString(entry.getValue()))
.append(", ");
}
if(!map.isEmpty()) {
sb.setLength(sb.length() -2);
}
sb.append("}");
return sb.toString();
} else {
return dtoToString(obj);
}
}
public static String dtoToString(Object obj) {
StringBuilder sb = new StringBuilder();
Class<?> clazz = obj.getClass();
sb.append(clazz.getSimpleName()).append(" {");
// 모든 필드 가져오기
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// static 필드는 무시
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
field.setAccessible(true); // private 필드 접근 가능하도록 설정
try {
Object value = field.get(obj); // 필드 값 가져오기
sb.append(field.getName())
.append("=")
.append(value)
.append(", ");
} catch (IllegalAccessException e) {
sb.append(field.getName()).append("=ACCESS_ERROR, ");
}
}
if (fields.length > 0) {
sb.setLength(sb.length() - 2); // 마지막 콤마 제거
}
sb.append("}");
return sb.toString();
}
System.out.println(toString("Hello")); // "Hello"
System.out.println(toString(42)); // "42"
int[] arr = {1, 2, 3};
System.out.println(toString(arr)); // "[1, 2, 3]"
List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
System.out.println(toString(list)); // "[Apple, Banana, Cherry]"
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
System.out.println(toString(map)); // "{A: 1, B: 2}"
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
User user = new User("John", 30);
System.out.println(toString(user)); // "User {name=John, age=30}"
이제 개발할 때 데이터를 확인하는데 중복 코드를 줄이고 조금 더 쉽게 값을 확인할 수 있게 되었다!!
뿌듯하다!! 🥳🥳