ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 공통 메서드 설계 Static vs Spring Bean
    Spring 2024. 12. 26. 14:16

     

    현재 프로젝트에서 Redis를 사용하게 되어서 공통으로 Redis에 키를 통해 값을 저장하고, 조회하는 유틸을 만들었다.

    static 메서드 사용과 Spring의 빈으로 등록하는 방식에 대한 고민을 하게 되었다!

    기존 구현

    public class RedisUtil {
    	private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
    	public static final ObjectMapper mapper = new ObjectMapper();
    	
    	public static <T> void setData(RedisTemplate<String, Object> redisTemplate, String key, T data) {
    	    try {
    	        String jsonData = mapper.writeValueAsString(data);
    	        redisTemplate.opsForValue().set(key, jsonData);
    	    } catch (JsonProcessingException e) {
    	        logger.error("JSON 변환 오류: {}", e.getMessage());
    	    }
    	}
    	
    	public static <T> Optional<T> getData(RedisTemplate<String, Object> redisTemplate, String key, Class<T> classType) throws JsonMappingException, JsonProcessingException {
    	    Object data = redisTemplate.opsForValue().get(key);
    	    if(data == null) {
        		throw new CustomException("ERR000", "키가 유효하지않습니다.");
    	    }
    	    try {
    	    	return Optional.ofNullable(mapper.readValue(data.toString(), classType));
    	    } catch (JsonProcessingException e) {
    	    	throw new CustomException("ERR000", "파싱오류가 발생했습니다.");
    		}
    	}
    }

     

    setData, getDate 메소드는 당연히 공통으로 사용할 생각이여서 유틸로 설정하고 static으로 만들어야한다고 생각했다.

    하지만 RedisTemplate은 스프링 환경에 맞게 설정 파일을 통해 생성되기 때문에 static 메서드와 생성 시점이 다르다.

     

    구현상의 문제

    결국 호출하는 쪽에서 RedisTemplate을 주입받아 파라미터로 넘겨야 하는 상황이다.

    그래서 코드가 좀 복잡해지고 가독성이 떨어지는 느낌이 들었다.. (ㅜㅜ)

     

    static 메서드는 메모리에 처음 저장되므로 RedisUtil.getData()와 같이 바로 호출할 수 있는 장점이 있지만...

    그 한 줄을 위해 호출하는 곳에서 매번 RedisTemplate을 주입받아야 하는 번거로움이 있는것같다.. (이것이 멍청비용?)

     

    결국 스프링 애플리케이션 환경 내에서 이루어지는 작업이고, RedisTemplate도 스프링의 생명주기 관리 하에 빈으로 등록되는데.. 

    이 유틸리티 클래스도 결국 스프링이 관리하는 RedisTemplate 빈을 받아야 하는 상황이라, 약간의 모순 같은 느낌이 들었다..

     

    그리고 static 유틸 메소드는 

    여러가지 특징(인스턴스 없이 호출가능, 메모리 관리.. )등이 있지만

    인스턴스 상태에 의존하지않고, 입력값을 받아서 동일한 결과를 반환하는게 가장 큰 특징인것같다.

    그치만 내가 지금 구현한거는 어떻게보면 RedisTemplate 에 상당히 의존하고있다는것. 없으면 말짱 도루묵!

     

    결론🌟

    그래서 static메서드를 없애고 빈으로 등록시키로 결정!

    (RedisTemplate과 같은 Spring의 생명주기로 관리) 

     

    물론 호출시에는 RedisService를 선언해야하지만, RedisTemplate를 파라미터로 넘기는것 보단 훨씬 나은것같다.

    유틸의 개념도 아닌것같아서 이름도 변경했다.

    (제 코드는 단순 참고용입니다 >,,<)

     

    @Slf4j
    @Component
    public class RedisService {
    	
        @Autowired
        private ObjectMapper objectMapper;
        
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    	
    	public <T> void setData(String key, T data) {
    	    try {
    	        String jsonData = objectMapper.writeValueAsString(data);
    	        redisTemplate.opsForValue().set(key, jsonData);
    	    } catch (JsonProcessingException e) {
    	        log.error("JSON 변환 오류: {}", e.getMessage());
    	    }
    	}
    	
    	public <T> T getData(String key, Class<T> classType) {
    	    Object data = redisTemplate.opsForValue().get(key);
    	    if(data == null) {
        		throw new CustomException("ERR000", "키가 유효하지않습니다.");
    	    }
    	    try {
    	    	T result = objectMapper.readValue(data.toString(), classType);
    	        if (result == null) {
    	        	throw new CustomException("ERR000", "파싱 결과가 null입니다.");
    	        }
    	        return result;
    	    } catch (JsonProcessingException e) {
    	    	throw new CustomException("ERR000", "파싱오류가 발생했습니다.");
    		} catch (Exception e) {
    			throw new CustomException("ERR000", "파싱오류가 발생했습니다.");
    		}
    	}
    }

     

     

    참고

    이런 주제에 대해 더 많은 정보가 있으니 계속 참고하면 좋을듯싶다.

    https://curiousjinan.tistory.com/entry/util-class-static-vs-spring-bean#2.%20%EB%82%B4%EA%B0%80%20%EC%9E%91%EC%84%B1%ED%95%9C%20%EC%BD%94%EB%93%9C%EC%97%90%20%EB%8C%80%ED%95%9C%20%EC%84%A4%EB%AA%85-1

Designed by Tistory.