해당 포스팅은 JPA native Query와 Spring Boot를 이용한 CRUD 개발 4편이다.
F-lab 멘토링 과제를 위한 개발을 진행중이며,
멘토님께 피드백을 받으며 개발을 할 때 신경써야 하는 부분을 찾아가고 있다.
멘토님의 피드백을 받고 수정한 부분
1. 병렬처리나 비동기처리로 성능을 올릴 수 있을 것 같은 로직은 그렇게 수정해볼 것
2. 평균점수를 얻는 로직이 성능적으로 분명 좋지 않을 것 같으니 다시 수정해볼 것
3. log back을 이용하여 log파일을 만들어 볼 것
이슈 해결
오늘은 특별한 이슈가 없었음.
개발 Main 내용
1. 병렬처리와 비동기처리
서비스 로직을 잘 확인해보니, 게시글을 조회 할 때 게시판에서 게시글을 가져오는 로직과 Redis에 캐시로 저장되어있는 조회수를 병렬로 함께 가져오는 방법이 가능할 것 같다는 생각이 들었다.
그래서 해당 코드를 아래처럼 수정하였다.
/**
* 게시글 정보 가져오기
*/
@Override
public BoardVo getBoardInfo(Long boardId) {
incrementRedisBoardView(boardId); // Async
// Parallel Processing
CompletableFuture<Board> boardFuture = CompletableFuture.supplyAsync(() -> boardRepository.getBoardInfo(boardId));
CompletableFuture<Integer> redisViewFuture = CompletableFuture.supplyAsync(() -> getBoardViewRedis(boardId));
return boardFuture.thenCombine(redisViewFuture, (board, redisView) -> {
BoardVo boardVo = BoardVo.fromBoardEntity(board);
boardVo.setBoardView(board.getBoardView() + redisView);
double averageScore = scoreRepository.getBoardAverageScore(boardId);
boardVo.setAverageScore(averageScore);
return boardVo;
}).join(); // 대기하고 결과 반환
}
또한 조회수는 사실 1단위까지 명확하게 드러나지 않아도 될 부분이라고 판단하였다.
그래서 조회수가 1씩 증가하는 로직은 @Async를 이용하여 비동기 메서드로 만들어주었다.
/**
* Redis 에 조회수 추가 후 반환 ( 비동기로 처리 하는 메서드 )
*/
@Async
@Transactional
@Override
public void incrementRedisBoardView(Long boardId) {
String redisKey = RedisStringCode.BOARD_KEY_CODE;
String redisHashKey = String.valueOf(boardId);
HashOperations<String, Object, Integer> hashOperations = redisTemplate.opsForHash();
Integer redisView = hashOperations.get(redisKey, redisHashKey);
if ( redisView != null ) {
hashOperations.increment(redisKey, redisHashKey, 1);
} else {
hashOperations.put(redisKey, redisHashKey, 1);
}
}
2. 평점 로직 수정
평점이 기존에는 게시글을 불러올 때마다, 평점 테이블에서 쿼리문으로 평균 점수를 계산하고 반환해주었다.
그러나 이렇게 할 경우 1만명의 사람이 한 개의 게시글을 조회할 경우 한 번에 계산 쿼리문이 1만개가 돌게되므로 성능에 문제가 될 수 있다고 한다.
이 부분은 얼추 예상했던 부분이라, 빠르게 수정해주었다.
/**
* 평점 계산
*/
@Async
@Transactional
@Override
public void calculateRating() {
List<Long> boardIdList = boardRepository.getAllBoardId();
for (Long boardId : boardIdList) {
double avgScore = scoreRepository.getBoardAverageScore(boardId);
boardRepository.updateAverageScore(boardId, avgScore, LocalDateTime.now());
}
}
해당 로직을 1분마다 돌리는 스케쥴러 코드를 만들었다.
@Scheduled(fixedDelay = 1000 * 60) // 1분에 한 번 실행
public void insertAverageScoreDB() {
scoreService.calculateRating();
}
이렇게 해서 1분에 한 번씩 평균점수가 계산된 값이 TB_BOARD의 신규 컬럼 AVG_SCORE에 입력이 되고, 게시글을 불러올 때에는 별도의 계산없이 AVG_SCORE에 들어있는 값을 그대로 가져오니 성능의 문제를 해결할 수 있었다.
3. log back
로그도 확인을 할 수 있게 되었다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<property name="moduleId" value="around_hub_spring_boot"/>
<property name="type" value="around_hub"/>
<property name="logback" value="logback"/>
<property name="logdir" value="log"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<!-- 파일 앱렌더 추가 -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>${logdir}/application.log</file>
<append>true</append>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console"/>
<!-- 파일 앱렌더 추가를 root에 설정 -->
<appender-ref ref="file"/>
</root>
</configuration>
이렇게 log를 설정해주었고,
log 폴더 속 application.log 파일에 로그가 정상적으로 쌓이는 것을 확인 할 수 있었다.
해당 CRUD 개발 깃허브 링크 :
https://github.com/JeonDaehong/JpaNativeQuery-Redis-CRUD-Project
GitHub - JeonDaehong/JpaNativeQuery-Redis-CRUD-Project
Contribute to JeonDaehong/JpaNativeQuery-Redis-CRUD-Project development by creating an account on GitHub.
github.com
'개발 회고록' 카테고리의 다른 글
[ 개발 회고록 ] Thread 와 Thread의 데이터 공유 (1) | 2024.01.02 |
---|---|
[ 개발 회고록 ] 비동기 메서드 동시성 문제 발견 및 수정 (0) | 2023.12.04 |
[ 개발 회고록 ] 게시글 평균 점수 기능을 구현해보자 (0) | 2023.11.27 |
[ 개발 회고록 ] 배타적 잠금 문제와 Validation (0) | 2023.11.22 |
[ 개발 회고록 ] JPA nativeQuery와 SpringBoot를 이용한 CRUD 개발 (0) | 2023.11.21 |