1. 이번 주 항해 취업 리부트코스에서 내가 구현한 기능
원래대로라면 6주차에 구현해야했었던 기능이었지만 새로 배우는 과정이다보니 시간이 오래걸렸다.
- spring batch 기반 조회수 누적(일일, 주간, 월간)
- spring batch 기반 정산 금액 누적(일일, 주간, 월간)
- 정산된 금액 API 형태로 전달.
2. 이번 주 겪은 트러블슈팅
문제와 원인
로직 상에는 큰 문제가 없다고 생각했는데 ItemReader에서 제대로 읽어오지 못하고 NullPointerException 발생.
@Bean
@JobScope
public Step monthlyVideoViewStatsStep(
ItemReader<VideoViewStats> videoViewStatsJpaPagingItemReader,
ItemProcessor<VideoViewStats, VideoViewStats> monthlyVideoViewStatsItemProcessor
) {
log.info("Starting monthly video view stats step");
return new StepBuilder("monthlyVideoViewStatsStep", jobRepository)
.<VideoViewStats, VideoViewStats>chunk(10, transactionManager)
.reader(videoViewStatsJpaPagingItemReader)
.processor(monthlyVideoViewStatsItemProcessor)
.writer(videoViewStatsItemDBWriter)
.build();
}
// JPA PagingItemReader 정의
@Bean
@StepScope
public ItemReader<VideoViewStats> videoViewStatsJpaPagingItemReader(
@Value("#{jobParameters['dateTime']}") LocalDateTime dateTime,
@Value("#{jobParameters['statisticsType']}") String dateType
) {
log.info("Starting video view stats jpa paging item reader");
LocalDate date = dateTime.toLocalDate();
LocalDate startDate = date.minusDays(7).with(DayOfWeek.MONDAY);;
LocalDate endDate = startDate.plusDays(6);
if(dateType.equals("month")){
startDate = date.minusMonths(1).withDayOfMonth(1); // 직전 월의 첫째 날
endDate = startDate.plusMonths(1).minusDays(1); // 해당 월의 마지막 일
}
Map<String, Object> parameters = new HashMap<>();
parameters.put("category", VideoViewStats.Category.DAY);
parameters.put("startDate", startDate);
parameters.put("endDate", endDate);
return new JpaPagingItemReaderBuilder<VideoViewStats>()
.name("videoViewStatsJpaPagingItemReader")
.entityManagerFactory(entityManagerFactory)
.pageSize(10)
.queryString("SELECT v FROM VideoViewStats v WHERE v.category = :category " +
"AND v.startDate BETWEEN :startDate AND :endDate " +
"ORDER BY v.startDate ASC")
.parameterValues(parameters)
.build();
}
문제 해결 시도
- 별도의 커스텀 Reader로 클래스를 생성해서 시도해봤으나 동일한 문제 발생.
해결
참고 : https://jojoldu.tistory.com/132
- @Bean이랑 @StepScope를 동시에 사용해서 JopParameter 사용 시 리턴 타입과 생성한 값이 완전히 동일하지 않으면 도중에 proxy 객체에 걸려서 오류가 발생함.
- 참고 사이트에서 확인해보니, reader()에서 ItemReader 객체로 반환하게 될 때, @StepScope가 없으면 정상적으로 동작하지만 만약 @Bean과 @StepScope를 동시에 사용할 경우 @StepScope의 proxyMode = ScopedProxyMode.TARGET_CLASS로 인해 ItemReader 인터페이스의 프록시 객체를 생성하여 리턴하게 된다.
- 다만 프록시 객체에는 stream을 open/close 하는 메소드가 없음.
- 즉 stream을 관리하는 메소드가 없으므로 EntityManager가 생성이 되지 않음.
따라서 반환타입 자체를 구현체의 타입을 직접 쓰게 되면 정상적으로 동작함. 위 코드를 예시로 든다면 ItemReader 대신 JpaPagingItemReader를 반환하면 정상적으로 동작한다.
3. 이번 주 진행된 개인 프로젝트에서 얻은 인사이트
- 배치 작업을 비즈니스 로직에서 처리해야되는 부분만 고려했었는데, 그게 아니더라도 더 많은 활용이 가능하다는 걸 깨달았다. 개발 과정에서 테스트에 필요한 더미 데이터를 생성하고 DB에 저장하는 역할도 가능하고, 이외에도 다양하게 테스트 해볼 수 있는 영역이 많다보니 좀 더 알아보면 좋겠다라는 생각을 했다.
- 배치의 성능을 향상시키기 위해서는 디테일한 부분들에 대해서 이해해야 할 필요성을 느꼈다. 배치 작업의 성능을 향상시키는 대표적인 방법은 4가지가 있는데, 그 중에서도 가장 많이 쓰는 방식은 multi thread, parallel processing, partitioning step 정도가 대표적이다.(이외에도 remote chunking이 있지만 입문 단계에서 사용하기에는 어려움이 있다.) 위 과정 중에서 multi thread의 경우 간단한 방식이라면 쉽게 구현이 가능하지만, 좀 더 성능을 개선시키기 위해 별도의 TaskExecutor를 상속받아 구현한다고 하면 thread Pool 이나 등등 고민해야될 부분들이 많다. 이처럼 큰 영역은 아니지만 세세한 부분에서 조정해야 될 부분이 많다고 느꼈다.
항해99 취업 리부트 코스를 수강하고 작성한 콘텐츠입니다.
'항해99 > 5-8주차' 카테고리의 다른 글
[항해 취업 리부트 코스] 6주차 프로젝트 WIL (0) | 2024.04.30 |
---|---|
[항해 취업 리부트 코스] 5주차 프로젝트 WIL (0) | 2024.04.22 |