기록하며 성장한다 - 개발, 회고

개발 회고록

[ 개발 회고록 ] 병렬 프로그래밍과 비동기 구현

전대홍 2023. 11. 29. 21:13

 

해당 포스팅은 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