Spring Boot: mongoDB → PostgreSQL 마이그레이션: Conversing 이슈 해결

2024. 9. 24. 19:49Spring Boot

728x90
반응형

#1. 개요

  • 영업보고 기능을 Java Spring Boot - PostgreSQL 환경에서 구현 완료함.
  • 기존 내부적으로 서비스하던 Node / MongoDB 환경에서 구현 된 영업보고 기능 내의 데이터를 PostgreSQL 환경으로 마이그레이션을 진행해야함.
    • 데이터가 너무 많아서, API 서버 상에서 데이터가 담긴 mongoDB 를 연결하여 PostgreSQL 로 로직을 통해 마이그레이션을 구현함.
  • 그 과정 속에서 Conversion 이슈가 아래와 같이 발생했고, 해결함.

#2. 문제 및 해결

Exception : org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
  • Conversion 이슈
    • mongodb 에서 넘어오는 데이터가 스키마랑 달리 안 맞는 이슈 존재.
    • 데이터 훑어본 결과, string 으로 명시된 데이터가 데이터 상에는 숫자로 저장 되는 경우가 존재.
@Nullable
public static Object invokeConverter(GenericConverter converter, @Nullable Object source,
		TypeDescriptor sourceType, TypeDescriptor targetType) {

	try {
		return converter.convert(source, sourceType, targetType);
	}
	catch (ConversionFailedException ex) {
		throw ex;
	}
	catch (Throwable ex) {
		throw new ConversionFailedException(sourceType, targetType, source, ex);
	}
}
  • 위 라이브러리 코드에서 ConversionFailedException 오류 발생
    • 즉 sourceType 과 targetType 이 안 맞아서 생기는 오류
    • invokeConverter 메서드는 주어진 변환기(GenericConverter)를 통해 소스 데이터를 목표 타입으로 변환하려고 시도
      • GenericConverter는 일반적으로 스프링 프레임워크에서 제공하는 인터페이스로, 커스텀 변환기 로직이 여기에 구현될 수 있음
  • 따라서 다 string 으로 받고, double 로 따로 전환을 해주는 것으로 변경.
  • Converter 코드
@Component
public class NumberToStringConverter implements Converter<Object, String> {

	@Override
	public String convert(Object source) {
		if (source instanceof Number) {
			return String.valueOf(source);  // 숫자를 문자열로 변환
		}
		return source.toString();  // 이미 문자열인 경우 그대로 반환
	}
}

  • MongoConfig 상 Converter 등록
@Bean
public MongoCustomConversions customConversions() {
	return new MongoCustomConversions(List.of(new NumberToStringConverter()));
}
  • string → 숫자 변환 코드
public static boolean isNumeric(String str) {
	return str != null && str.matches("-?\\\\d+(\\\\.\\\\d+)?");
}

public static Double strToDouble(String strValue) {
	if (!isNumeric(strValue)) {
		return 0.0;
	}

	try {
		return new BigDecimal(strValue).doubleValue(); // 정확도 유지
	} catch (NumberFormatException ex) {
		return 0.0;
	}
}

public static BigDecimal strToBigDecimal(String strValue) {
	if (!isNumeric(strValue)) {
		return BigDecimal.ZERO; // null 대신 0을 반환하여 값이 사라지지 않도록 처리
	}

	try {
		return new BigDecimal(strValue); // 직접 BigDecimal로 변환
	} catch (NumberFormatException ex) {
		return BigDecimal.ZERO; // 변환 실패 시 0으로 대체
	}
}
  • 또 하나의 오류가 발생.
    • 특정 크기를 넘어가는 숫자들은 0으로 반환 됨
  • 디버깅을 해보니, string 으로 변경된 큰 숫자는 8.007441165E9 이런식의 string 값 처리가 됨. 그래서 bigDecimal 로 변경하는 과정에서 Numeric 으로 인식을 못함.
    • 큰 숫자들의 string 값을 isNumeric() 함수의 적용했을 때 숫자로 판단을 하지 않는 것이 원인.
  • 따라서 isNumeric 메서드를 아래와 같이 고쳐줌.
public static boolean isNumeric(String str) {
	return str != null && str.matches("-?\\\\d+(\\\\.\\\\d+)?([eE]-?\\\\d+)?");
}

728x90
반응형