반응형
해당 글에서는 Spring Boot Cache를 이를 이용하는 방법에 대해서 이해를 돕기 위한 글입니다.
[참고] Spring Boot Cache의 이론과 환경설정 방법에 대해 궁금하시다면 이전에 작성한 글을 참고하시면 도움이 됩니다.
1) 개발환경 구성
💡 해당 개발환경은 MyBatis를 기반으로 RDBMS로부터 전달받은 데이터를 캐시 처리하는 환경으로 구성하였습니다.
개발 환경 | 버전 |
java | 1.8 |
Spring Boot | 2.7.4 |
빌드관리도구 | Gradle 7.5 |
개발 툴 | IntelliJ IDEA 2022.3 |
spring-boot-starter-cache | 2.7.4 |
mybatis-spring-boot-starter | 2.2.2 |
[참고] 이전에 Spring Boot의 Mybatis 환경을 구성한 글이 있습니다. 이전에 작성한 글을 참고하시면 도움이 됩니다.
2) Spring Boot Cache Annotation
💡 Spring Boot Cache에서는 아래의 주요한 어노테이션을 사용합니다.
💡 해당 어노테이션들은 value(캐시 값), key(캐시 키)을 기반으로 수행이 됩니다.
어노테이션 | 주요 기능 | 캐싱 시점 | 설명 |
@Cacheable | 캐시 조회, 저장 기능 | - 캐시 존재 시 ‘메서드 호출 전 실행’ - 캐시 미 존재 시 ‘메서드 호출 후 실행’ | - 캐시 정보를 메모리 상에 '저장'하거나 '조회' 해오는 기능을 수행할때 사용됩니다. |
@CachePut | 캐시 저장 기능 | - 캐시 존재 시 ‘메서드 호출 후 실행’ - 캐시 미 존재 시 ‘메서드 호출 후 실행’ | - 캐시 정보를 메모리상에 '저장'하며 존재 시 갱신하는 기능을 수행할때 사용합니다. |
@CacheEvict | 캐시 삭제 기능 | - beforeInvocation 속성값이 true일때 ‘메서드 호출 전 실행’ - beforeInvocation 속성값이 false일때 ‘메서드 호출 후 실행’ | - 캐시 정보를 메모리상에 '삭제'하는 기능을 수행할때 사용합니다. |
[ 더 알아보기 ]
💡 메서드의 호출 전/후 실행은 무슨 차이일까?
- 메서드의 호출 전에 실행 시 DB로부터 데이터를 처리하는 부분에 대해서 줄어들게 됩니다.
- 반대로 메서드의 호출 후에 실행하는 경우 DB로부터 데이터를 처리하여 가져오는 부분에 대해서 처리하는 부분이 늘어납니다.
💡 @CacheEvict의 beforeInvocation 속성이란?
- 캐시의 데이터를 삭제하는데 메서드 호출 전/후에 발생할지에 대해 설정하는 속성을 의미합니다.
- 속성의 기본값은 false이며 메서드 실행 전에 캐시에서 데이터를 삭제하여 예외가 발생한다면 캐시에서 데이터가 삭제되지 않습니다.
- 이에 따라 예외가 발생할 가능성이 있는 메서드에 대해서 beforeInvocation= “true”를 사용하지 않는 것이 좋습니다.
💡 @Cacheable과 @CachePut의 다른 점은?
- @Cacheable의 경우는 ‘캐시가 존재하지 않을 경우’ 캐시를 저장하지만 @CachePut의 경우는 ‘캐시의 존재 여부를 떠나서’ 항상 저장 혹은 갱신을 수행합니다.
3) @Cacheable 이용 예시
💡 @Cacheable 이란?
- 캐시 정보를 메모리 상에 '저장'하거나 '조회' 해오는 기능을 수행할 때 사용됩니다.
- 상세한 설명으로는 캐시의 속성값인 '값(value)과 키(key)'를 기반으로 수행이 됩니다.
- 최초 호출이 된 경우에는 메서드가 호출되어 '캐시 정보를 저장' 하며, 동일하게 재 호출이 된 경우에는 메서드의 호출 없이 '저장된 캐시의 정보를 조회하여 반환'합니다.
- @CachePut 어노테이션과 비슷한 기능을 수행하지만 '캐시 정보를 조회'하는데 주로 사용합니다.
- 해당 어노테이션의 선언 위치는 주로 @Service를 선언한 인터페이스의 구현체 부분에서 함께 선언하여 사용합니다.
1. 파라미터 존재 없이(Cache Key) @Cacheable 이용 예시
💡 해당 방법에서는 ‘파라미터가 존재하지 않은 상태’에서 @Cacheable를 사용하는 경우에 대해서 확인해 봅니다.
[참고] @Cacheable Annotation의 속성값 종류
속성 | 설명 |
value | 캐시 이름을 지정합니다. |
key | 캐시할 값을 식별하는 데 사용되는 SpEL 표현식입니다. |
condition | 캐시를 적용할 조건을 지정합니다. |
unless | 캐시를 적용하지 않을 조건을 지정합니다. |
[ 더 알아보기 ]
💡SpEL(Spring Expression Language) 표현식이란?
- 스프링 표현언어로 스프링 프레임워크에서 사용되는 표현 언어입니다. 주로 XML이나 애노테이션에서 사용되며, 객체 그래프를 조회하거나 조작하는 데 사용됩니다.
1.1. SQL Mapper 구성
💡 데이터베이스로부터 코드의 모든 정보를 조회해 오는 SQL문입니다.
💡 해당 부분에서는 조건문(파라미터) 없이 테이블의 데이터를 조회합니다.
<!-- 전체 코드를 조회합니다-->
<select id="selectCodeList" resultType="codeDto">
SELECT t1.*
FROM multiflex_scma.tb_code t1
</select>
1.2. CodeServiceImpl.java 구성
💡 해당 파일에서는 interface의 구현체를 구성하는 파일에서 캐시 어노테이션을 적용하였으며 메서드의 기능은 코드 리스트를 조회하는 기능을 수행합니다.
💡 해당 부분에서는 ‘key’ 속성을 포함하지 않은 상태에서 @Cacheable("codeCacheInfo")를 통해서 최초 캐시를 만들기 위해 저장 형태로 구성하였습니다.
@Slf4j
@Service
public class CodeServiceImpl implements CodeService {
private final SqlSession sqlSession;
public CodeServiceImpl(SqlSession ss) {
this.sqlSession = ss;
}
/**
* 코드 리스트 조회
*
* @param codeDto 코드
* @return CodeDto
*/
@Transactional(readOnly = true)
@Cacheable(value = "codeCacheInfo")
public CodeDto selectCode(CodeDto codeDto) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
return cm.selectCode(codeDto);
}
}
1.3. 결과 화면
💡 결과적으로 API를 여러 번 호출하였을 경우 캐시 데이터가 저장되지 않고 매번 DB에서 데이터를 조회해 주는 것을 확인할 수 있습니다.
1.4. 결론
💡 즉 @Cacheable 어노테이션에서 속성 'key'를 사용하지 않으면 데이터가 저장되지 않음을 확인하였습니다.
💡 인자(파라미터)가 존재하지 않으면 캐시에 저장이 되지 않아서 반복적으로 메서드가 실행됨이 확인되었습니다.
2. 파라미터 존재(Cache Key)하고 @Cacheable 이용 예시
💡 해당 방법에서는 ‘파라미터가 존재하는 상태’에서 @Cacheable를 사용하는 경우에 대해서 확인해 봅니다.
2.1. SQL Mapper
💡 데이터베이스로부터 코드의 다건을 조회해 오는 SQL문입니다.
💡 해당 부분에서는 코드 키(cd)에 따라서 데이터를 단건으로 조회하는 기능을 수행합니다.
<!--코드를 조회합니다-->
<select id="selectCode" resultType="codeDto">
SELECT t1.grp_cd
, t1.cd
, t1.grp_cd_nm
, t1.cd_nm
FROM multiflex_scma.tb_code t1
WHERE t1.use_yn = true
<if test="cd != null">
AND t1.cd = #{cd}
</if>
</select>
2.2. CodeServiceImpl.java
💡 해당 파일에서는 interface의 구현체를 구성하는 파일에서 캐시 어노테이션을 적용하였으며 메서드의 기능은 코드의 단검을 조회하는 기능을 수행합니다.
💡 해당 부분에서는 @Cacheable("codeCacheInfo")를 통해서 최초 조회에 캐시를 저장하고 인자에 ‘key’ 속성을 포함하여 캐시로 저장이 됩니다.
@Slf4j
@Service
public class CodeServiceImpl implements CodeService {
private final SqlSession sqlSession;
public CodeServiceImpl(SqlSession ss) {
this.sqlSession = ss;
}
/**
* 코드 리스트를 조회합니다.
*
* @param codeDto CodeDto
* @return List<CodeDto>
*/
@Cacheable(value = "codeCacheInfo", key="#codeDto.grpCd")
public List<CodeDto> selectCodeList(CodeDto codeDto) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
return cm.selectCodeList(codeDto);
}
}
2.3. 결과 화면
💡 결과적으로 동일한 API를 여러 번 호출하였을 경우 캐시에 저장이 되어서 DB에서 데이터를 조회해 오는 것이 아닌 캐시에서 데이터를 조회해 오는 것이 확인되었습니다.
💡 즉, 인자가 존재한다면 ‘최초 한 번을 조회’하고 이후에는 캐싱된 정보를 조회해 주는 상황을 보여주고 있습니다.
2.4. 결과
💡 Cache의 키가 존재해야 메서드 실행 이전에 수행되어서 캐시 정보를 기반으로 데이터를 가져옵니다.
💡 인자가 존재하면, 최초 한 번은 DB에서 조회를 해오고 이후는 캐시 정보에 저장된 데이터를 가져와서 SQL문을 수행하지 않습니다.
4) @CachePut 사용 예시
💡 @CachePut 이란?
- 캐시 정보를 메모리상에 '저장'하며 존재 시 갱신하는 기능을 수행할 때 사용합니다.
- 상세한 설명으로는 캐시의 속성값인 '값(value)과 키(key)'를 기반으로 수행이 됩니다.
- 최초 호출이 된 경우에는 메서드가 호출되어 ‘캐시 정보를 저장’하며, 동일하게 재 호출이 된 경우에도 메서드가 호출되어 ‘캐시 정보를 갱신(수정 및 저장)’하여 캐시의 정보를 반환합니다.
- @Cacheable 어노테이션과 비슷한 기능을 수행하지만 '캐시 정보를 저장'하는데 주로 사용이 됩니다.
- 해당 어노테이션의 선언 위치는 주로 @Service를 선언한 인터페이스의 구현체 부분에서 함께 선언하여 사용합니다.
1. SQL Mapper
💡 데이터베이스로부터 코드 목록에서 코드 키 값을 기반으로 조회합니다.
<!--코드 값을 기반으로 코드 정보를 조회합니다-->
<select id="selectCodeByCd" resultType="codeDto">
SELECT t1.grp_cd
, t1.cd
, t1.grp_cd_nm
, t1.grp_cd
, t1.grp_cd_nm
, t1.cd_nm
, t1.sort_order
FROM multiflex_scma.tb_code t1
WHERE t1.cd = #{cd}
</select>
2. CodeServiceImple.java
💡 해당 파일은 interface의 구현체에 해당하는 CodeServiceImpl.java 파일의 selectCodeByCd() 메서드입니다.
💡 해당 메서드에서는 키 값을 기반으로 가져온 데이터를 ‘캐시’에 저장합니다. 최초 호출 이후에 재 호출을 하는 경우에도 해당 데이터를 ‘갱신’합니다.
/**
* 코드 키 값을 기반으로 코드 정보를 조회합니다
*
* @param cd String
* @return CodeDto
*/
@Transactional(readOnly = true)
@CachePut(value = "codeCacheInfo", key = "#cd")
public CodeDto selectCodeByCd(String cd) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
return cm.selectCodeByCd(cd);
}
💡 해당 파일은 interface의 구현체에 해당하는 CodeServiceImpl.java 파일의 selectCode() 메서드입니다.
💡 이전에 selectCodeByCd() 메서드를 통해서 캐시 값을 갱신하였다면 selectCode() 메서드를 통해서 저장된 캐시값을 조회하는 예시를 구성하였습니다.
/**
* 코드 리스트 조회
*
* @param codeDto 코드
* @return CodeDto
*/
@Transactional(readOnly = true)
@Cacheable(value = "codeCacheInfo", key = "#codeDto.cd")
public CodeDto selectCode(CodeDto codeDto) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
return cm.selectCode(codeDto);
}
3. 결과 화면
3.1. @CachePut을 통해서 캐시를 저장합니다.
💡 selectCodeByCd() 메서드를 통해서 캐시를 저장합니다.
3.2. @Cacheable를 통해 캐시 정보를 조회합니다.
💡 selectCode() 메서드를 통해 캐시가 저장되어 있는지 확인해 봅니다.
- 해당 로그를 확인해 보았을 때 selectCode() 메서드는 실행하지 않고 이전에 selectCodeByCd()로 저장된 캐시정보로 저장된 데이터를 조회해 옵니다.
3.3. @CachePut를 통해서 캐시를 갱신합니다.
💡 selectCodeByCd() 메서드에 저장한 캐시를 기반으로 재 호출을 하여서 갱신을 합니다. 해당 작업을 위해서 데이터베이스 데이터를 수정하였습니다.
💡 동일하게 호출하는 경우 캐시 된 값을 조회하기에 해당 데이터가 갱신되지 않았습니다.
💡 이를 위해서 메서드를 재 호출하여서 갱신합니다.
3.4. @Cacheable 갱신된 캐시를 확인합니다.
💡 이전에 갱신한 데이터를 위해 재 조회를 합니다. 이를 통해 데이터가 잘 갱신되었음을 확인하였습니다.
5) @CacheEvict 사용 예시
💡 @CacheEvict 란?
- 캐시 정보를 메모리상에 '삭제'하는 기능을 수행할 때 사용합니다.
- 상세한 설명으로는 캐시는 속성값인 '값(value)과 키(key)'를 기반으로 수행이 됩니다.
- 어노테이션의 속성인 beforeInvocation의 값이 true이면 '메서드 호출 전'에 실행이 되며, false이면 '메서의 호출 후에 실행'이 됩니다.
- 캐시 정보가 ‘사용되지 않는 상태로 변경’이 되거나 관련 캐시 정보가 필요가 없어지는 ‘데이터 삭제’와 같은 경우에 주로 사용이 됩니다.
- 해당 어노테이션의 선언 위치는 주로 @Service를 선언한 인터페이스의 구현체 부분에서 함께 선언하여 사용합니다.
1. SQL Mapper
💡 해당 SQL문에서는 캐시 정보가 필요가 없어지는 상태로 변경되는 경우(use_yn = false)에 대한 예시로 작성하였습니다.
<!--코드를 삭제합니다-->
<update id="deleteCode" parameterType="codeDto">
UPDATE multiflex_scma.tb_code
SET use_yn = false
WHERE cd = #{cd}
</update>
2. CodeServiceImpl.java
💡 해당 예시에서는 캐시 정보를 삭제를 위해 @CacheEvict(value = "codeCacheInfo", key = "#codeDto.grpCd", beforeInvocation = false)로 구성하였습니다.
💡 codeCacheInfo라는 캐시의 codeDto.grpCd키 값으로 접근하여 존재하는 캐시를 삭제하는 부분으로 구성하였습니다.
/**
* 코드 삭제
*
* @param codeDto CodeDto
* @return int
*/
@Transactional
@CacheEvict(value = "codeCacheInfo", key = "#codeDto.cd", beforeInvocation = false)
public int deleteCode(CodeDto codeDto) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
try {
return cm.deleteCode(codeDto);
} catch (Exception e) {
throw new BusinessExceptionHandler(e.getMessage(), ErrorCode.INSERT_ERROR);
}
}
💡 또한 해당 예시에서는 캐시 정보가 삭제가 잘 되었는지 확인을 위해 코드 정보를 조회하는 @Cacheable(value = "codeCacheInfo", key = "#codeDto.cd")를 통해서 캐시 정보가 조회되었는지 확인하는 부분으로 구성하였습니다.
/**
* 코드 리스트 조회
*
* @param codeDto 코드
* @return CodeDto
*/
@Transactional(readOnly = true)
@Cacheable(value = "codeCacheInfo", key = "#codeDto.cd")
public CodeDto selectCode(CodeDto codeDto) {
CodeMapper cm = sqlSession.getMapper(CodeMapper.class);
return cm.selectCode(codeDto);
}
3. 결과 화면
3.1. 캐시 정보를 최초 저장합니다. : 캐시 저장(@CachePut)
3.2. 캐시 정보의 저장 여부를 확인합니다. : 캐시 조회(@Cacheable)
오늘도 감사합니다. 😀
반응형
'Java > Spring Boot' 카테고리의 다른 글
[Java] 스케줄링 & Spring Boot Quartz 이해하고 적용하기 -1 : 설정 및 간단예시 (1) | 2023.04.22 |
---|---|
[Java] Spring Boot Batch 이해하고 설정하기 -1 : 정의 및 이해 (2) | 2023.04.22 |
[Java] Spring Boot Cache 이해하고 설정하기 -1 : 정의, 환경 설정 (2) | 2023.04.16 |
[Java] Spring Boot AOP(Aspect-Oriented Programming) 이해하고 설정하기 (2) | 2023.03.01 |
[Java] Spring Boot Tomcat Access Log 이해하고 설정하기 (0) | 2023.02.26 |