diff --git a/src/main/java/org/withtime/be/withtimebe/domain/date/preference/service/command/DatePreferenceTestCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/date/preference/service/command/DatePreferenceTestCommandServiceImpl.java index 15609e0..7f6be6a 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/date/preference/service/command/DatePreferenceTestCommandServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/date/preference/service/command/DatePreferenceTestCommandServiceImpl.java @@ -14,6 +14,8 @@ import org.withtime.be.withtimebe.domain.date.preference.repository.DatePreferenceQuestionRepository; import org.withtime.be.withtimebe.domain.date.preference.repository.DatePreferenceTestResultRepository; import org.withtime.be.withtimebe.domain.date.preference.util.DatePreferenceTestScoreCalculator; +import org.withtime.be.withtimebe.domain.member.annotation.GetPoint; +import org.withtime.be.withtimebe.domain.member.annotation.enums.PointAction; import org.withtime.be.withtimebe.domain.member.entity.Member; import org.withtime.be.withtimebe.global.error.code.DatePreferenceErrorCode; import org.withtime.be.withtimebe.global.error.exception.DatePreferenceException; @@ -33,6 +35,7 @@ public class DatePreferenceTestCommandServiceImpl implements DatePreferenceTestC private final DatePreferenceTestScoreCalculator datePreferenceTestScoreCalculator; @Override + @GetPoint(action = PointAction.COMPLETE_TEST) public DatePreferenceResponseDTO.TestResult test(Member member, DatePreferenceRequestDTO.Test request) { // valid 판단 if (!validateRequest(request)) { diff --git a/src/main/java/org/withtime/be/withtimebe/domain/date/service/command/DateCommandServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/date/service/command/DateCommandServiceImpl.java index b5e463f..b1e1ff7 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/date/service/command/DateCommandServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/date/service/command/DateCommandServiceImpl.java @@ -19,6 +19,8 @@ import org.withtime.be.withtimebe.domain.date.repository.DateCourseRepository; import org.withtime.be.withtimebe.domain.date.repository.DatePlaceRepository; import org.withtime.be.withtimebe.domain.date.service.command.dto.RecommendedCourseResult; +import org.withtime.be.withtimebe.domain.member.annotation.GetPoint; +import org.withtime.be.withtimebe.domain.member.annotation.enums.PointAction; import org.withtime.be.withtimebe.domain.member.entity.Member; import org.withtime.be.withtimebe.global.error.code.DateCourseErrorCode; import org.withtime.be.withtimebe.global.error.exception.DateCourseException; @@ -37,11 +39,9 @@ public class DateCommandServiceImpl implements DateCommandService{ private final DateCourseRepository dateCourseRepository; private final DatePlaceRepository datePlaceRepository; - /** 컨트롤러로 전달할 단일 추천 결과 (코스 + 중복 방지 시그니처) */ - - - @Transactional(readOnly = true) /** 단일 코스 생성 (저장/북마크/attemptCount 없음, excludedCourseSignatures로 중복 제외) */ + @Transactional(readOnly = true) + @GetPoint(action = PointAction.CREATE_DATE_COURSE) public RecommendedCourseResult createDateCourse(DateRequestDTO.CreateDateCourse request) { if (request == null || request.dateDurationTime() == null) { return new RecommendedCourseResult(List.of(), null); @@ -118,7 +118,6 @@ public RecommendedCourseResult createDateCourse(DateRequestDTO.CreateDateCourse } // ───────────────────────── 내부 로직 ───────────────────────── - /** 주소 키워드 토큰으로 후보 조회 + ID 기준 중복 제거(순서 보존) */ private List collectCandidatesByAddressTokens(List tokens) { if (tokens == null || tokens.isEmpty()) return List.of(); @@ -287,6 +286,7 @@ public DateCourse deleteDateCourseBookmark(Long dateCourseId, Member member){ } // 데이트코스 북마크 생성 - AI 기반 데이트 코스 만들기 + @GetPoint(action = PointAction.SAVE_DATE_COURSE) public DateCourseBookmark createDateCourseBookmarkWithGeneratedCourse( DateRequestDTO.SaveDateCourse request, Member member @@ -304,6 +304,7 @@ public DateCourseBookmark createDateCourseBookmarkWithGeneratedCourse( } // 데이트코스 북마크 생성 - 직접 데이트 코스 찾아보기 + @GetPoint(action = PointAction.SAVE_DATE_COURSE) public DateCourseBookmark createDateCourseBookmark(Long dateCourseId, Member member) { DateCourse dateCourse = dateCourseRepository.findById(dateCourseId) .orElseThrow(() -> new DateCourseException(DateCourseErrorCode.DateCourse_NOT_FOUND)); diff --git a/src/main/java/org/withtime/be/withtimebe/domain/date/service/query/DateQueryServiceImpl.java b/src/main/java/org/withtime/be/withtimebe/domain/date/service/query/DateQueryServiceImpl.java index 00e4be5..b10f019 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/date/service/query/DateQueryServiceImpl.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/date/service/query/DateQueryServiceImpl.java @@ -5,16 +5,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.withtime.be.withtimebe.domain.date.converter.DateConverter; import org.withtime.be.withtimebe.domain.date.dto.request.DateRequestDTO; import org.withtime.be.withtimebe.domain.date.entity.DateCourse; -import org.withtime.be.withtimebe.domain.date.entity.DateCourseBookmark; -import org.withtime.be.withtimebe.domain.date.repository.DateCourseBookmarkRepository; import org.withtime.be.withtimebe.domain.date.repository.DateCourseRepository; +import org.withtime.be.withtimebe.domain.log.placecategorylog.annotation.LogPlaceCategory; +import org.withtime.be.withtimebe.domain.member.annotation.GetPoint; +import org.withtime.be.withtimebe.domain.member.annotation.enums.PointAction; import org.withtime.be.withtimebe.domain.member.entity.Member; -import java.util.List; - @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -22,12 +20,15 @@ public class DateQueryServiceImpl implements DateQueryService { private final DateCourseRepository dateCourseRepository; + @LogPlaceCategory + @GetPoint(action = PointAction.VIEW_DATE_COURSE) public Page findDateCourses(DateRequestDTO.DateCourseSearchCond dateCourseSearchCond, Pageable pageable){ return dateCourseRepository.searchDateCourseByApplyPage(dateCourseSearchCond, pageable); } + @LogPlaceCategory + @GetPoint(action = PointAction.VIEW_DATE_COURSE) public Page findDateCourseBookmarks(DateRequestDTO.DateCourseSearchCond dateCourseSearchCond, Pageable pageable, Member member){ return dateCourseRepository.searchDateCourseBookmarkByMemberAndApplyPage(dateCourseSearchCond, member, pageable); } - } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/aop/LogPlaceCategoryAspect.java b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/aop/LogPlaceCategoryAspect.java index 4937c5b..2bede1e 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/aop/LogPlaceCategoryAspect.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/aop/LogPlaceCategoryAspect.java @@ -1,27 +1,21 @@ package org.withtime.be.withtimebe.domain.log.placecategorylog.aop; import java.lang.reflect.Field; -import java.time.DayOfWeek; -import java.time.Duration; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.reflect.MethodSignature; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -33,86 +27,61 @@ public class LogPlaceCategoryAspect { private final RedisTemplate redisTemplate; - @Before("@annotation(org.withtime.be.withtimebe.domain.log.placecategorylog.annotation.LogPlaceCategory)") + @Async + @AfterReturning("@annotation(org.withtime.be.withtimebe.domain.log.placecategorylog.annotation.LogPlaceCategory)") public void logPlaceCategory(JoinPoint joinPoint) { - - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - String[] paramNames = signature.getParameterNames(); + Object[] args = joinPoint.getArgs(); - List placeCategoryIds = IntStream.range(0, args.length) - .mapToObj(i -> extractIdsFromParam(paramNames[i], args[i])) - .flatMap(Collection::stream) + List keywords = Arrays.stream(args) + .flatMap(arg -> extractKeywordsFromDTO(arg).stream()) .toList(); - savePlaceCategoryIds(placeCategoryIds); + saveKeywords(keywords); } - // 1. 파라미터로부터 placeCategoryId 추출 - private List extractIdsFromParam(String paramName, Object arg) { - - if (paramName.contains("placeCategoryId") && arg instanceof Long id) { - return List.of(id); - } - - if (paramName.contains("placeCategoryId") && arg instanceof List list) { - return list.stream() - .filter(Long.class::isInstance) - .map(Long.class::cast) - .toList(); - } - - // DTO로 간주하고 추출 시도 - return extractIdsFromDTO(arg); - } - - // 2. DTO로부터 placeCategoryId 추출 - private List extractIdsFromDTO(Object dto) { + private List extractKeywordsFromDTO(Object dto) { if (dto == null) return Collections.emptyList(); - List ids = new ArrayList<>(); + List keywords = new ArrayList<>(); - // DTO에 정의된 필드에 접근 for (Field field : dto.getClass().getDeclaredFields()) { - if (!field.getName().contains("placeCategoryId")) continue; + if (!field.getName().contains("userPreferredKeywords")) continue; - field.setAccessible(true); // private 필드 접근 설정 + field.setAccessible(true); try { - Object value = field.get(dto); // 값 추출 - if (value instanceof Long placeCategoryId) { - ids.add(placeCategoryId); - } - else if (value instanceof List list) { - for (Object id : list) { - if (id instanceof Long placeCategoryId) { - ids.add(placeCategoryId); + Object value = field.get(dto); + if (value instanceof List list) { + for (Object keyword : list) { + if (keyword instanceof String str) { + keywords.add(str); } } } } catch (Exception e) { - log.warn("DTO에서 placeCategoryId 추출 실패"); + log.warn("[LogPlaceCategoryAspect] DTO에서 userPreferredKeywords 추출 실패", e); } } - return ids; + return keywords; } - private void savePlaceCategoryIds(List placeCategoryIds) { - if (placeCategoryIds.isEmpty()) return; + private void saveKeywords(List keywords) { + if (keywords.isEmpty()) return; - LocalDateTime now = LocalDateTime.now(); - String today = now.format(DateTimeFormatter.ofPattern("yyyyMMdd")); - String redisKey = "log:place-category:" + today; + LocalDate now = LocalDate.now(); + String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + String redisKey = "log:user-preferred-keywords:" + formattedDate; - // ZSET - 카테고리 별 검색 횟수 기록 - placeCategoryIds - .forEach(id -> redisTemplate.opsForZSet().incrementScore(redisKey, id, 1)); + keywords.forEach(keyword -> + redisTemplate.opsForZSet().incrementScore(redisKey, keyword, 1) + ); - // TTL - 이번 주까지로 설정 + // TTL 설정 Long expire = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS); if (expire == null || expire <= 0) { - LocalDateTime endOfWeek = now.with(DayOfWeek.SUNDAY).with(LocalTime.MAX); - Duration duration = Duration.between(now, endOfWeek); - redisTemplate.expire(redisKey, duration.getSeconds(), TimeUnit.SECONDS); + redisTemplate.expire(redisKey, 1, TimeUnit.HOURS); } + + log.info("[LogPlaceCategoryAspect] 키워드 로그 임시 저장 완료"); } } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/converter/PlaceCategoryLogConverter.java b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/converter/PlaceCategoryLogConverter.java index e4dc3b0..e904fd0 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/converter/PlaceCategoryLogConverter.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/converter/PlaceCategoryLogConverter.java @@ -3,6 +3,8 @@ import java.time.LocalDate; import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.withtime.be.withtimebe.domain.date.entity.PlaceCategory; import org.withtime.be.withtimebe.domain.log.placecategorylog.dto.PlaceCategoryLogResponseDTO; @@ -12,9 +14,17 @@ public class PlaceCategoryLogConverter { public static PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLogList toWeeklyPlaceCategoryLogList(List placeCategoryLogList) { - List weeklyPlaceCategoryLogList = placeCategoryLogList.stream() - .sorted(Comparator.comparing(PlaceCategoryLog::getCount).reversed()) // count 기준 내림차순 - .map(PlaceCategoryLogConverter::toWeeklyPlaceCategoryLog) + // 1. 키워드 별 count 합산 + Map countPerKeyword = placeCategoryLogList.stream() + .collect(Collectors.groupingBy( + PlaceCategoryLog::getPlaceCategoryLabel, + Collectors.summingInt(PlaceCategoryLog::getCount) + )); + + // 2. DTO 변환 + List weeklyPlaceCategoryLogList = countPerKeyword.entrySet().stream() + .map((entry) -> toWeeklyPlaceCategoryLog(entry.getKey(), entry.getValue())) + .sorted(Comparator.comparing(PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLog::count).reversed()) // count 기준 내림차순 .toList(); return PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLogList.builder() @@ -22,17 +32,16 @@ public static PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLogList toWeeklyPla .build(); } - public static PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLog toWeeklyPlaceCategoryLog(PlaceCategoryLog placeCategoryLog) { + public static PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLog toWeeklyPlaceCategoryLog(String placeCategoryLabel, Integer count) { return PlaceCategoryLogResponseDTO.WeeklyPlaceCategoryLog.builder() - .placeCategoryLabel(placeCategoryLog.getPlaceCategoryLabel()) - .count(placeCategoryLog.getCount()) + .placeCategoryLabel(placeCategoryLabel) + .count(count) .build(); } - public static PlaceCategoryLog toPlaceCategoryLog(PlaceCategory placeCategory, Integer count, LocalDate date) { + public static PlaceCategoryLog toPlaceCategoryLog(String placeCategoryLabel, Integer count, LocalDate date) { return PlaceCategoryLog.builder() - .placeCategoryId(placeCategory.getId()) - .placeCategoryLabel(placeCategory.getLabel()) + .placeCategoryLabel(placeCategoryLabel) .count(count) .date(date) .build(); diff --git a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/model/PlaceCategoryLog.java b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/model/PlaceCategoryLog.java index ddb999a..e78c18d 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/model/PlaceCategoryLog.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/model/PlaceCategoryLog.java @@ -22,7 +22,6 @@ public class PlaceCategoryLog extends BaseEntity { @Id private String id; - private Long placeCategoryId; private String placeCategoryLabel; private LocalDate date; private Integer count; diff --git a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/repository/PlaceCategoryLogRepository.java b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/repository/PlaceCategoryLogRepository.java index e7d48ec..cba6ae0 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/repository/PlaceCategoryLogRepository.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/repository/PlaceCategoryLogRepository.java @@ -8,5 +8,5 @@ public interface PlaceCategoryLogRepository extends MongoRepository { List findByDateBetween(LocalDate startDate, LocalDate endDate); - List findByPlaceCategoryIdInAndDate(List placeCategoryIds, LocalDate date); + List findByDateAndPlaceCategoryLabelIn(LocalDate date, List placeCategoryLabel); } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/scheduler/PlaceCategoryLogScheduler.java b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/scheduler/PlaceCategoryLogScheduler.java index 641cd7c..19203a0 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/scheduler/PlaceCategoryLogScheduler.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/log/placecategorylog/scheduler/PlaceCategoryLogScheduler.java @@ -34,7 +34,6 @@ public class PlaceCategoryLogScheduler { private final RedisTemplate redisTemplate; private final PlaceCategoryLogRepository placeCategoryLogRepository; - private final PlaceCategoryRepository placeCategoryRepository; @Scheduled(cron = "${scheduler.logs.place-category.sync-cron}") // 매 5분마다 @CacheEvict( @@ -43,64 +42,60 @@ public class PlaceCategoryLogScheduler { cacheManager = "redisCacheManager", beforeInvocation = false ) - public void syncPlaceCategoryLogsToDB() { + @Scheduled(cron = "${scheduler.logs.place-category.sync-cron}") // 매 5분마다 + public void syncUserPreferredKeywordsToDB() { log.info("[PlaceCategoryLogScheduler] 동기화 스케쥴러 동작"); // 현재 날짜 및 레디스 키 생성 - LocalDate now = LocalDate.from(LocalDateTime.now().minusMinutes(1)); + LocalDate now = LocalDate.now(); String formattedDate = now.format(DateTimeFormatter.ofPattern("yyyyMMdd")); - String redisKey = "log:place-category:" + formattedDate; + String redisKey = "log:user-preferred-keywords:" + formattedDate; // ZSET 추출 Set> zSet = redisTemplate.opsForZSet().rangeWithScores(redisKey, 0, -1); - // 없는 경우 Skip - if (zSet == null || zSet.isEmpty()) + if (zSet == null || zSet.isEmpty()) { + log.info("[PlaceCategoryLogScheduler] 저장된 로그가 없어 동기화 작업을 종료합니다."); return; + } - // ZSET 데이터 가공 - Map placeCategoryScoreMap = zSet.stream() + // 키워드별 점수 추출 + Map keywordScoreMap = zSet.stream() .collect(Collectors.toMap( - tuple -> Long.valueOf(String.valueOf(tuple.getValue())), + tuple -> String.valueOf(tuple.getValue()), tuple -> tuple.getScore().intValue() )); - // IN 쿼리로 이미 존재하는 PlaceCategoryLog 조회 - List placeCategoryIds = new ArrayList<>(placeCategoryScoreMap.keySet()); - List placeCategoryLogList = placeCategoryLogRepository.findByPlaceCategoryIdInAndDate(placeCategoryIds, now); - - // placeCategoryId - placeCategoryLog 매핑 - Map placeCategoryLogMap = placeCategoryLogList.stream() - .collect(Collectors.toMap(PlaceCategoryLog::getPlaceCategoryId, Function.identity())); - - // 레디스 데이터를 다큐먼트에 반영 - for (Map.Entry entry : placeCategoryScoreMap.entrySet()) { - Long placeCategoryId = entry.getKey(); + // 키워드를 통해 이미 저장되어 있던 로그 조회 + List keywords = new ArrayList<>(keywordScoreMap.keySet()); + List existingLogs = placeCategoryLogRepository.findByDateAndPlaceCategoryLabelIn(now, keywords); + + // for문에서 빠른 분기 처리를 위한 Map 생성 + Map logMap = existingLogs.stream() + .collect(Collectors.toMap(PlaceCategoryLog::getPlaceCategoryLabel, Function.identity())); + + // saveAll로 저장될 로그 리스트 + List logsToSave = new ArrayList<>(); + for (Map.Entry entry : keywordScoreMap.entrySet()) { + String keyword = entry.getKey(); Integer score = entry.getValue(); - // 기존 로그가 존재하는 경우 count만 증가 - if (placeCategoryLogMap.containsKey(placeCategoryId)) { - placeCategoryLogMap.get(placeCategoryId).incrementCount(score); - } - // 그렇지 않으면 새로 로그 생성 - else { - Optional placeCategoryOptional = placeCategoryRepository.findById(placeCategoryId); - if(placeCategoryOptional.isPresent()) { - PlaceCategory placeCategory = placeCategoryOptional.get(); - PlaceCategoryLog newPlaceCategoryLog = PlaceCategoryLogConverter - .toPlaceCategoryLog(placeCategory, score, now); - placeCategoryLogList.add(newPlaceCategoryLog); - } + if (logMap.containsKey(keyword)) { + logMap.get(keyword).incrementCount(score); + } else { + PlaceCategoryLog log = PlaceCategoryLogConverter.toPlaceCategoryLog(keyword, score, now); + logsToSave.add(log); } } - - // 다큐먼트를 DB에 반영 - if (!placeCategoryLogList.isEmpty()) { - placeCategoryLogRepository.saveAll(placeCategoryLogList); + + // saveAll로 한번에 저장되도록 + if (!logsToSave.isEmpty() || !existingLogs.isEmpty()) { + placeCategoryLogRepository.saveAll(existingLogs); // 기존 로그 count 증가 반영 + placeCategoryLogRepository.saveAll(logsToSave); // 신규 로그 저장 redisTemplate.delete(redisKey); - log.info("[PlaceCategoryLogScheduler] 카테고리 로그 저장 완료, 개수 : {}", placeCategoryLogList.size()); + log.info("[PlaceCategoryLogScheduler] 키워드 로그 저장 완료, 개수 : {}", logsToSave.size() + existingLogs.size()); } } } diff --git a/src/main/java/org/withtime/be/withtimebe/domain/member/aop/GetPointAspect.java b/src/main/java/org/withtime/be/withtimebe/domain/member/aop/GetPointAspect.java index 99071d9..a7f9a0a 100644 --- a/src/main/java/org/withtime/be/withtimebe/domain/member/aop/GetPointAspect.java +++ b/src/main/java/org/withtime/be/withtimebe/domain/member/aop/GetPointAspect.java @@ -3,6 +3,8 @@ import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; @@ -13,10 +15,13 @@ import org.withtime.be.withtimebe.global.security.domain.CustomUserDetails; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Aspect @Component @RequiredArgsConstructor +@Order(Ordered.LOWEST_PRECEDENCE - 1) // 읽기 전용 트랜잭션보다 먼저 실행되도록 하여 포인트 변경 감지되도록 public class GetPointAspect { private final MemberCommandService memberCommandService; @@ -31,6 +36,7 @@ public void addPoint(GetPoint getPoint) { // 인증 객체 추출 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if(authentication == null || !authentication.isAuthenticated()) { + log.info("[GetPointAspect] 유저의 인증 정보가 존재하지 않아 포인트가 적립되지 않았습니다."); return; } @@ -39,6 +45,7 @@ public void addPoint(GetPoint getPoint) { if(userDetails instanceof CustomUserDetails customUserDetails) { Long memberId = customUserDetails.getMember().getId(); memberCommandService.addPoint(memberId, point); + log.info("[GetPointAspect] {} 님의 포인트 적립에 성공하였습니다.", customUserDetails.getMember().getUsername()); } } }