작지만 꾸준한 반복

자주 사용되는 데이터 캐싱하기 본문

카테고리 없음

자주 사용되는 데이터 캐싱하기

iamjooon2 2023. 3. 19. 21:44

캐싱은 반복적으로 사용되는 데이터를 미리 저장해두는 것을 말한다.

갑자기 분위기 캐싱?

블랙잭 미션을 하면서 캐싱과 관련된 리뷰를 보았다.

이 블랙잭에서 사용되는 카드의 개수는 52개로 이미 정해져있다.

게임이 한 번 실행된다면 상관 없지만, 여러번 게임이 진행된다면 카드뭉치를 그때마다 새로 생성해야되니,

캐시로 성능상 이점을 가질 수 있지 않겠냐는 말이었다.

 

내 리뷰는 아니었지만, 해당 리뷰를 받은 코드와 내 코드가 별반 다를 게 없기에, 캐싱을 적용해보았다.

 

내 기존 코드는 다음과 같다.

public class Deck {

    private final List<Card> cards;
    
    ...
    
    public static Deck create() {
        List<Card> initialCards = generateCards();
        Collections.shuffle(initialCards);
        
        return new Deck(new LinkedList<>(initialCards));
    }
    
    
    private static List<Card> createCards() {
        return Arrays.stream(Denomination.values())
            .flatMap(denomination -> Arrays.stream(Suit.values())
                .map(suit -> new Card(denomination, suit))
            .collect(Collectors.toList());
    }
    
    public List<Card> getCards() {
        return Collections.unmodifableList(cards);
    }
}

52장의 카드를 새로 생성하는 방식이다.

 

난 이 코드를 아래와 같이 변경하였다

public class Deck {
    private static final List<Card> CARDS;
    
    static {
       CARDS = Arrays.stream(Denomination.values())
                .flatMap(denomination -> Arrays.stream(Suit.values())
                        .map(suit -> new Card(denomination, suit)))
                .collect(Collectors.toUnmodifiableList());
    }
    
    ...
    
    public List<Card> getCards() {
        return Collections.unmodifableList(CARDS);
    }    
 }

 

정적 변수를 사용하기위해 static final을 붙인 변수를 생성해두고, 그 다음 static 블록을 통해 해당 변수를 초기화 해준다

이후 방어적 복사를 통해 원본을 그대로 보내주는 방식을 사용하였다

 

 

다음 미션인 체스 미션에서도 이 캐싱을 적극 활용해보았다

 

체스판에는 가로 8칸, 세로 8칸, 총 64개의 포지션이 있다.

기보 이동시 move 현재칸 이동할 칸 형태로 입력하는 것이 요구사항이었기에

사용자가 기보 이동할 때마다 해당 64개의 포지션 중 하나인지 검증하고 생성해서 리턴한다면 성능상 낭비를 불러올 수 있다.

 

이러한 상황에서 64개의 포지션을 미리 캐싱해두었다.

public class Position {
    private static final Map<String, Position> CACHE;

    static {
        CACHE = Arrays.stream(File.values())
                .flatMap(file -> Arrays.stream(Rank.values())
                        .map(rank -> Map.entry(file.command() + rank.command(), new Position(file, rank))))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
    }
    
    public static Position from(final String position) {
        if (!CACHE.containsKey(position)) {
            throw new IllegalArgumentException("잘못된 위치값입니다.");
        }
        return CACHE.get(position);
    }
}

String 데이터 타입의 Position을 key, Position 객체를 value로 미리 생성해둔 다음

사용자가 String 형태로 입력했을 때, 그에 맞는 포지션을 가져오도록 하였다.

 

어차피 사용하는 Position은 지정되어있으니

미리 만들어두어 캐싱해두고 가져다 쓰자! 라는 생각이었다.

 

 

우아한테크코스 미션을 하면서 사소하지만 성능상 이점을 가져올 수 있는 캐싱을 떠올려보고, 적용해보았다

프로그램의 성능은 몹시 중요하다.

프로그램 개발시 성능상 이점을 챙길 수 있는 부분이 있을지 고민해보도록 하는 습관을 들여보아야겠다.