Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE-88] 지원서 칸반보드 및 지원자 페이지별 면접 기록 조회 캐싱 적용 #231 #232

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fef89cf
[config]: 캐시 응답을 위해 CacheControl 헤더 적용
Profile-exe Aug 28, 2024
4ca0c78
[config]: ETag 응답 헤더를 포함시켜주는 필터 등록
Profile-exe Aug 28, 2024
5a22e03
[config]: 캐싱을 위한 CacheManager 빈 등록
Profile-exe Aug 28, 2024
7b4fe05
[feat]: Board 응답 캐싱
Profile-exe Aug 28, 2024
86a9d93
[feat]: Column 응답 캐싱
Profile-exe Aug 28, 2024
7ae0121
[feat]: Card 응답 캐싱
Profile-exe Aug 28, 2024
60a8887
[feat]: Record 응답 캐싱
Profile-exe Aug 28, 2024
1e94f81
[fix]: LocalDateTime 직렬화 문제 해결
Profile-exe Aug 29, 2024
7254ac2
[fix]: RedisCacheManager에서 BoardCardResponseDto 직렬화 오류 해결
Profile-exe Aug 29, 2024
8f1cad3
Revert "[config]: 캐싱을 위한 CacheManager 빈 등록"
Profile-exe Aug 29, 2024
ec45fd2
[feat]: boardsByColumnIds가 columnId마다 개별적으로 캐싱
Profile-exe Aug 31, 2024
69c8383
[feat]: 커스텀 어노테이션에서 SpEL 사용을 위해 파서 구현
Profile-exe Sep 1, 2024
d5a355a
[feat]: 캐시 무효화 처리를 위한 Aspect와 어노테이션 구현
Profile-exe Sep 1, 2024
50a27e2
[feat]: BoardService 캐시 무효화 적용
Profile-exe Sep 1, 2024
b1a9523
[feat]: ColumnService::getByNavigationId 캐싱 key 명시
Profile-exe Sep 1, 2024
8b11bf2
[feat]: CardService 캐시 무효화 적용
Profile-exe Sep 2, 2024
4bec5ca
[feat]: CommentService 캐시 무효화 적용
Profile-exe Sep 2, 2024
329c390
[fix]: 지원서 카드와 업무 카드에 따라 캐시 무효화 전략을 구분
Profile-exe Sep 3, 2024
8d8faeb
[refactor]: 불필요한 로직 제거
Profile-exe Sep 3, 2024
ad88883
[refactor]: 캐시 저장소 이름을 _Ids에서 _Id로 변경
Profile-exe Sep 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.econovation.recruit.api.card.service;

import com.econovation.recruitdomain.domains.board.domain.Board;
import com.econovation.recruitdomain.out.BoardLoadPort;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class BoardCacheService {

private final BoardLoadPort boardLoadPort;

@Cacheable(value = "boardsByColumnsId", key = "#columnsId")
public List<Board> getBoardByColumnsId(Integer columnsId) {
return boardLoadPort.getBoardByColumnsId(columnsId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

import com.econovation.recruit.api.card.usecase.BoardLoadUseCase;
import com.econovation.recruit.api.card.usecase.BoardRegisterUseCase;
import com.econovation.recruitcommon.annotation.InvalidateCacheByCardLocation;
import com.econovation.recruitcommon.annotation.InvalidateCacheByColumnLocation;
import com.econovation.recruitcommon.annotation.InvalidateCacheByHopeField;
import com.econovation.recruitcommon.annotation.InvalidateCache;
import com.econovation.recruitcommon.annotation.InvalidateCaches;
import com.econovation.recruitcommon.utils.Result;
import com.econovation.recruitdomain.common.aop.redissonLock.RedissonLock;
import com.econovation.recruitdomain.domains.board.domain.Board;
Expand Down Expand Up @@ -38,6 +43,7 @@ public class BoardService implements BoardLoadUseCase, BoardRegisterUseCase {
private final BoardLoadPort boardLoadPort;
private final ColumnLoadPort columnLoadPort;
private final ColumnRecordPort columnRecordPort;
private final BoardCacheService boardCacheService;

/* @Override
public Board save(Map<String, Integer> newestLocation, String hopeField, Integer navLoc) {
Expand Down Expand Up @@ -104,11 +110,16 @@ public Board findById(Integer id) {
}

@Override
@InvalidateCaches({
@InvalidateCache(cacheName = "boardsByColumnsId", key = "#board.columnId"),
@InvalidateCache(cacheName = "boardCardsByNavigationId", key = "#board.navigationId")
})
public void execute(Board board) {
boardRecordPort.save(board);
}

@Override
@InvalidateCache(cacheName = "boardsByColumnsId", key = "#columnId")
public Board createWorkBoard(Integer columnId, Long cardId) {
Columns column = columnLoadPort.findById(columnId);
List<Board> boardByNavigationIdAndColumnId =
Expand Down Expand Up @@ -160,9 +171,8 @@ public Columns createColumn(String title, Integer navigationId) {
}*/

@Override
@InvalidateCacheByHopeField
public void createApplicantBoard(String applicantId, String hopeField, Long cardId) {
// \"hopeField\" -> hopeField 로 변경
hopeField = hopeField;
Integer columnsId = 0;
if (hopeField.equals("개발자")) {
columnsId = DEVELOPER_COLUMNS_ID;
Expand Down Expand Up @@ -197,6 +207,10 @@ public void createApplicantBoard(String applicantId, String hopeField, Long card

@Override
@Transactional
@InvalidateCaches({
@InvalidateCache(cacheName = "columnsByNavigationId", key = "#navigationId"),
@InvalidateCache(cacheName = "boardCardsByNavigationId", key = "#navigationId")
})
public Columns createColumn(String title, Integer navigationId) {
Columns column = Columns.builder().title(title).navigationId(navigationId).build();

Expand Down Expand Up @@ -244,7 +258,9 @@ public Navigation getNavigationByNavLoc(Integer navLoc) {

@Override
public List<Board> getBoardByColumnsIds(List<Integer> columnsIds) {
return boardLoadPort.getBoardByColumnsIds(columnsIds);
return columnsIds.stream()
.flatMap(columnId -> boardCacheService.getBoardByColumnsId(columnId).stream())
.toList();
}

@Override
Expand Down Expand Up @@ -275,6 +291,7 @@ public Result<Board> getBoardByNextBoardId(Integer boardId) {
paramClassType = UpdateLocationBoardDto.class,
leaseTime = 500,
waitTime = 500)
@InvalidateCacheByCardLocation
public void relocateCard(UpdateLocationBoardDto updateLocationBoardDto) {
List<Integer> invisibleBoard = List.of(1, 2, 3);
// 기준 보드는 이동이 불가하다.
Expand All @@ -295,6 +312,7 @@ public void relocateCard(UpdateLocationBoardDto updateLocationBoardDto) {

@Override
@Transactional
@InvalidateCacheByColumnLocation
public void updateColumnLocation(UpdateLocationColumnDto updateLocationDto) {
// 첫번째로 옮기는 경우 (nextColumnId == 0)
if (updateLocationDto.getTargetColumnId().equals(0)) {
Expand Down Expand Up @@ -336,6 +354,7 @@ public void updateColumnLocation(UpdateLocationColumnDto updateLocationDto) {
}

@Override
@InvalidateCache(cacheName = "boardsByColumnsId", key = "#board.columnId")
public void delete(Board board) {
boardRecordPort.delete(board);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import com.econovation.recruit.api.card.usecase.CardRegisterUseCase;
import com.econovation.recruit.api.card.usecase.ColumnsUseCase;
import com.econovation.recruit.api.config.security.SecurityUtils;
import com.econovation.recruitcommon.annotation.InvalidateCacheByCardId;
import com.econovation.recruitcommon.annotation.InvalidateCacheByCreateWorkCard;
import com.econovation.recruitcommon.annotation.InvalidateCacheByUpdateWorkCard;
import com.econovation.recruitcommon.utils.Result;
import com.econovation.recruitdomain.common.aop.domainEvent.Events;
import com.econovation.recruitdomain.common.events.WorkCardDeletedEvent;
Expand All @@ -32,6 +35,8 @@
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -56,6 +61,7 @@ public List<Card> findAll() {

@Override
@Transactional(readOnly = true)
@Cacheable(value = "boardCardsByNavigationId", key = "#navigationId")
public List<BoardCardResponseDto> getByNavigationId(Integer navigationId, Integer year) {
Long userId = SecurityUtils.getCurrentUserId();
List<Columns> columns = columnsUseCase.getByNavigationId(navigationId);
Expand Down Expand Up @@ -153,6 +159,7 @@ public CardResponseDto findCardById(Long cardId) {

@Override
@Transactional
@InvalidateCacheByCardId
public void deleteById(Long cardId) {
Board board = boardLoadUseCase.getBoardByCardId(cardId);
Result<Board> prevBoard = boardLoadUseCase.getBoardByNextBoardId(board.getId());
Expand All @@ -173,6 +180,7 @@ public void deleteById(Long cardId) {

@Override
@Transactional
@InvalidateCacheByCreateWorkCard
public void saveWorkCard(CreateWorkCardDto createWorkCardDto) {
Card card =
Card.builder()
Expand All @@ -186,6 +194,7 @@ public void saveWorkCard(CreateWorkCardDto createWorkCardDto) {

@Override
@Transactional
@InvalidateCacheByUpdateWorkCard
public void update(Long cardId, UpdateWorkCardDto updateWorkCardDto) {
Card card = cardLoadPort.findById(cardId);
// 단 title 이 null일 수도 있고, content가 null일 수도 있다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.econovation.recruitdomain.out.ColumnRecordPort;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -14,6 +15,7 @@ public class ColumnService implements ColumnsUseCase {
private final ColumnRecordPort columnRecordPort;
private final ColumnLoadPort columnLoadPort;

@Cacheable(value = "columnsByNavigationId", key = "#navigationId")
public List<Columns> getByNavigationId(Integer navigationId) {
return columnLoadPort.getColumnByNavigationId(navigationId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.econovation.recruit.api.comment.usecase.CommentUseCase;
import com.econovation.recruit.api.config.security.SecurityUtils;
import com.econovation.recruitcommon.annotation.InvalidateCacheByCreateComment;
import com.econovation.recruitcommon.annotation.InvalidateCacheByCommentId;
import com.econovation.recruitcommon.annotation.InvalidateCacheByDeleteComment;
import com.econovation.recruitcommon.utils.Result;
import com.econovation.recruitdomain.common.aop.redissonLock.RedissonLock;
import com.econovation.recruitdomain.domains.card.domain.Card;
Expand All @@ -24,6 +27,7 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -39,6 +43,7 @@ public class CommentService implements CommentUseCase {

@Override
@Transactional
@InvalidateCacheByCreateComment
public Comment saveComment(CommentRegisterDto commentDto) {
Long userId = SecurityUtils.getCurrentUserId();
// applicantId null 이면 "" 으로 바꿔준다. cardId 가 null 이면 0 으로 바꿔준다.
Expand Down Expand Up @@ -81,6 +86,7 @@ private Comment convertComment(CommentRegisterDto commentDto, Long userId) {

@Override
@Transactional
@InvalidateCacheByCommentId
public void deleteComment(Long commentId) {
Long idpId = SecurityUtils.getCurrentUserId();
Comment comment = commentLoadPort.findById(commentId);
Expand Down Expand Up @@ -117,6 +123,7 @@ public Comment findById(Long commentId) {
@Override
@RedissonLock(LockName = "댓글좋아요", identifier = "commentId")
@Transactional
@InvalidateCacheByCommentId
public void createCommentLike(Long commentId) {
// 기존에 눌렀으면 취소 처리
Long idpId = SecurityUtils.getCurrentUserId();
Expand Down Expand Up @@ -145,6 +152,7 @@ private void createCommentLike(Comment comment, Long idpId) {
@Override
@RedissonLock(LockName = "댓글좋아요", identifier = "commentId")
@Transactional
@InvalidateCacheByCommentId
public void deleteCommentLike(Long commentId) {
// 현재 내가 눌렀던 댓글만 삭제할 수 있다.
Long idpId = SecurityUtils.getCurrentUserId();
Expand Down Expand Up @@ -217,6 +225,7 @@ public Boolean isCheckedLike(Long commentId) {

@Override
@Transactional
@InvalidateCacheByCommentId
public void updateCommentContent(Long commentId, Map<String, String> contents) {
String content = contents.get("content");
// 내가 작성한 comment 만 수정할 수 있다.
Expand All @@ -230,6 +239,7 @@ public void updateCommentContent(Long commentId, Map<String, String> contents) {
//
@Override
@Transactional(readOnly = true)
@Cacheable(value = "commentsByApplicantId", key = "#applicantId")
public List<CommentPairVo> findByApplicantId(String applicantId) {
Long idpId = SecurityUtils.getCurrentUserId();
List<Comment> comments = commentLoadPort.findByApplicantId(applicantId);
Expand All @@ -238,6 +248,7 @@ public List<CommentPairVo> findByApplicantId(String applicantId) {

@Override
@Transactional
@InvalidateCacheByDeleteComment
public void deleteCommentByCardId(Long cardId) {
List<Comment> comments = commentLoadPort.findByCardId(cardId);
if (comments.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.context.annotation.Profile;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.web.filter.ForwardedHeaderFilter;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;

Expand All @@ -23,34 +24,43 @@ public class ServletFilterConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean securityFilterChain(
@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
Filter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(Integer.MAX_VALUE - 3);
registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
return registration;
Filter securityFilter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(securityFilter);
registrationBean.setOrder(Integer.MAX_VALUE - 5);
registrationBean.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
return registrationBean;
}

@Bean
public FilterRegistrationBean setResourceUrlEncodingFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new ResourceUrlEncodingFilter());
registrationBean.setOrder(Integer.MAX_VALUE - 2);
registrationBean.setOrder(Integer.MAX_VALUE - 4);
return registrationBean;
}

@Bean
public FilterRegistrationBean setForwardedHeaderFilterOrder() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(forwardedHeaderFilter);
registrationBean.setOrder(Integer.MAX_VALUE - 1);
registrationBean.setOrder(Integer.MAX_VALUE - 3);
return registrationBean;
}

@Bean
public FilterRegistrationBean setHttpContentCacheFilterOrder() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(httpContentCacheFilter);
registrationBean.setOrder(Integer.MAX_VALUE);
registrationBean.setOrder(Integer.MAX_VALUE - 2);
return registrationBean;
}

@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> registrationBean
= new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
registrationBean.setOrder(Integer.MAX_VALUE - 1);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

@Configuration
@RequiredArgsConstructor
Expand All @@ -23,4 +26,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
// // registrationBean.setUrlPatterns(Arrays.asList("/api/v1/*"));
// return registrationBean;3
// }

@Override
public void addInterceptors(final InterceptorRegistry registry) {
CacheControl cacheControl = CacheControl.noCache().mustRevalidate();

WebContentInterceptor webContentInterceptor = new WebContentInterceptor();
webContentInterceptor.addCacheMapping(cacheControl, "/**");

registry.addInterceptor(webContentInterceptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -36,6 +37,7 @@ public class LabelService implements LabelUseCase {
waitTime = 1000L,
leaseTime = 1000L)
@Transactional
@CacheEvict(value = "boardCardsByNavigationId", allEntries = true)
public Boolean createLabel(String applicantId) {
Long idpId = SecurityUtils.getCurrentUserId();
Card card = cardLoadPort.findByApplicantId(applicantId);
Expand Down Expand Up @@ -67,6 +69,7 @@ public List<String> findByApplicantId(String applicantId) {
identifier = "applicantId",
waitTime = 1000L,
leaseTime = 1000L)
@CacheEvict(value = "boardCardsByNavigationId", allEntries = true)
public void deleteLabel(String applicantId) {
Long idpId = SecurityUtils.getCurrentUserId();
Label label = labelLoadPort.loadLabelByApplicantIdAndIdpId(applicantId, idpId);
Expand All @@ -87,6 +90,7 @@ public void deleteLabelByCardId(Long cardId) {

@Override
@Transactional
@CacheEvict(value = "boardCardsByNavigationId", allEntries = true)
public Boolean createLabelByCardId(Long cardId) {
Long idpId = SecurityUtils.getCurrentUserId();
Card card = cardLoadPort.findById(cardId);
Expand Down
Loading