자주 불러오는 데이터는 캐싱으로 조회 성능을 높여보자

안상철·2023년 1월 18일
0

Kotlin Spring Boot

목록 보기
14/14
post-thumbnail

우리는 API를 통해 조회한 데이터를 화면에 입혀 클라이언트에게 보여준다.

그런데 변경이 잘 되지 않으면서 자주 조회되는 데이터가 있다면 매번 api를 요청 할 때 마다 쿼리를 날리는 것이 서버에서는 부담이 될 수 있기에 조회 성능이 좋지 않다.

자주 사용하는 데이터는 캐싱을 통해 캐시저장소에 데이터를 저장 해 놓고 불러와 조회성능을 높여보자

회사에서 대학교 평가시스템 업무를 담당하고 있다. 대학교 학과정보는 변동되지 않으므로, 학과정보를 캐시에 넣어 사용 해 보자.

1. 로컬 캐시 라이브러리 ehcache

ehcache는 로컬 캐시 라이브러리이다.

캐싱을 사용 할 디렉토리나 모듈의 build.gradle에

implementation("org.ehcache:ehcache:3.9.5")

를 추가하자

2. CacheConfig

다음은 언제나 그랬듯 캐시 라이브러리를 사용하기 위한 설정파일이다.

class CacheConfig

class CacheLogger : CacheEventListener<Any, Any> {

    private val log = LoggerFactory.getLogger(javaClass)

    override fun onEvent(cacheEvent: CacheEvent<out Any, out Any>) {
        log.info("Key: [${cacheEvent.key}] | EventType: [${cacheEvent.type}] | Old value: [${cacheEvent.oldValue}] | New value: [${cacheEvent.newValue}]")
    }
}

정확히는 캐시 로그 파일이다. 캐싱 처리에 대한 모니터링을 할 수 있다.

@Configuration을 통해 설정파일임을 알리고 @EnableCaching 어노테이션으로 캐싱을 사용할 수 있도록 한다. ApiAppication 클래스에 작성 해 줘도 된다.

3. ehcache.xml

다음은 xml파일로 스프링에게 캐싱정보를 알려준다. 캐싱 할 이벤트와 저장시간, 저장공간을 명명 해 놓는 것으로 보인다. dept라는 이름으로 10분간 데이터를 임시 저장소에 저장 해 놓고 꺼내다 쓴다.
key-type 태그를 보니 api를 호출할 때 intercept해서 판별하는 것 같다.

<config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>
    <cache-template name="default">
        <listeners>
            <listener>
                <class>kr.co.~.config.CacheLogger</class>
                <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
                <event-ordering-mode>UNORDERED</event-ordering-mode>
                <events-to-fire-on>CREATED</events-to-fire-on>
                <events-to-fire-on>UPDATED</events-to-fire-on>
                <events-to-fire-on>EXPIRED</events-to-fire-on>
                <events-to-fire-on>REMOVED</events-to-fire-on>
                <events-to-fire-on>EVICTED</events-to-fire-on>
            </listener>
        </listeners>

    </cache-template>
    
        <cache alias="dept" uses-template="default">
        <key-type>java.lang.String</key-type>
        <value-type>java.util.ArrayList</value-type>
        <expiry>
            <ttl unit="minutes">10</ttl>
        </expiry>
        <resources>
            <heap>100</heap>
            <offheap unit="MB">1</offheap>
        </resources>
    </cache>

4. Service

다음은 서비스단에서 @Cacheable 어노테이션에 캐싱 할 이름을 정해주면 해당 정보가 조회될 때 미리 저장 된 캐시 저장소에서 이름으로 데이터를 찾는다. 아래 예시는 CQRS 개념에 따라 읽기 전용으로 만든 서비스이다. 대학교 정보로 해당 대학교에 소속 된 학과를 모두 찾는다.

@Service
@Transactional(readOnly = true)
class SchoolInfoQueryService(
    private val deptRepository: DeptRepository,
) {

    @Cacheable(cacheNames = ["dept"])
    fun findDepts(collegeCode: String): List<DeptOut> {
        return deptRepository.getListByCollegeCode(collegeCode)
            .map { DeptOut.fromEntity(it) }
    }

}

이제 위 서비스를 불러올 때 마다 매번 쿼리를 날리지 않고, 정해진 기한 안에서 캐싱 된 정보를 불러온다.

캐시를 사용하면 조회 성능이 높아진다는 장점이 있지만 단점으로는 업데이트가 일어나도 미리 캐싱된 정보를 가져오기 때문에 조회 타이밍이 맞지 않을 수 있다. 이 경우 서버를 다시 돌려야 했었다. 따라서 극단적으로 변경되지 않는 정보가 아니면 사용 할 경우가 많지는 않을 것 같다.

profile
웹 개발자(FE / BE) anna입니다.

0개의 댓글