[Test] DataSource Bean & properties(yaml) & SQLGrammarException: could not prepare statement [Table ~~ not found]
·
JAVA/Application
■ 단일 DataSource 설정properties/yaml에서 db connection 설정 방법은 이렇다.spring.datasource.properties.driverClassName=...spring.datasource.url=...spring.datasource.username=...spring.datasource.password=... 하지만 만약 멀티 데이터소스 요구사항이 다른 경우, 위와 같은 properties에 따른 기본적인 DataSource가 필요한 것이 아니라, 별도로 구현해둔 빈이 필요할 수 있다. (🔗 예를 들면 이런 상황) 그리고 그런 커스텀 DataSource를 사용하도록 테스트 환경을 구성할 때 어떻게 DataSource와 DB 접속 정보를 설정해야 하는지, 놓칠 수 있..
MDC를 사용해 멀티쓰레드 로깅하기 (for 쓰레드간 Context 전달)
·
JAVA/Application
■ Intro멀티쓰레드 환경의 비동기 로깅을 해야했고, MDC를 활용했다.상황을 설명하자면 요청 경로는 동일하게 들어오지만 중간에 쓰레드가 나뉘는 코드가 있었는데, 요청의 컨텍스트를 유지하고 있어야 했다. 예를들어 처리시간을 로깅하려면 처리시작 시간을 보유하고 있어야 어디선가 처리가 끝날 때 총 처리 시간을 구할 수 있다. 비동기일 경우 처리가 끝나는 시점을 모르기 때문에, 그런 최적의 방법에 대한 고민이 필요했다. 결론적으로 MDC + TaskDecorator + AOP를 통해서 구현했다. 비동기 처리의 완료시점을 확실히 하는 것이 중요하므로 MDC를 사용하여 쓰레드를 트래킹하는 것이 적절해보였다. TaskDecorator가 MDC 컨텍스트를 분기 대상 쓰레드로 넘겨서 종료시점까지 정보를 유지하려는 방..
[Springboot] 스케줄러 동적 등록/삭제/변경
·
JAVA/Application
■ Intro동적으로 스케줄링 정보를 변경해야 할 일이 있다. 예시로 스케줄링 주기를 중간에 바꿔야 한다든가, 스케줄 내용을 변경해야 한다든가 말이다. 값이 동적으로 들어오는 위치를 DB라면, DB의 데이터 변경을 자동으로 알아차린 후 스케줄러를 변경시켜야 한다. 그래서 지난 번에는 어떻게 변경감지를 할지 고민했었고, 폴링으로 감지하기로 결정했다. 이번에는 데이터 변경이 감지된 후 스케줄러를 동적으로 변경하는 방법을 정리하겠다. ■ 샘플 구조예시로, 1분마다 DB에서 '주기 값'이 변경되었는지 확인하고, 스케줄러에 반영하는 간단한 샘플 구조를 작성했다. 각자의 비스니스에 맞게 커스텀해서 사용하면 될 것이다. @Componentpublic class SampleDynamicScheduler {    pri..
[Spring] DTO 매핑시 특정 키만 null 값
·
JAVA/Application
나머지 값들은 다 정상적으로 매핑되어 값이 잘 들어가는데, 특정 키만 null로 들어가는 것을 발견했다.원인은 필드명이었다. 아래 글 덕분에 알게 되었다. https://bcp0109.tistory.com/309 Spring Request DTO 에 null 값이 들어가는 이유 (Jackson, Lombok)Overview Spring Boot 로 REST API 를 테스트 하다가 이상한 이슈에 직면했습니다. 클라이언트에서 @RequestBody 로 요청을 받기 위한 DTO 클래스를 만들고 값을 입력 받았는데 null 값이 입력되는 겁니다. 처bcp0109.tistory.com  문제가 되는 필드명은 cId였다. 소문자 하나 다음에 바로 대문자가 나오는 필드명이 문제가 되는 것이다. Spring은 htt..
[Spring AOP] Consumer onMessage() 비동기 수신 로깅
·
JAVA/Application
■ AOP를 통해 onMessage() 비동기 수신 로깅 Redis가 Consume할 때 호출되어 작업을 처리하기 시작하는 부분인 onMessage() 메서드를 AOP를 통해 로깅하려는 상황이었다. 기능 구현을 위한 샘플 코드를 짜서 테스트해보았다. 구조는 이러하다.- ConsumerFactory: 메시지를 구독할 컨슈머들을 생성- StreamListenerImpl: 구독한 메세지를 처리할 onMessage 구현- ListenContainer: 위 리스너를 관리할 컨테이너 설정- LoggingAspect: 테스트 대상인 Aspect들- RedisConfig: 기본 레디스 설정  *참고: ListenerContainer, Listener, 그리고 Consumer 객체간의 관계를 이해하는 것이 필요하다면, ..
[Redis] cannot deserialize from Object value (SerializationException)
·
JAVA/Application
Springboot Cache를 적용하고자 할 때 발생가능한 오류입니다.- 저는 redis cache 사용했습니다.- 또한 캐시 value에 대해서 GenericJackson2JsonRedisSerializer를 적용했습니다. 서버에 캐시를 적용했는데 만약 이런 에러를 만났다면, jackson의 역직렬화에 대해 생각해봅시다.캐시된 응답을 올바른 객체로 내보내야 하는데 역직렬화할때 쓸 생성자가 없다면cannot deserialize from Object value를 로그에서 만날 수 있습니다.*생성자의 존재 문제가 아닌, 역직렬화하기 적절한 생성자를 두는 것이 핵심입니다.org.springframework.data.redis.serializer.SerializationException: Could not ..
다량의 이미지 업로드 속도 최적화 / 트래픽 대비 / 비동기 처리
·
JAVA/Application
■ 리뷰등록시 문제 가능성너무나도 희망찬 이야기겠지만,만약 우리 서비스의 인기가 폭발해서동시에 많은 유저가 사진리뷰를 업로드한다면 어떨까? 동기적으로 처리할경우 대용량 사진 업로딩이 하나라도 있다면, 뒤를 이을 수많은 업로딩이 블로킹되며 작업이 주르륵 지연된다. 그래서 이때는 비동기 처리를 생각해야 한다.  이 상황을 대비하고자 하여..문제의 코드를 수선해봤다.  ■ 성능 측정 (TPS)2초 안에 25건의 스레드(사용자)를 실행함 (= 약 0.08초마다 스레드를 순차 실행)각 스레드(사용자)는 요청을 총 4개 보냄(= 총 100건의 요청을 보내는 테스트) □ 변경 이전  □ 변경 이후전반적으로 TPS도 상승하고,처리시간도 약 0.65배로 단축했다.  "> □ 비동기 + Multipart 관련 문제 해결사..
[Java] ObjectMapper에 TypeReference를 사용할 때
·
JAVA
TypeReferenceJackson 라이브러리를 사용하여 JSON을 역직렬화할 때, 제네릭 타입 정보를 보존하기 위해 사용한다.  ∨ ObjectMapper.readValue() 에서 안전한 매핑 가능ObjectMapper.readValue(arg, type) 의 두번째 인자에 타입정보를 표기할 수 있다.변환대상인 arg를 어떤 타입으로 매핑할 것인가의 정보를 주는 것이다. 만약 json을 어디선가 받아와서 변환하고 싶다면, type에는 Class 타입 또는 TypeReference가 온다.특히 TypeReference를 ObjectMapper에 사용하면 경고(ClassCastException 발생 가능 경고)가 나지 않게 할 수 있다.mapper.readValue(json, ArrayList.clas..
[Java] Generic Type erasure / 제네릭 타입 소거
·
JAVA
Generic Type erasure자바에서 Generic Type erasure 프로세스는 말 그대로 제네릭 타입을 지워버리는 프로세스다. 자바에서 제네릭은 런타임에 소거된다. 제네릭은 타입 검사 이점이 있으므르 컴파일때 타입 검사를 보장한다. 그러나 런타임에는 타입 정보를 삭제한다.  런타임에 타입을 삭제한다?모든 타입을 Object 타입으로 바꾼다.또는 명시된 바운드로 바꾼다. 즉 컴파일 / 런타임 때 아래와 같이 다르다.// 컴파일시 타입 체크public class Test { public void test(T test) { ... }}// 타입 소거가 된 런타임public class Test { public void test(Object test) { ...
상시 업데이트함 -- Effective java 스터디
·
JAVA/Effective Java
(24.07.00) ItemXX: ~~~ ~~  (24.07.00) Item77: 예외를 무시하지 말라~~   (24.07.00) Item76: 가능한 한 실패 원자적으로 만들라~~   (24.07.00) Item75: 예외의 상세 메시지에 실패 관련 정보를 담으라~~    (24.07.00) Item74: 메서드가 던지는 모든 예외를 문서화하라~~   (24.07.22) Item73: 추상화 수준에 맞는 예외를 던지라 ## 예외 번역이란너무 저수준 예외를 계속 전파하면, 내부 구현 방식도 드러나고 혼란스러워진다. 그래서 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다.try { ...} catch (LowerLevelException e) { // 추상..