From 61bf70ff141c4b70aaa04593b60dccb50cda1f40 Mon Sep 17 00:00:00 2001 From: Sihun23 Date: Sat, 25 Jan 2025 02:50:42 +0900 Subject: [PATCH 01/16] =?UTF-8?q?CLAP-81=20Feat:=EA=B0=90=EC=82=AC?= =?UTF-8?q?=EB=A1=9C=EA=B9=85=20API=20=EA=B5=AC=ED=98=84(refactoring=20?= =?UTF-8?q?=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../inbound/web/admin/LogController.java | 41 +++++ .../web/dto/admin/AnonymousLogResponse.java | 28 ++++ .../web/dto/admin/MemberLogResponse.java | 26 +++ .../persistense/ApiLogPersistenceAdapter.java | 158 ++++++++++++++++++ .../entity/log/AnonymousLogEntity.java | 13 +- .../persistense/entity/log/ApiLogEntity.java | 28 +++- .../entity/log/MemberLogEntity.java | 15 +- .../entity/log/constant/ApiHttpMethod.java | 3 +- .../entity/member/MemberEntity.java | 3 + .../log/AnonymousLogRepository.java | 6 +- .../repository/log/MemberLogRepository.java | 6 +- .../application/RetrieveApiLogsUsecase.java | 55 ++++++ .../mapper/AnonymousLogMapper.java | 21 +++ .../application/mapper/MemberLogMapper.java | 19 +++ .../inbound/domain/LoginDomainService.java | 13 ++ .../port/inbound/log/GetApiLogsUseCase.java | 14 ++ .../outbound/log/ApiLogRepositoryPort.java | 15 ++ .../server/config/annotation/LogType.java | 12 ++ .../clap/server/config/aop/LoggingAspect.java | 142 ++++++++++++++++ .../clap/server/domain/model/log/ApiLog.java | 32 +++- .../clap/server/exception/ErrorContext.java | 21 +++ .../mapper/RetrieveApiLogsUsecaseTest.java | 149 +++++++++++++++++ 23 files changed, 803 insertions(+), 18 deletions(-) create mode 100644 src/main/java/clap/server/adapter/inbound/web/admin/LogController.java create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java create mode 100644 src/main/java/clap/server/application/RetrieveApiLogsUsecase.java create mode 100644 src/main/java/clap/server/application/mapper/AnonymousLogMapper.java create mode 100644 src/main/java/clap/server/application/mapper/MemberLogMapper.java create mode 100644 src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java create mode 100644 src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java create mode 100644 src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java create mode 100644 src/main/java/clap/server/config/annotation/LogType.java create mode 100644 src/main/java/clap/server/config/aop/LoggingAspect.java create mode 100644 src/main/java/clap/server/exception/ErrorContext.java create mode 100644 src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java diff --git a/build.gradle b/build.gradle index f46f2163..8523b115 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ repositories { } dependencies { + implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' //ElasticSearch diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java new file mode 100644 index 00000000..b6445aa8 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java @@ -0,0 +1,41 @@ +package clap.server.adapter.inbound.web.admin; + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.application.port.inbound.log.GetApiLogsUseCase; +import clap.server.common.annotation.architecture.WebAdapter; +import clap.server.config.annotation.LogType; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@WebAdapter +@RestController +@RequestMapping("/api/management") +@RequiredArgsConstructor +public class LogController { + + private final GetApiLogsUseCase getApiLogsUseCase; + + /** + * 로그인 시도 기록 조회 + */ + @LogType("로그인 로그 조회") + @GetMapping("/login-logs") + public List getLoginAttempts() { + return getApiLogsUseCase.getAnonymousLogs(); + } + + /** + * 모든 API 호출 기록 조회 + */ + @LogType("API 로그 조회") + @GetMapping("/api-logs") + public List getApiCalls(@RequestParam(defaultValue = "") String logType) { + return getApiLogsUseCase.getMemberLogs(); + } +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java new file mode 100644 index 00000000..21e8c9c1 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java @@ -0,0 +1,28 @@ +package clap.server.adapter.inbound.web.dto.admin; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.time.LocalDateTime; + +public record AnonymousLogResponse( + @NotBlank + Long logId, + @NotBlank + String loginNickname, + @NotBlank + LocalDateTime requestAt, + @NotBlank + LocalDateTime responseAt, + @NotBlank + String requestUrl, + @NotBlank + String requestMethod, + @NotBlank + Integer statusCode, + @NotNull + String customStatusCode, + @NotNull + Integer failedAttempts +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java new file mode 100644 index 00000000..a767d3fe --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java @@ -0,0 +1,26 @@ +package clap.server.adapter.inbound.web.dto.admin; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.time.LocalDateTime; + +public record MemberLogResponse( + @NotBlank + Long logId, + @NotBlank + Long memberId, + @NotBlank + LocalDateTime requestAt, + @NotBlank + LocalDateTime responseAt, + @NotBlank + String requestUrl, + @NotBlank + String requestMethod, + @NotBlank + Integer statusCode, + @NotNull + String customStatusCode +) { +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java new file mode 100644 index 00000000..4247c64a --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -0,0 +1,158 @@ +package clap.server.adapter.outbound.persistense; + +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.ApiLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository; +import clap.server.adapter.outbound.persistense.repository.log.ApiLogRepository; +import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository; +import clap.server.application.port.outbound.log.ApiLogRepositoryPort; +import clap.server.domain.model.log.ApiLog; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class ApiLogPersistenceAdapter implements ApiLogRepositoryPort { + + private final AnonymousLogRepository anonymousLogRepository; + private final MemberLogRepository memberLogRepository; + private final ApiLogRepository apiLogRepository; + + @Override + public void save(ApiLog apiLog) { + validateApiLog(apiLog); // 유효성 검증 추가 + + ApiLogEntity entity; + + if ("로그인 시도".equals(apiLog.getLogType())) { + // logType이 '로그인 시도'인 경우 비회원 로그 저장 + entity = createAnonymousLogEntity(apiLog); + } else { + // 회원 로그 저장 + entity = createMemberLogEntity(apiLog); + } + + apiLogRepository.save(entity); + } + + @Override + public List findAllLogs() { + return apiLogRepository.findAll().stream() + .map(this::mapToDomain) // 엔티티를 도메인 객체로 매핑 + .collect(Collectors.toList()); + } + + @Override + public List findAnonymousLogs(String logType) { + return anonymousLogRepository.findByLogType(logType); + } + + @Override + public List findMemberLogs() { + return memberLogRepository.findAll(); + } + + // 유효성 검증 메서드 + private void validateApiLog(ApiLog apiLog) { +// if (apiLog.getLogType() == null || apiLog.getLogType().isBlank()) { +// throw new IllegalArgumentException("Log type must not be null or empty"); +// } +// if (!"로그인 시도".equals(apiLog.getLogType()) && apiLog.getMemberId() == null) { +// throw new IllegalArgumentException("Member ID must not be null for member logs"); +// } + } + + // AnonymousLogEntity 생성 메서드 + private AnonymousLogEntity createAnonymousLogEntity(ApiLog apiLog) { + return AnonymousLogEntity.builder() + .serverIp(apiLog.getServerIp()) + .clientIp(apiLog.getClientIp()) + .requestUrl(apiLog.getRequestUrl()) + .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) // Enum 변환 + .statusCode(apiLog.getStatusCode()) + .customStatusCode(apiLog.getCustomStatusCode()) + .request(apiLog.getRequest()) + .response(apiLog.getResponse()) + .requestAt(apiLog.getRequestAt()) + .responseAt(apiLog.getResponseAt()) + .loginNickname(apiLog.getMemberId() != null ? apiLog.getMemberId() : "UNKNOWN") // 닉네임 저장 + .logType(apiLog.getLogType()) + .build(); + } + + // MemberLogEntity 생성 메서드 + private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { + Long memberId = parseMemberId(apiLog.getMemberId()); + MemberEntity member = MemberEntity.builder() + .memberId(memberId) + .build(); + + return MemberLogEntity.builder() + .member(member) + .serverIp(apiLog.getServerIp()) + .clientIp(apiLog.getClientIp()) + .requestUrl(apiLog.getRequestUrl()) + .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) // Enum 변환 + .statusCode(apiLog.getStatusCode()) + .customStatusCode(apiLog.getCustomStatusCode()) + .request(apiLog.getRequest()) + .response(apiLog.getResponse()) + .requestAt(apiLog.getRequestAt()) + .responseAt(apiLog.getResponseAt()) + .logType(apiLog.getLogType()) + .build(); + } + + // memberId 파싱 유틸 메서드 + private Long parseMemberId(String memberId) { + try { + return memberId != null ? Long.parseLong(memberId) : 0L; // 기본값 설정 + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid member ID: " + memberId, e); + } + } + + // 엔티티 → 도메인 매핑 메서드 + private ApiLog mapToDomain(ApiLogEntity entity) { + if (entity instanceof MemberLogEntity memberLogEntity) { + return ApiLog.builder() + .logId(memberLogEntity.getLogId()) + .serverIp(memberLogEntity.getServerIp()) + .clientIp(memberLogEntity.getClientIp()) + .requestUrl(memberLogEntity.getRequestUrl()) + .requestMethod(memberLogEntity.getRequestMethod().name()) + .statusCode(memberLogEntity.getStatusCode()) + .customStatusCode(memberLogEntity.getCustomStatusCode()) + .request(memberLogEntity.getRequest()) + .response(memberLogEntity.getResponse()) + .requestAt(memberLogEntity.getRequestAt()) + .responseAt(memberLogEntity.getResponseAt()) + .memberId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID + .logType(memberLogEntity.getLogType()) + .build(); + } else if (entity instanceof AnonymousLogEntity anonymousLogEntity) { + return ApiLog.builder() + .logId(anonymousLogEntity.getLogId()) + .serverIp(anonymousLogEntity.getServerIp()) + .clientIp(anonymousLogEntity.getClientIp()) + .requestUrl(anonymousLogEntity.getRequestUrl()) + .requestMethod(anonymousLogEntity.getRequestMethod().name()) + .statusCode(anonymousLogEntity.getStatusCode()) + .customStatusCode(anonymousLogEntity.getCustomStatusCode()) + .request(anonymousLogEntity.getRequest()) + .response(anonymousLogEntity.getResponse()) + .requestAt(anonymousLogEntity.getRequestAt()) + .responseAt(anonymousLogEntity.getResponseAt()) + .memberId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID + .logType(anonymousLogEntity.getLogType()) + .build(); + } + throw new IllegalStateException("Unknown log entity type"); + } +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java index 005fd4dc..15dbc3f4 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java @@ -1,5 +1,6 @@ package clap.server.adapter.outbound.persistense.entity.log; +import clap.server.domain.model.log.ApiLog; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; @@ -13,7 +14,15 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @DiscriminatorValue("ANONYMOUS") @SuperBuilder -public class AnonymousLogEntity extends ApiLogEntity{ - @Column +public class AnonymousLogEntity extends ApiLogEntity { + + @Column(nullable = false) private String loginNickname; + + @Override + public ApiLog toDomain() { + return toCommonDomainBuilder() + .memberId(loginNickname) + .build(); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index ab8c7d0e..31e24d0f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -2,6 +2,7 @@ import clap.server.adapter.outbound.persistense.entity.common.BaseTimeEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.domain.model.log.ApiLog; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -17,7 +18,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DTYPE") -public class ApiLogEntity extends BaseTimeEntity { +public abstract class ApiLogEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long logId; @@ -53,4 +54,27 @@ public class ApiLogEntity extends BaseTimeEntity { @Column(nullable = false) private LocalDateTime responseAt; -} \ No newline at end of file + @Column(nullable = false) + private String logType; + + @Version + private Long version; // 낙관적 락 관리를 위한 버전 + + protected ApiLog.ApiLogBuilder toCommonDomainBuilder() { + return ApiLog.builder() + .logId(logId) + .serverIp(serverIp) + .clientIp(clientIp) + .requestUrl(requestUrl) + .requestMethod(requestMethod.name()) + .statusCode(statusCode) + .customStatusCode(customStatusCode) + .request(request) + .response(response) + .requestAt(requestAt) + .responseAt(responseAt) + .logType(logType); + } + + public abstract ApiLog toDomain(); +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java index 0a761ceb..264a6214 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java @@ -1,6 +1,7 @@ package clap.server.adapter.outbound.persistense.entity.log; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.domain.model.log.ApiLog; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -12,8 +13,16 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @DiscriminatorValue("MEMBER") @SuperBuilder -public class MemberLogEntity extends ApiLogEntity{ - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") +public class MemberLogEntity extends ApiLogEntity { + + @ManyToOne(/*optional = false, */fetch = FetchType.LAZY) + @JoinColumn(name = "member_id")//, nullable = false) private MemberEntity member; + + @Override + public ApiLog toDomain() { + return toCommonDomainBuilder() + .memberId(member != null ? String.valueOf(member.getMemberId()) : null) // memberId 포함 + .build(); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/ApiHttpMethod.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/ApiHttpMethod.java index f9014fa1..71233ab2 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/ApiHttpMethod.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/ApiHttpMethod.java @@ -10,5 +10,6 @@ public enum ApiHttpMethod { GET, PATCH, PUT, - DELETE; + DELETE, + UNKNOWN; // Logging } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java index ba9862ad..eb914f17 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java @@ -58,4 +58,7 @@ public class MemberEntity extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "admin_id") private MemberEntity admin; + + @Version + private Long version; // 낙관적 락을 위한 버전 관리 } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java index 6b373f3f..c59ac6a9 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java @@ -4,6 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface AnonymousLogRepository extends JpaRepository { -} \ No newline at end of file + // 비회원 로그 + List findByLogType(String logType); +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java index e6d462f4..8ccaf8cd 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java @@ -4,6 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface MemberLogRepository extends JpaRepository { -} \ No newline at end of file + // 회원 + List findAll(); +} diff --git a/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java b/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java new file mode 100644 index 00000000..d8fcfd9a --- /dev/null +++ b/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java @@ -0,0 +1,55 @@ +package clap.server.application; + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.application.mapper.AnonymousLogMapper; +import clap.server.application.mapper.MemberLogMapper; +import clap.server.application.port.inbound.domain.LoginDomainService; +import clap.server.application.port.inbound.log.GetApiLogsUseCase; +import clap.server.application.port.outbound.log.ApiLogRepositoryPort; +import clap.server.domain.model.log.ApiLog; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class RetrieveApiLogsUsecase implements GetApiLogsUseCase { + + private final ApiLogRepositoryPort apiLogRepositoryPort; + private final LoginDomainService loginDomainService; + + @Override + public List getAnonymousLogs() { + // 비회원 로그에서 '로그인 시도' 로그를 조회하여 DTO로 변환 + return apiLogRepositoryPort.findAnonymousLogs("로그인 시도").stream() + .map(entity -> { + ApiLog log = entity.toDomain(); // 엔티티를 도메인 객체로 변환 + int failedAttempts = loginDomainService.getFailedAttemptCount(log.getMemberId()); + return AnonymousLogMapper.toDto(log, failedAttempts); + }) + .toList(); + } + + @Override + public List getMemberLogs() { + // 회원 로그를 조회하여 DTO로 변환 + return apiLogRepositoryPort.findMemberLogs().stream() + .map(entity -> { + ApiLog log = entity.toDomain(); // 엔티티를 도메인 객체로 변환 + return MemberLogMapper.toDto(log); + }) + .toList(); + } + + @Override + public List getApiLogs() { + // 모든 로그 조회 + return apiLogRepositoryPort.findAllLogs(); + } + + public void save(ApiLog apiLog) { + apiLogRepositoryPort.save(apiLog); + } +} diff --git a/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java b/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java new file mode 100644 index 00000000..deb656e9 --- /dev/null +++ b/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java @@ -0,0 +1,21 @@ +package clap.server.application.mapper; + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.domain.model.log.ApiLog; + +public class AnonymousLogMapper { + + public static AnonymousLogResponse toDto(ApiLog log, Integer failedAttempts) { + return new AnonymousLogResponse( + log.getLogId(), + log.getMemberId(), // 비회원 닉네임 + log.getRequestAt(), + log.getResponseAt(), + log.getRequestUrl(), + log.getRequestMethod(), + log.getStatusCode(), + log.getCustomStatusCode(), + failedAttempts != null ? failedAttempts : 0 // 실패 시도 수 기본값 + ); + } +} diff --git a/src/main/java/clap/server/application/mapper/MemberLogMapper.java b/src/main/java/clap/server/application/mapper/MemberLogMapper.java new file mode 100644 index 00000000..0cfc0e29 --- /dev/null +++ b/src/main/java/clap/server/application/mapper/MemberLogMapper.java @@ -0,0 +1,19 @@ +package clap.server.application.mapper; + +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.domain.model.log.ApiLog; + +public class MemberLogMapper { + public static MemberLogResponse toDto(ApiLog log) { + return new MemberLogResponse( + log.getLogId(), + Long.valueOf(log.getMemberId()), // memberId는 항상 값을 가져야 함 + log.getRequestAt(), + log.getResponseAt(), + log.getRequestUrl(), + log.getRequestMethod(), + log.getStatusCode(), + log.getCustomStatusCode() + ); + } +} diff --git a/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java b/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java new file mode 100644 index 00000000..02293e21 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java @@ -0,0 +1,13 @@ +package clap.server.application.port.inbound.domain; + +import org.springframework.stereotype.Service; + +// TODO: 로그인 도메인 실패횟수 예제 코드 (나중에 삭제할 예정) +@Service +public class LoginDomainService { + + public Integer getFailedAttemptCount(String loginNickname) { + // 로그인 실패 횟수 계산 로직 추가 + return 3; // 테스트값 + } +} diff --git a/src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java b/src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java new file mode 100644 index 00000000..7611deec --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java @@ -0,0 +1,14 @@ +package clap.server.application.port.inbound.log; + + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.domain.model.log.ApiLog; + +import java.util.List; + +public interface GetApiLogsUseCase { + List getAnonymousLogs(); + List getMemberLogs(); + List getApiLogs(); +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java b/src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java new file mode 100644 index 00000000..5943c69e --- /dev/null +++ b/src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java @@ -0,0 +1,15 @@ +package clap.server.application.port.outbound.log; + +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.domain.model.log.ApiLog; + +import java.util.List; + +public interface ApiLogRepositoryPort { + void save(ApiLog apiLog); + List findAllLogs(); + + List findAnonymousLogs(String logType); + List findMemberLogs(); +} diff --git a/src/main/java/clap/server/config/annotation/LogType.java b/src/main/java/clap/server/config/annotation/LogType.java new file mode 100644 index 00000000..425b4da0 --- /dev/null +++ b/src/main/java/clap/server/config/annotation/LogType.java @@ -0,0 +1,12 @@ +package clap.server.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) // 런타임 시 어노테이션 접근 가능 +@Target(ElementType.METHOD) // 메서드 +public @interface LogType { + String value(); +} diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java new file mode 100644 index 00000000..2d341906 --- /dev/null +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -0,0 +1,142 @@ +package clap.server.config.aop; + +import clap.server.application.port.outbound.log.ApiLogRepositoryPort; +import clap.server.config.annotation.LogType; +import clap.server.domain.model.log.ApiLog; +import clap.server.exception.ErrorContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +//import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; + +@Slf4j +@Aspect +@Component +@RequiredArgsConstructor +public class LoggingAspect { + + private final ApiLogRepositoryPort apiLogRepositoryPort; + private final ObjectMapper objectMapper; + + @Pointcut("execution(* clap.server.adapter.inbound.web..*Controller.*(..)) || execution(* clap.server.adapter.inbound.web..*Controller.*(..))") // TODO : 컨트롤러만 있는 패키지로 수정 예정 + public void controllerMethods() { + } + + @Around("controllerMethods()") + public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = wrapRequest(attributes.getRequest()); + HttpServletResponse response = attributes.getResponse(); + LocalDateTime requestAt = LocalDateTime.now(); + + Object result = null; + try { + result = joinPoint.proceed(); + } finally { + LocalDateTime responseAt = LocalDateTime.now(); + + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + String logType = extractLogType(methodSignature); + + //TODO: security filter 구현 후 주석 해제 +// boolean isAuthenticated = isUserAuthenticated(); + + String customCode = getCustomCode(response); + + // 로그 저장 로직 + ApiLog.ApiLogBuilder logBuilder = ApiLog.builder() + .serverIp("127.0.0.1") + .clientIp(request.getRemoteAddr()) + .requestUrl(request.getRequestURI()) + .requestMethod(request.getMethod()) + .statusCode(response != null ? response.getStatus() : 500) + .customStatusCode(customCode) + .request(extractRequestBody(request)) + .response(result != null ? result.toString() : "UNKNOWN") + .requestAt(requestAt) + .responseAt(responseAt) + .logType(logType); + + //TODO: security filter 구현 후 주석 해제 + if ("로그인 시도".equals(logType)) { + String nickname = extractNicknameFromRequestBody(request); + logBuilder.memberId(nickname); // 비회원 로그 +// } else if (isAuthenticated) { +// Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); +// if (principal instanceof clap.server.adapter.outbound.persistense.entity.member.MemberEntity member) { +// logBuilder.memberId(String.valueOf(member.getMemberId())); // 회원 로그 +// } + } + + apiLogRepositoryPort.save(logBuilder.build()); + } + return result; + } + + private HttpServletRequest wrapRequest(HttpServletRequest request) { + // ContentCachingRequestWrapper로 래핑 + if (request instanceof ContentCachingRequestWrapper) { + return request; + } + return new ContentCachingRequestWrapper(request); + } + + private String extractLogType(MethodSignature methodSignature) { + // LogType 어노테이션에서 로그 타입 추출 + if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { + return methodSignature.getMethod().getAnnotation(LogType.class).value(); + } + return "일반 요청"; // 기본값 + } + + private String getCustomCode(HttpServletResponse response) { + // ErrorContext에서 커스텀 코드 추출 + String customCode = ErrorContext.getCustomCode(); + return customCode != null ? customCode : "CUSTOM" + (response != null ? response.getStatus() : 500); + } + + private String extractNicknameFromRequestBody(HttpServletRequest request) { + try { + // 요청 본문에서 nickname 필드 추출 + String requestBody = extractRequestBody(request); + JsonNode jsonNode = objectMapper.readTree(requestBody); + return jsonNode.has("nickname") ? jsonNode.get("nickname").asText() : null; + } catch (Exception e) { + return null; + } + } + + private String extractRequestBody(HttpServletRequest request) { + try { + // ContentCachingRequestWrapper를 통해 요청 본문 읽기 + ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request; + byte[] content = cachingRequest.getContentAsByteArray(); + return new String(content, StandardCharsets.UTF_8); + } catch (Exception e) { + return "ERROR: Unable to read request body"; + } + } + + //TODO: security filter 구현 후 주석 해제 +// private boolean isUserAuthenticated() { +// // 사용자 인증 상태 확인 +// var authentication = SecurityContextHolder.getContext().getAuthentication(); +// return authentication != null && authentication.isAuthenticated() +// && !"anonymousUser".equals(authentication.getPrincipal()); +// } +} diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 045aab1a..f3e4c842 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -1,6 +1,5 @@ package clap.server.domain.model.log; -import clap.server.domain.model.common.BaseTime; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,17 +10,34 @@ @Getter @SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ApiLog extends BaseTime { +public class ApiLog { private Long logId; private Integer statusCode; - private Long memberId; + private String memberId; private LocalDateTime requestAt; private LocalDateTime responseAt; private String dtype; - private String request; - private String requestUrl; - private String response; - private String clientIp; + private String request; + private String requestUrl; + private String response; + private String clientIp; private String customStatusCode; private String serverIp; - } + private String requestMethod; + private String logType; + + // '로그인 시도'인지 확인 + public boolean isLoginAttempt() { + return "로그인 시도".equals(this.logType); + } + + // 익명 사용자 여부 확인 + public boolean isAnonymousUser() { + return this.memberId == null || this.memberId.isBlank(); + } + + // 비즈니스 로직: 회원 로그 여부 확인 + public boolean isMemberLog() { + return !isAnonymousUser() && !"로그인 시도".equals(this.logType); + } +} diff --git a/src/main/java/clap/server/exception/ErrorContext.java b/src/main/java/clap/server/exception/ErrorContext.java new file mode 100644 index 00000000..bc9221b3 --- /dev/null +++ b/src/main/java/clap/server/exception/ErrorContext.java @@ -0,0 +1,21 @@ +package clap.server.exception; + +public class ErrorContext { + + private static final ThreadLocal customCodeHolder = new ThreadLocal<>(); + + // customCode 설정 + public static void setCustomCode(String customCode) { + customCodeHolder.set(customCode); + } + + // customCode 가져오기 + public static String getCustomCode() { + return customCodeHolder.get(); + } + + // ThreadLocal 초기화 + public static void clear() { + customCodeHolder.remove(); + } +} diff --git a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java new file mode 100644 index 00000000..96f03190 --- /dev/null +++ b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java @@ -0,0 +1,149 @@ +package clap.server.application.mapper; + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.application.RetrieveApiLogsUsecase; +import clap.server.application.port.inbound.domain.LoginDomainService; +import clap.server.application.port.outbound.log.ApiLogRepositoryPort; +import clap.server.domain.model.log.ApiLog; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +class RetrieveApiLogsUsecaseTest { + + private final ApiLogRepositoryPort apiLogRepositoryPort = mock(ApiLogRepositoryPort.class); + private final LoginDomainService loginDomainService = mock(LoginDomainService.class); + private final RetrieveApiLogsUsecase usecase = new RetrieveApiLogsUsecase(apiLogRepositoryPort, loginDomainService); + + @Test + void getMemberLogs_ShouldReturnConvertedResponses() { + // MemberEntity 생성 + MemberEntity mockMemberEntity = MemberEntity.builder() + .memberId(5L) // 정확한 memberId 설정 + .build(); + + // MemberLogEntity 생성 + MemberLogEntity mockEntity = MemberLogEntity.builder() + .logId(2L) + .member(mockMemberEntity) // member 필드에 mockMemberEntity 설정 + .requestAt(LocalDateTime.now()) + .responseAt(LocalDateTime.now()) + .requestUrl("/api/data") + .requestMethod(ApiHttpMethod.GET) + .statusCode(200) + .customStatusCode("SUCCESS") + .build(); + + // 디버깅: mockEntity 생성 확인 + System.out.println("DEBUG: Created mockEntity - Member ID: " + mockEntity.getMember().getMemberId()); + + // Mock 리포지토리 설정 + when(apiLogRepositoryPort.findMemberLogs()).thenReturn(List.of(mockEntity)); + + // 테스트 실행 + List responses = usecase.getMemberLogs(); + + // 디버깅: 반환된 responses 확인 + // 결과 검증 + assertThat(responses).hasSize(1); // 리스트 크기 확인 + MemberLogResponse response = responses.get(0); + assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 + assertThat(response.memberId()).isEqualTo(5L); // memberId 검증 + verify(apiLogRepositoryPort, times(1)).findMemberLogs(); // 리포지토리 호출 검증 + } + + @Test + void getAnonymousLogs_ShouldReturnConvertedResponses() { + // AnonymousLogEntity 생성 + AnonymousLogEntity mockEntity = AnonymousLogEntity.builder() + .logId(1L) + .loginNickname("testUser") + .requestAt(LocalDateTime.now()) + .responseAt(LocalDateTime.now()) + .requestUrl("/api/login") + .requestMethod(ApiHttpMethod.POST) + .statusCode(200) + .customStatusCode("OK") + .build(); + + // 디버깅: mockEntity 생성 확인 + System.out.println("DEBUG: Created mockEntity - Login Nickname: " + mockEntity.getLoginNickname()); + + // Mock 서비스 설정 + when(apiLogRepositoryPort.findAnonymousLogs("로그인 시도")).thenReturn(List.of(mockEntity)); + when(loginDomainService.getFailedAttemptCount("testUser")).thenReturn(3); // 실패 시도 수 설정 + + // 테스트 실행 + List responses = usecase.getAnonymousLogs(); + + // 디버깅: 반환된 responses 확인 + System.out.println("DEBUG: Received responses: " + responses); + + // 결과 검증 + assertThat(responses).hasSize(1); // 리스트 크기 확인 + AnonymousLogResponse response = responses.get(0); + assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 + assertThat(response.failedAttempts()).isEqualTo(3); // 실패 시도 검증 + verify(apiLogRepositoryPort, times(1)).findAnonymousLogs("로그인 시도"); + verify(loginDomainService, times(1)).getFailedAttemptCount("testUser"); + } + + @Test + void getAllLogs_ShouldReturnAllLogs() { + // AnonymousLogEntity 생성 + AnonymousLogEntity anonymousLogEntity = AnonymousLogEntity.builder() + .logId(1L) + .loginNickname("testUser") + .requestAt(LocalDateTime.now()) + .responseAt(LocalDateTime.now()) + .requestUrl("/api/login") + .requestMethod(ApiHttpMethod.POST) + .statusCode(200) + .customStatusCode("OK") + .build(); + + // MemberLogEntity 생성 + MemberEntity mockMemberEntity = MemberEntity.builder() + .memberId(5L) + .build(); + + MemberLogEntity memberLogEntity = MemberLogEntity.builder() + .logId(2L) + .member(mockMemberEntity) + .requestAt(LocalDateTime.now()) + .responseAt(LocalDateTime.now()) + .requestUrl("/api/data") + .requestMethod(ApiHttpMethod.GET) + .statusCode(200) + .customStatusCode("SUCCESS") + .build(); + + // 디버깅: mockEntities 생성 확인 + System.out.println("DEBUG: Created anonymousLogEntity - Login Nickname: " + anonymousLogEntity.getLoginNickname()); + System.out.println("DEBUG: Created memberLogEntity - Member ID: " + memberLogEntity.getMember().getMemberId()); + + // Mock 리포지토리 설정 + when(apiLogRepositoryPort.findAllLogs()).thenReturn(List.of(anonymousLogEntity.toDomain(), memberLogEntity.toDomain())); + + // 테스트 실행 + List logs = usecase.getApiLogs(); + + // 디버깅: 반환된 logs 확인 + System.out.println("DEBUG: Received logs: " + logs); + + // 결과 검증 + assertThat(logs).hasSize(2); + assertThat(logs.get(0).getLogId()).isEqualTo(1L); // AnonymousLogEntity 검증 + assertThat(logs.get(1).getLogId()).isEqualTo(2L); // MemberLogEntity 검증 + verify(apiLogRepositoryPort, times(1)).findAllLogs(); + } +} From be2cb18fa8ac3a5b5b4e7f62c0236e7e4460a124 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Thu, 30 Jan 2025 21:01:08 +0900 Subject: [PATCH 02/16] =?UTF-8?q?CLAP-81=20Fix:=20=EC=9D=BC=EB=B0=98=20API?= =?UTF-8?q?=20=ED=98=B8=EC=B6=9C=20=EC=8B=9C=20DB=EC=97=90=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EB=A1=9C=EA=B7=B8=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/admin/LogController.java | 4 +- .../web/task/FindTaskHistoryController.java | 2 + .../persistense/ApiLogPersistenceAdapter.java | 13 ++++- .../persistense/entity/log/ApiLogEntity.java | 4 +- .../clap/server/config/aop/LoggingAspect.java | 55 ++++++++++--------- .../clap/server/domain/model/log/ApiLog.java | 7 +-- 6 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java index b6445aa8..9ccd2d4b 100644 --- a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java @@ -24,7 +24,7 @@ public class LogController { /** * 로그인 시도 기록 조회 */ - @LogType("로그인 로그 조회") + @LogType("로그인 로그") @GetMapping("/login-logs") public List getLoginAttempts() { return getApiLogsUseCase.getAnonymousLogs(); @@ -33,7 +33,7 @@ public List getLoginAttempts() { /** * 모든 API 호출 기록 조회 */ - @LogType("API 로그 조회") + @LogType("일반 로그") @GetMapping("/api-logs") public List getApiCalls(@RequestParam(defaultValue = "") String logType) { return getApiLogsUseCase.getMemberLogs(); diff --git a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java index d4b71d92..5dfc22be 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java @@ -5,6 +5,7 @@ import clap.server.adapter.inbound.web.dto.task.response.FindTaskHistoryResponse; import clap.server.application.port.inbound.task.FindTaskHistoriesUsecase; import clap.server.common.annotation.architecture.WebAdapter; +import clap.server.config.annotation.LogType; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -26,6 +27,7 @@ public class FindTaskHistoryController { private final FindTaskHistoriesUsecase findTaskHistoriesUsecase; + @LogType("일반 로그") @Operation(summary = "작업 히스토리 조회") @Secured({"ROLE_MANAGER","ROLE_USER"}) @GetMapping("/{taskId}/histories") diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index 4247c64a..b32ace12 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -10,19 +10,23 @@ import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository; import clap.server.application.port.outbound.log.ApiLogRepositoryPort; import clap.server.domain.model.log.ApiLog; +import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Component @RequiredArgsConstructor +@Transactional public class ApiLogPersistenceAdapter implements ApiLogRepositoryPort { private final AnonymousLogRepository anonymousLogRepository; private final MemberLogRepository memberLogRepository; private final ApiLogRepository apiLogRepository; + private final EntityManager entityManager; @Override public void save(ApiLog apiLog) { @@ -30,8 +34,8 @@ public void save(ApiLog apiLog) { ApiLogEntity entity; - if ("로그인 시도".equals(apiLog.getLogType())) { - // logType이 '로그인 시도'인 경우 비회원 로그 저장 + if ("로그인 로그".equals(apiLog.getLogType())) { + // logType이 '로그인 로그'인 경우 비회원 로그 저장 entity = createAnonymousLogEntity(apiLog); } else { // 회원 로그 저장 @@ -92,9 +96,12 @@ private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { MemberEntity member = MemberEntity.builder() .memberId(memberId) .build(); + //TODO: member 가져오도록 수정 -> 영속화 + // 이미 존재하는 memberId로 MemberEntity를 조회 + MemberEntity newMember = entityManager.find(MemberEntity.class, memberId); return MemberLogEntity.builder() - .member(member) + .member(newMember) .serverIp(apiLog.getServerIp()) .clientIp(apiLog.getClientIp()) .requestUrl(apiLog.getRequestUrl()) diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index 31e24d0f..e18eb09a 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -17,7 +17,7 @@ @SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @Inheritance(strategy = InheritanceType.SINGLE_TABLE) -@DiscriminatorColumn(name = "DTYPE") +@DiscriminatorColumn public abstract class ApiLogEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -55,7 +55,7 @@ public abstract class ApiLogEntity extends BaseTimeEntity { private LocalDateTime responseAt; @Column(nullable = false) - private String logType; + private String logType; //TODO: enum으로 수정 @Version private Long version; // 낙관적 락 관리를 위한 버전 diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index 2d341906..beacd667 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -1,11 +1,14 @@ package clap.server.config.aop; +import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; import clap.server.application.port.outbound.log.ApiLogRepositoryPort; import clap.server.config.annotation.LogType; import clap.server.domain.model.log.ApiLog; import clap.server.exception.ErrorContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.EntityManager; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -15,8 +18,9 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -//import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -33,7 +37,7 @@ public class LoggingAspect { private final ApiLogRepositoryPort apiLogRepositoryPort; private final ObjectMapper objectMapper; - @Pointcut("execution(* clap.server.adapter.inbound.web..*Controller.*(..)) || execution(* clap.server.adapter.inbound.web..*Controller.*(..))") // TODO : 컨트롤러만 있는 패키지로 수정 예정 + @Pointcut("execution(* clap.server.adapter.inbound.web..*Controller.*(..))") public void controllerMethods() { } @@ -52,14 +56,11 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String logType = extractLogType(methodSignature); - - //TODO: security filter 구현 후 주석 해제 -// boolean isAuthenticated = isUserAuthenticated(); - String customCode = getCustomCode(response); + log.info("logType={}", logType); // 로그 저장 로직 - ApiLog.ApiLogBuilder logBuilder = ApiLog.builder() + ApiLog apiLog = ApiLog.builder() .serverIp("127.0.0.1") .clientIp(request.getRemoteAddr()) .requestUrl(request.getRequestURI()) @@ -70,20 +71,24 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { .response(result != null ? result.toString() : "UNKNOWN") .requestAt(requestAt) .responseAt(responseAt) - .logType(logType); + .logType(logType).build(); //TODO: security filter 구현 후 주석 해제 - if ("로그인 시도".equals(logType)) { + if ("로그인 로그".equals(logType)) { String nickname = extractNicknameFromRequestBody(request); - logBuilder.memberId(nickname); // 비회원 로그 -// } else if (isAuthenticated) { -// Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); -// if (principal instanceof clap.server.adapter.outbound.persistense.entity.member.MemberEntity member) { -// logBuilder.memberId(String.valueOf(member.getMemberId())); // 회원 로그 -// } + apiLog = apiLog.toBuilder().memberId(nickname).build(); + log.info("로그인 로그={}", logType); + } else if (isUserAuthenticated()) { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + log.info("일반 로그={}", logType); + log.info("principal={}", principal); + if (principal instanceof SecurityUserDetails userDetails) { + // userDetails에서 userId를 추출하여 apiLog에 memberId로 설정 + log.info("useDetails={}", userDetails.getUserId()); + apiLog = apiLog.toBuilder().memberId(String.valueOf(userDetails.getUserId())).build(); + } } - - apiLogRepositoryPort.save(logBuilder.build()); + apiLogRepositoryPort.save(apiLog); } return result; } @@ -97,11 +102,11 @@ private HttpServletRequest wrapRequest(HttpServletRequest request) { } private String extractLogType(MethodSignature methodSignature) { - // LogType 어노테이션에서 로그 타입 추출 + // 일반 로그 if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { return methodSignature.getMethod().getAnnotation(LogType.class).value(); } - return "일반 요청"; // 기본값 + return "로그인 로그"; // 기본값 } private String getCustomCode(HttpServletResponse response) { @@ -133,10 +138,10 @@ private String extractRequestBody(HttpServletRequest request) { } //TODO: security filter 구현 후 주석 해제 -// private boolean isUserAuthenticated() { -// // 사용자 인증 상태 확인 -// var authentication = SecurityContextHolder.getContext().getAuthentication(); -// return authentication != null && authentication.isAuthenticated() -// && !"anonymousUser".equals(authentication.getPrincipal()); -// } + private boolean isUserAuthenticated() { + // 사용자 인증 상태 확인 + var authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null && authentication.isAuthenticated() + && !"anonymousUser".equals(authentication.getPrincipal()); + } } diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index f3e4c842..6f9da523 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -1,14 +1,13 @@ package clap.server.domain.model.log; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.SuperBuilder; import java.time.LocalDateTime; @Getter -@SuperBuilder +@SuperBuilder(toBuilder = true) +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ApiLog { private Long logId; From 2874dcd14b2c8b54865a7bf863e3151f47367227 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Thu, 30 Jan 2025 22:34:31 +0900 Subject: [PATCH 03/16] =?UTF-8?q?CLAP-81=20Fix:=20LogAspect=20logApiReques?= =?UTF-8?q?ts=EC=97=90=EC=84=9C=20saveApiLog=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/admin/LogController.java | 6 -- .../persistense/ApiLogPersistenceAdapter.java | 53 +++++------ .../entity/log/AnonymousLogEntity.java | 2 +- .../persistense/entity/log/ApiLogEntity.java | 4 +- .../entity/log/MemberLogEntity.java | 6 +- .../entity/log/constant/LogTypeEnum.java | 22 +++++ .../application/RetrieveApiLogsUsecase.java | 1 - .../port/outbound/log/CommandLogPort.java | 7 ++ ...ogRepositoryPort.java => LoadLogPort.java} | 4 +- .../server/config/annotation/LogType.java | 4 +- .../clap/server/config/aop/LoggingAspect.java | 95 +++++++++---------- .../clap/server/domain/model/log/ApiLog.java | 10 +- .../mapper/RetrieveApiLogsUsecaseTest.java | 1 - 13 files changed, 111 insertions(+), 104 deletions(-) create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java create mode 100644 src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java rename src/main/java/clap/server/application/port/outbound/log/{ApiLogRepositoryPort.java => LoadLogPort.java} (85%) diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java index 9ccd2d4b..f3bf084e 100644 --- a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java @@ -21,18 +21,12 @@ public class LogController { private final GetApiLogsUseCase getApiLogsUseCase; - /** - * 로그인 시도 기록 조회 - */ @LogType("로그인 로그") @GetMapping("/login-logs") public List getLoginAttempts() { return getApiLogsUseCase.getAnonymousLogs(); } - /** - * 모든 API 호출 기록 조회 - */ @LogType("일반 로그") @GetMapping("/api-logs") public List getApiCalls(@RequestParam(defaultValue = "") String logType) { diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index b32ace12..a103ad31 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -4,11 +4,13 @@ import clap.server.adapter.outbound.persistense.entity.log.ApiLogEntity; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository; import clap.server.adapter.outbound.persistense.repository.log.ApiLogRepository; import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository; -import clap.server.application.port.outbound.log.ApiLogRepositoryPort; +import clap.server.application.port.outbound.log.CommandLogPort; +import clap.server.application.port.outbound.log.LoadLogPort; import clap.server.domain.model.log.ApiLog; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; @@ -20,8 +22,8 @@ @Component @RequiredArgsConstructor -@Transactional -public class ApiLogPersistenceAdapter implements ApiLogRepositoryPort { +@Transactional(readOnly = true) +public class ApiLogPersistenceAdapter implements CommandLogPort, LoadLogPort { private final AnonymousLogRepository anonymousLogRepository; private final MemberLogRepository memberLogRepository; @@ -29,19 +31,15 @@ public class ApiLogPersistenceAdapter implements ApiLogRepositoryPort { private final EntityManager entityManager; @Override + @Transactional public void save(ApiLog apiLog) { - validateApiLog(apiLog); // 유효성 검증 추가 - + validateApiLog(apiLog); ApiLogEntity entity; - - if ("로그인 로그".equals(apiLog.getLogType())) { - // logType이 '로그인 로그'인 경우 비회원 로그 저장 + if (LogTypeEnum.LOGIN.equals(apiLog.getLogType())) { entity = createAnonymousLogEntity(apiLog); } else { - // 회원 로그 저장 entity = createMemberLogEntity(apiLog); } - apiLogRepository.save(entity); } @@ -62,40 +60,37 @@ public List findMemberLogs() { return memberLogRepository.findAll(); } - // 유효성 검증 메서드 + private void validateApiLog(ApiLog apiLog) { -// if (apiLog.getLogType() == null || apiLog.getLogType().isBlank()) { -// throw new IllegalArgumentException("Log type must not be null or empty"); -// } -// if (!"로그인 시도".equals(apiLog.getLogType()) && apiLog.getMemberId() == null) { -// throw new IllegalArgumentException("Member ID must not be null for member logs"); -// } + if (apiLog.getLogType() == null) { + throw new IllegalArgumentException("Log type must not be null or empty"); + } + if (!LogTypeEnum.LOGIN.equals(apiLog.getLogType()) && apiLog.getUserId() == null) { + throw new IllegalArgumentException("Member ID must not be null for member logs"); + } } - // AnonymousLogEntity 생성 메서드 + private AnonymousLogEntity createAnonymousLogEntity(ApiLog apiLog) { return AnonymousLogEntity.builder() .serverIp(apiLog.getServerIp()) .clientIp(apiLog.getClientIp()) .requestUrl(apiLog.getRequestUrl()) - .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) // Enum 변환 + .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) .statusCode(apiLog.getStatusCode()) .customStatusCode(apiLog.getCustomStatusCode()) .request(apiLog.getRequest()) .response(apiLog.getResponse()) .requestAt(apiLog.getRequestAt()) .responseAt(apiLog.getResponseAt()) - .loginNickname(apiLog.getMemberId() != null ? apiLog.getMemberId() : "UNKNOWN") // 닉네임 저장 + .loginNickname(apiLog.getUserId() != null ? apiLog.getUserId() : "UNKNOWN") .logType(apiLog.getLogType()) .build(); } - // MemberLogEntity 생성 메서드 private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { - Long memberId = parseMemberId(apiLog.getMemberId()); - MemberEntity member = MemberEntity.builder() - .memberId(memberId) - .build(); + Long memberId = parseMemberId(apiLog.getUserId()); + //TODO: member 가져오도록 수정 -> 영속화 // 이미 존재하는 memberId로 MemberEntity를 조회 MemberEntity newMember = entityManager.find(MemberEntity.class, memberId); @@ -105,7 +100,7 @@ private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { .serverIp(apiLog.getServerIp()) .clientIp(apiLog.getClientIp()) .requestUrl(apiLog.getRequestUrl()) - .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) // Enum 변환 + .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) .statusCode(apiLog.getStatusCode()) .customStatusCode(apiLog.getCustomStatusCode()) .request(apiLog.getRequest()) @@ -116,7 +111,6 @@ private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { .build(); } - // memberId 파싱 유틸 메서드 private Long parseMemberId(String memberId) { try { return memberId != null ? Long.parseLong(memberId) : 0L; // 기본값 설정 @@ -125,7 +119,6 @@ private Long parseMemberId(String memberId) { } } - // 엔티티 → 도메인 매핑 메서드 private ApiLog mapToDomain(ApiLogEntity entity) { if (entity instanceof MemberLogEntity memberLogEntity) { return ApiLog.builder() @@ -140,7 +133,7 @@ private ApiLog mapToDomain(ApiLogEntity entity) { .response(memberLogEntity.getResponse()) .requestAt(memberLogEntity.getRequestAt()) .responseAt(memberLogEntity.getResponseAt()) - .memberId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID + .userId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID .logType(memberLogEntity.getLogType()) .build(); } else if (entity instanceof AnonymousLogEntity anonymousLogEntity) { @@ -156,7 +149,7 @@ private ApiLog mapToDomain(ApiLogEntity entity) { .response(anonymousLogEntity.getResponse()) .requestAt(anonymousLogEntity.getRequestAt()) .responseAt(anonymousLogEntity.getResponseAt()) - .memberId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID + .userId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID .logType(anonymousLogEntity.getLogType()) .build(); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java index 15dbc3f4..10845486 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java @@ -22,7 +22,7 @@ public class AnonymousLogEntity extends ApiLogEntity { @Override public ApiLog toDomain() { return toCommonDomainBuilder() - .memberId(loginNickname) + .userId(loginNickname) .build(); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index e18eb09a..bb80d53f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -2,6 +2,7 @@ import clap.server.adapter.outbound.persistense.entity.common.BaseTimeEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; import clap.server.domain.model.log.ApiLog; import jakarta.persistence.*; import lombok.AccessLevel; @@ -55,7 +56,8 @@ public abstract class ApiLogEntity extends BaseTimeEntity { private LocalDateTime responseAt; @Column(nullable = false) - private String logType; //TODO: enum으로 수정 + @Enumerated(EnumType.STRING) + private LogTypeEnum logType; @Version private Long version; // 낙관적 락 관리를 위한 버전 diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java index 264a6214..5a505ff6 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java @@ -15,14 +15,14 @@ @SuperBuilder public class MemberLogEntity extends ApiLogEntity { - @ManyToOne(/*optional = false, */fetch = FetchType.LAZY) - @JoinColumn(name = "member_id")//, nullable = false) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") private MemberEntity member; @Override public ApiLog toDomain() { return toCommonDomainBuilder() - .memberId(member != null ? String.valueOf(member.getMemberId()) : null) // memberId 포함 + .userId(member != null ? String.valueOf(member.getMemberId()) : null) .build(); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java new file mode 100644 index 00000000..7ac80d44 --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java @@ -0,0 +1,22 @@ +package clap.server.adapter.outbound.persistense.entity.log.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum LogTypeEnum { + LOGIN("로그인 로그"), + GENERAL("일반 로그"); + + private final String description; + + public static LogTypeEnum fromDescription(String description) { + for (LogTypeEnum logType : LogTypeEnum.values()) { + if (logType.getDescription().equals(description)) { + return logType; + } + } + throw new IllegalArgumentException("해당하는 로그 타입이 없습니다: " + description); + } +} diff --git a/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java b/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java index d8fcfd9a..6e58afe0 100644 --- a/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java +++ b/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java @@ -6,7 +6,6 @@ import clap.server.application.mapper.MemberLogMapper; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.application.port.inbound.log.GetApiLogsUseCase; -import clap.server.application.port.outbound.log.ApiLogRepositoryPort; import clap.server.domain.model.log.ApiLog; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java b/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java new file mode 100644 index 00000000..b5d8dc45 --- /dev/null +++ b/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java @@ -0,0 +1,7 @@ +package clap.server.application.port.outbound.log; + +import clap.server.domain.model.log.ApiLog; + +public interface CommandLogPort { + void save(ApiLog apiLog); +} diff --git a/src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java similarity index 85% rename from src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java rename to src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java index 5943c69e..fbc114b4 100644 --- a/src/main/java/clap/server/application/port/outbound/log/ApiLogRepositoryPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java @@ -6,10 +6,8 @@ import java.util.List; -public interface ApiLogRepositoryPort { - void save(ApiLog apiLog); +public interface LoadLogPort { List findAllLogs(); - List findAnonymousLogs(String logType); List findMemberLogs(); } diff --git a/src/main/java/clap/server/config/annotation/LogType.java b/src/main/java/clap/server/config/annotation/LogType.java index 425b4da0..e8683bd9 100644 --- a/src/main/java/clap/server/config/annotation/LogType.java +++ b/src/main/java/clap/server/config/annotation/LogType.java @@ -5,8 +5,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Retention(RetentionPolicy.RUNTIME) // 런타임 시 어노테이션 접근 가능 -@Target(ElementType.METHOD) // 메서드 +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) public @interface LogType { String value(); } diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index beacd667..f1b477d8 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -1,14 +1,14 @@ package clap.server.config.aop; import clap.server.adapter.inbound.security.SecurityUserDetails; -import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; -import clap.server.application.port.outbound.log.ApiLogRepositoryPort; + +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.application.port.outbound.log.CommandLogPort; import clap.server.config.annotation.LogType; import clap.server.domain.model.log.ApiLog; import clap.server.exception.ErrorContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.persistence.EntityManager; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -18,9 +18,9 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -34,7 +34,7 @@ @RequiredArgsConstructor public class LoggingAspect { - private final ApiLogRepositoryPort apiLogRepositoryPort; + private final CommandLogPort commandLogPort; private final ObjectMapper objectMapper; @Pointcut("execution(* clap.server.adapter.inbound.web..*Controller.*(..))") @@ -46,79 +46,77 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = wrapRequest(attributes.getRequest()); HttpServletResponse response = attributes.getResponse(); - LocalDateTime requestAt = LocalDateTime.now(); Object result = null; try { result = joinPoint.proceed(); } finally { LocalDateTime responseAt = LocalDateTime.now(); - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - String logType = extractLogType(methodSignature); + LogTypeEnum logType = getLogType(methodSignature); String customCode = getCustomCode(response); - log.info("logType={}", logType); - - // 로그 저장 로직 - ApiLog apiLog = ApiLog.builder() - .serverIp("127.0.0.1") - .clientIp(request.getRemoteAddr()) - .requestUrl(request.getRequestURI()) - .requestMethod(request.getMethod()) - .statusCode(response != null ? response.getStatus() : 500) - .customStatusCode(customCode) - .request(extractRequestBody(request)) - .response(result != null ? result.toString() : "UNKNOWN") - .requestAt(requestAt) - .responseAt(responseAt) - .logType(logType).build(); - - //TODO: security filter 구현 후 주석 해제 - if ("로그인 로그".equals(logType)) { - String nickname = extractNicknameFromRequestBody(request); - apiLog = apiLog.toBuilder().memberId(nickname).build(); - log.info("로그인 로그={}", logType); - } else if (isUserAuthenticated()) { + String userId = null; + + if (LogTypeEnum.LOGIN.equals(logType)) { + userId = getNicknameFromRequestBody(request); + saveApiLog(request, response, result, responseAt, logType, customCode, userId); + } else if (LogTypeEnum.GENERAL.equals(logType)) { + if (!isUserAuthenticated()) { + log.error("인증된 사용자가 아니기 때문에 로그를 기록할 수 없습니다."); + } Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - log.info("일반 로그={}", logType); - log.info("principal={}", principal); if (principal instanceof SecurityUserDetails userDetails) { - // userDetails에서 userId를 추출하여 apiLog에 memberId로 설정 - log.info("useDetails={}", userDetails.getUserId()); - apiLog = apiLog.toBuilder().memberId(String.valueOf(userDetails.getUserId())).build(); + saveApiLog(request, response, result, responseAt, logType, customCode, userId); } } - apiLogRepositoryPort.save(apiLog); } return result; } + private void saveApiLog(HttpServletRequest request, HttpServletResponse response, + Object result, LocalDateTime responseAt, LogTypeEnum logType, + String customCode, String userId) { + ApiLog apiLog = ApiLog.builder() + .serverIp("127.0.0.1") + .clientIp(request.getRemoteAddr()) + .requestUrl(request.getRequestURI()) + .requestMethod(request.getMethod()) + .statusCode(response.getStatus()) + .customStatusCode(customCode) + .request(getRequestBody(request)) + .response(result != null ? result.toString() : "UNKNOWN") + .requestAt(LocalDateTime.now()) + .responseAt(responseAt) + .logType(logType) + .userId(userId) + .build(); + commandLogPort.save(apiLog); + } + + private HttpServletRequest wrapRequest(HttpServletRequest request) { - // ContentCachingRequestWrapper로 래핑 if (request instanceof ContentCachingRequestWrapper) { return request; } return new ContentCachingRequestWrapper(request); } - private String extractLogType(MethodSignature methodSignature) { - // 일반 로그 + private LogTypeEnum getLogType(MethodSignature methodSignature) { if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { - return methodSignature.getMethod().getAnnotation(LogType.class).value(); + return LogTypeEnum.fromDescription(methodSignature.getMethod().getAnnotation(LogType.class).value()); + } else { + throw new IllegalArgumentException("Log 추적이 허용되지 않은 엔드포인트"); } - return "로그인 로그"; // 기본값 } private String getCustomCode(HttpServletResponse response) { - // ErrorContext에서 커스텀 코드 추출 String customCode = ErrorContext.getCustomCode(); return customCode != null ? customCode : "CUSTOM" + (response != null ? response.getStatus() : 500); } - private String extractNicknameFromRequestBody(HttpServletRequest request) { + private String getNicknameFromRequestBody(HttpServletRequest request) { try { - // 요청 본문에서 nickname 필드 추출 - String requestBody = extractRequestBody(request); + String requestBody = getRequestBody(request); JsonNode jsonNode = objectMapper.readTree(requestBody); return jsonNode.has("nickname") ? jsonNode.get("nickname").asText() : null; } catch (Exception e) { @@ -126,9 +124,8 @@ private String extractNicknameFromRequestBody(HttpServletRequest request) { } } - private String extractRequestBody(HttpServletRequest request) { + private String getRequestBody(HttpServletRequest request) { try { - // ContentCachingRequestWrapper를 통해 요청 본문 읽기 ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request; byte[] content = cachingRequest.getContentAsByteArray(); return new String(content, StandardCharsets.UTF_8); @@ -137,10 +134,8 @@ private String extractRequestBody(HttpServletRequest request) { } } - //TODO: security filter 구현 후 주석 해제 private boolean isUserAuthenticated() { - // 사용자 인증 상태 확인 - var authentication = SecurityContextHolder.getContext().getAuthentication(); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal()); } diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 6f9da523..93335bcf 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -1,5 +1,6 @@ package clap.server.domain.model.log; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; import lombok.*; import lombok.experimental.SuperBuilder; @@ -12,7 +13,6 @@ public class ApiLog { private Long logId; private Integer statusCode; - private String memberId; private LocalDateTime requestAt; private LocalDateTime responseAt; private String dtype; @@ -23,19 +23,17 @@ public class ApiLog { private String customStatusCode; private String serverIp; private String requestMethod; - private String logType; + private LogTypeEnum logType; + private String userId; - // '로그인 시도'인지 확인 public boolean isLoginAttempt() { return "로그인 시도".equals(this.logType); } - // 익명 사용자 여부 확인 public boolean isAnonymousUser() { - return this.memberId == null || this.memberId.isBlank(); + return this.userId == null || this.userId.isBlank(); } - // 비즈니스 로직: 회원 로그 여부 확인 public boolean isMemberLog() { return !isAnonymousUser() && !"로그인 시도".equals(this.logType); } diff --git a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java index 96f03190..0e52f396 100644 --- a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java +++ b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java @@ -8,7 +8,6 @@ import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; import clap.server.application.RetrieveApiLogsUsecase; import clap.server.application.port.inbound.domain.LoginDomainService; -import clap.server.application.port.outbound.log.ApiLogRepositoryPort; import clap.server.domain.model.log.ApiLog; import org.junit.jupiter.api.Test; From 3313c65fbdd865fb1b74437df5c5e48b29403b93 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 00:20:18 +0900 Subject: [PATCH 04/16] =?UTF-8?q?CLAP-81=20Fix:=20memberLog=20=EB=B0=8F=20?= =?UTF-8?q?anonymousLog=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/admin/LogController.java | 4 +- .../persistense/ApiLogPersistenceAdapter.java | 136 +++--------------- .../mapper/ApiLogPersistenceMapper.java | 84 +++++++++++ .../log/CreateAnonymousLogsUsecase.java | 12 ++ .../inbound/log/CreateMemberLogsUsecase.java | 13 ++ ...gsUseCase.java => FindApiLogsUsecase.java} | 2 +- .../port/outbound/log/CommandLogPort.java | 6 +- .../log/CreateAnonymousLogsService.java | 25 ++++ .../service/log/CreateMemberLogsService.java | 33 +++++ .../log/FindApiLogsService.java} | 10 +- .../clap/server/config/aop/LoggingAspect.java | 56 ++------ .../server/domain/model/log/AnonymousLog.java | 24 ++++ .../clap/server/domain/model/log/ApiLog.java | 5 +- .../server/domain/model/log/MemberLog.java | 26 ++++ .../mapper/RetrieveApiLogsUsecaseTest.java | 4 +- 15 files changed, 269 insertions(+), 171 deletions(-) create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java create mode 100644 src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java create mode 100644 src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java rename src/main/java/clap/server/application/port/inbound/log/{GetApiLogsUseCase.java => FindApiLogsUsecase.java} (91%) create mode 100644 src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java create mode 100644 src/main/java/clap/server/application/service/log/CreateMemberLogsService.java rename src/main/java/clap/server/application/{RetrieveApiLogsUsecase.java => service/log/FindApiLogsService.java} (87%) diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java index f3bf084e..85332c98 100644 --- a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java @@ -2,7 +2,7 @@ import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; -import clap.server.application.port.inbound.log.GetApiLogsUseCase; +import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.WebAdapter; import clap.server.config.annotation.LogType; import lombok.RequiredArgsConstructor; @@ -19,7 +19,7 @@ @RequiredArgsConstructor public class LogController { - private final GetApiLogsUseCase getApiLogsUseCase; + private final FindApiLogsUsecase getApiLogsUseCase; @LogType("로그인 로그") @GetMapping("/login-logs") diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index a103ad31..8d4384d1 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -1,46 +1,46 @@ package clap.server.adapter.outbound.persistense; +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.ApiLogEntity; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; -import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; + +import clap.server.adapter.outbound.persistense.mapper.ApiLogPersistenceMapper; +import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository; import clap.server.adapter.outbound.persistense.repository.log.ApiLogRepository; import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository; import clap.server.application.port.outbound.log.CommandLogPort; import clap.server.application.port.outbound.log.LoadLogPort; +import clap.server.common.annotation.architecture.PersistenceAdapter; +import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; -import jakarta.persistence.EntityManager; +import clap.server.domain.model.log.MemberLog; + import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import java.util.List; + import java.util.stream.Collectors; -@Component +@PersistenceAdapter @RequiredArgsConstructor -@Transactional(readOnly = true) public class ApiLogPersistenceAdapter implements CommandLogPort, LoadLogPort { private final AnonymousLogRepository anonymousLogRepository; private final MemberLogRepository memberLogRepository; private final ApiLogRepository apiLogRepository; - private final EntityManager entityManager; + private final MemberPersistenceMapper memberPersistenceMapper; + private final ApiLogPersistenceMapper apiLogPersistenceMapper; @Override - @Transactional - public void save(ApiLog apiLog) { - validateApiLog(apiLog); - ApiLogEntity entity; - if (LogTypeEnum.LOGIN.equals(apiLog.getLogType())) { - entity = createAnonymousLogEntity(apiLog); - } else { - entity = createMemberLogEntity(apiLog); - } - apiLogRepository.save(entity); + public void saveMemberLog(MemberLog memberLog) { + apiLogRepository.save(apiLogPersistenceMapper.mapLogToMemberLogEntity(memberLog, memberPersistenceMapper.toEntity(memberLog.getMember()))); + + } + + @Override + public void saveAnonymousLog(AnonymousLog anonymousLog){ + apiLogRepository.save(apiLogPersistenceMapper.mapLogToAnonymousLogEntity(anonymousLog, anonymousLog.getLoginNickname())); } @Override @@ -59,100 +59,4 @@ public List findAnonymousLogs(String logType) { public List findMemberLogs() { return memberLogRepository.findAll(); } - - - private void validateApiLog(ApiLog apiLog) { - if (apiLog.getLogType() == null) { - throw new IllegalArgumentException("Log type must not be null or empty"); - } - if (!LogTypeEnum.LOGIN.equals(apiLog.getLogType()) && apiLog.getUserId() == null) { - throw new IllegalArgumentException("Member ID must not be null for member logs"); - } - } - - - private AnonymousLogEntity createAnonymousLogEntity(ApiLog apiLog) { - return AnonymousLogEntity.builder() - .serverIp(apiLog.getServerIp()) - .clientIp(apiLog.getClientIp()) - .requestUrl(apiLog.getRequestUrl()) - .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) - .statusCode(apiLog.getStatusCode()) - .customStatusCode(apiLog.getCustomStatusCode()) - .request(apiLog.getRequest()) - .response(apiLog.getResponse()) - .requestAt(apiLog.getRequestAt()) - .responseAt(apiLog.getResponseAt()) - .loginNickname(apiLog.getUserId() != null ? apiLog.getUserId() : "UNKNOWN") - .logType(apiLog.getLogType()) - .build(); - } - - private MemberLogEntity createMemberLogEntity(ApiLog apiLog) { - Long memberId = parseMemberId(apiLog.getUserId()); - - //TODO: member 가져오도록 수정 -> 영속화 - // 이미 존재하는 memberId로 MemberEntity를 조회 - MemberEntity newMember = entityManager.find(MemberEntity.class, memberId); - - return MemberLogEntity.builder() - .member(newMember) - .serverIp(apiLog.getServerIp()) - .clientIp(apiLog.getClientIp()) - .requestUrl(apiLog.getRequestUrl()) - .requestMethod(ApiHttpMethod.valueOf(apiLog.getRequestMethod())) - .statusCode(apiLog.getStatusCode()) - .customStatusCode(apiLog.getCustomStatusCode()) - .request(apiLog.getRequest()) - .response(apiLog.getResponse()) - .requestAt(apiLog.getRequestAt()) - .responseAt(apiLog.getResponseAt()) - .logType(apiLog.getLogType()) - .build(); - } - - private Long parseMemberId(String memberId) { - try { - return memberId != null ? Long.parseLong(memberId) : 0L; // 기본값 설정 - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid member ID: " + memberId, e); - } - } - - private ApiLog mapToDomain(ApiLogEntity entity) { - if (entity instanceof MemberLogEntity memberLogEntity) { - return ApiLog.builder() - .logId(memberLogEntity.getLogId()) - .serverIp(memberLogEntity.getServerIp()) - .clientIp(memberLogEntity.getClientIp()) - .requestUrl(memberLogEntity.getRequestUrl()) - .requestMethod(memberLogEntity.getRequestMethod().name()) - .statusCode(memberLogEntity.getStatusCode()) - .customStatusCode(memberLogEntity.getCustomStatusCode()) - .request(memberLogEntity.getRequest()) - .response(memberLogEntity.getResponse()) - .requestAt(memberLogEntity.getRequestAt()) - .responseAt(memberLogEntity.getResponseAt()) - .userId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID - .logType(memberLogEntity.getLogType()) - .build(); - } else if (entity instanceof AnonymousLogEntity anonymousLogEntity) { - return ApiLog.builder() - .logId(anonymousLogEntity.getLogId()) - .serverIp(anonymousLogEntity.getServerIp()) - .clientIp(anonymousLogEntity.getClientIp()) - .requestUrl(anonymousLogEntity.getRequestUrl()) - .requestMethod(anonymousLogEntity.getRequestMethod().name()) - .statusCode(anonymousLogEntity.getStatusCode()) - .customStatusCode(anonymousLogEntity.getCustomStatusCode()) - .request(anonymousLogEntity.getRequest()) - .response(anonymousLogEntity.getResponse()) - .requestAt(anonymousLogEntity.getRequestAt()) - .responseAt(anonymousLogEntity.getResponseAt()) - .userId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID - .logType(anonymousLogEntity.getLogType()) - .build(); - } - throw new IllegalStateException("Unknown log entity type"); - } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java new file mode 100644 index 00000000..0cdc514c --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java @@ -0,0 +1,84 @@ +package clap.server.adapter.outbound.persistense.mapper; + +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.ApiLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; +import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; +import org.springframework.stereotype.Component; + +@Component +public class ApiLogPersistenceMapper { + public AnonymousLogEntity mapLogToAnonymousLogEntity(ApiLog anonymousLog, String nickName) { + return AnonymousLogEntity.builder() + .serverIp(anonymousLog.getServerIp()) + .clientIp(anonymousLog.getClientIp()) + .requestUrl(anonymousLog.getRequestUrl()) + .requestMethod(ApiHttpMethod.valueOf(anonymousLog.getRequestMethod())) + .statusCode(anonymousLog.getStatusCode()) + .customStatusCode(anonymousLog.getCustomStatusCode()) + .request(anonymousLog.getRequest()) + .response(anonymousLog.getResponse()) + .requestAt(anonymousLog.getRequestAt()) + .responseAt(anonymousLog.getResponseAt()) + .loginNickname(nickName != null ? anonymousLog.getUserId() : "UNKNOWN") + .logType(anonymousLog.getLogType()) + .build(); + } + + public MemberLogEntity mapLogToMemberLogEntity(MemberLog memberLog, MemberEntity memberEntity) { + return MemberLogEntity.builder() + .member(memberEntity) + .serverIp(memberLog.getServerIp()) + .clientIp(memberLog.getClientIp()) + .requestUrl(memberLog.getRequestUrl()) + .requestMethod(ApiHttpMethod.valueOf(memberLog.getRequestMethod())) + .statusCode(memberLog.getStatusCode()) + .customStatusCode(memberLog.getCustomStatusCode()) + .request(memberLog.getRequest()) + .response(memberLog.getResponse()) + .requestAt(memberLog.getRequestAt()) + .responseAt(memberLog.getResponseAt()) + .logType(memberLog.getLogType()) + .build(); + } + + public ApiLog mapToDomain(ApiLogEntity entity) { + if (entity instanceof MemberLogEntity memberLogEntity) { + return ApiLog.builder() + .logId(memberLogEntity.getLogId()) + .serverIp(memberLogEntity.getServerIp()) + .clientIp(memberLogEntity.getClientIp()) + .requestUrl(memberLogEntity.getRequestUrl()) + .requestMethod(memberLogEntity.getRequestMethod().name()) + .statusCode(memberLogEntity.getStatusCode()) + .customStatusCode(memberLogEntity.getCustomStatusCode()) + .request(memberLogEntity.getRequest()) + .response(memberLogEntity.getResponse()) + .requestAt(memberLogEntity.getRequestAt()) + .responseAt(memberLogEntity.getResponseAt()) + .userId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID + .logType(memberLogEntity.getLogType()) + .build(); + } else if (entity instanceof AnonymousLogEntity anonymousLogEntity) { + return ApiLog.builder() + .logId(anonymousLogEntity.getLogId()) + .serverIp(anonymousLogEntity.getServerIp()) + .clientIp(anonymousLogEntity.getClientIp()) + .requestUrl(anonymousLogEntity.getRequestUrl()) + .requestMethod(anonymousLogEntity.getRequestMethod().name()) + .statusCode(anonymousLogEntity.getStatusCode()) + .customStatusCode(anonymousLogEntity.getCustomStatusCode()) + .request(anonymousLogEntity.getRequest()) + .response(anonymousLogEntity.getResponse()) + .requestAt(anonymousLogEntity.getRequestAt()) + .responseAt(anonymousLogEntity.getResponseAt()) + .userId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID + .logType(anonymousLogEntity.getLogType()) + .build(); + } + throw new IllegalStateException("Unknown log entity type"); + } +} diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java new file mode 100644 index 00000000..31bea12e --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java @@ -0,0 +1,12 @@ +package clap.server.application.port.inbound.log; + +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.time.LocalDateTime; + +public interface CreateAnonymousLogsUsecase { + void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogTypeEnum logType, String customCode, String requestBody, String nicknameFromRequestBody); + +} diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java new file mode 100644 index 00000000..c85b0108 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java @@ -0,0 +1,13 @@ +package clap.server.application.port.inbound.log; + +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.time.LocalDateTime; + +public interface CreateMemberLogsUsecase { + + void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, + LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Long userId); +} diff --git a/src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java similarity index 91% rename from src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java rename to src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java index 7611deec..08ead2a0 100644 --- a/src/main/java/clap/server/application/port/inbound/log/GetApiLogsUseCase.java +++ b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java @@ -7,7 +7,7 @@ import java.util.List; -public interface GetApiLogsUseCase { +public interface FindApiLogsUsecase { List getAnonymousLogs(); List getMemberLogs(); List getApiLogs(); diff --git a/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java b/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java index b5d8dc45..ed243aad 100644 --- a/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/CommandLogPort.java @@ -1,7 +1,9 @@ package clap.server.application.port.outbound.log; -import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.AnonymousLog; +import clap.server.domain.model.log.MemberLog; public interface CommandLogPort { - void save(ApiLog apiLog); + void saveMemberLog(MemberLog memberLog); + void saveAnonymousLog(AnonymousLog anonymousLog); } diff --git a/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java new file mode 100644 index 00000000..50fd2e3a --- /dev/null +++ b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java @@ -0,0 +1,25 @@ +package clap.server.application.service.log; + +import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.application.port.inbound.log.CreateAnonymousLogsUsecase; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.log.AnonymousLog; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; + +@ApplicationService +@RequiredArgsConstructor +public class CreateAnonymousLogsService implements CreateAnonymousLogsUsecase { + private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; + + @Override + public void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, String nickName) { + AnonymousLog anonymousLog = AnonymousLog.createAnonymousLog(request, response, result, responseAt, logType, customCode, body, nickName); + apiLogPersistenceAdapter.saveAnonymousLog(anonymousLog); + } +} diff --git a/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java new file mode 100644 index 00000000..75989cac --- /dev/null +++ b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java @@ -0,0 +1,33 @@ +package clap.server.application.service.log; + +import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.application.port.inbound.domain.MemberService; +import clap.server.application.port.inbound.log.CreateMemberLogsUsecase; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; +import clap.server.domain.model.member.Member; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@ApplicationService +@RequiredArgsConstructor +public class CreateMemberLogsService implements CreateMemberLogsUsecase { + + private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; + private final MemberService memberService; + + @Override + @Transactional + public void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, + LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Long userId) { + Member member = memberService.findById(userId); + MemberLog memberLog = MemberLog.createMemberLog(request, response, result, responseAt, logType, customCode, body, member); + apiLogPersistenceAdapter.saveMemberLog(memberLog); + } +} diff --git a/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java similarity index 87% rename from src/main/java/clap/server/application/RetrieveApiLogsUsecase.java rename to src/main/java/clap/server/application/service/log/FindApiLogsService.java index 6e58afe0..fd3fc021 100644 --- a/src/main/java/clap/server/application/RetrieveApiLogsUsecase.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -1,20 +1,20 @@ -package clap.server.application; +package clap.server.application.service.log; import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; import clap.server.application.mapper.AnonymousLogMapper; import clap.server.application.mapper.MemberLogMapper; import clap.server.application.port.inbound.domain.LoginDomainService; -import clap.server.application.port.inbound.log.GetApiLogsUseCase; +import clap.server.application.port.inbound.log.FindApiLogsUsecase; +import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.ApiLog; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; -@Service +@ApplicationService @RequiredArgsConstructor -public class RetrieveApiLogsUsecase implements GetApiLogsUseCase { +public class FindApiLogsService implements FindApiLogsUsecase { private final ApiLogRepositoryPort apiLogRepositoryPort; private final LoginDomainService loginDomainService; diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index f1b477d8..573dc5c8 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -3,9 +3,9 @@ import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; -import clap.server.application.port.outbound.log.CommandLogPort; +import clap.server.application.port.inbound.log.CreateAnonymousLogsUsecase; +import clap.server.application.port.inbound.log.CreateMemberLogsUsecase; import clap.server.config.annotation.LogType; -import clap.server.domain.model.log.ApiLog; import clap.server.exception.ErrorContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,9 +33,9 @@ @Component @RequiredArgsConstructor public class LoggingAspect { - - private final CommandLogPort commandLogPort; private final ObjectMapper objectMapper; + private final CreateAnonymousLogsUsecase createAnonymousLogsUsecase; + private final CreateMemberLogsUsecase createMemberLogsUsecase; @Pointcut("execution(* clap.server.adapter.inbound.web..*Controller.*(..))") public void controllerMethods() { @@ -44,7 +44,10 @@ public void controllerMethods() { @Around("controllerMethods()") public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - HttpServletRequest request = wrapRequest(attributes.getRequest()); + HttpServletRequest request = attributes.getRequest(); + if (!(request instanceof ContentCachingRequestWrapper)) { + request = new ContentCachingRequestWrapper(request); + } HttpServletResponse response = attributes.getResponse(); Object result = null; @@ -55,52 +58,23 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); LogTypeEnum logType = getLogType(methodSignature); String customCode = getCustomCode(response); - String userId = null; if (LogTypeEnum.LOGIN.equals(logType)) { - userId = getNicknameFromRequestBody(request); - saveApiLog(request, response, result, responseAt, logType, customCode, userId); + createAnonymousLogsUsecase.createAnonymousLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); } else if (LogTypeEnum.GENERAL.equals(logType)) { if (!isUserAuthenticated()) { - log.error("인증된 사용자가 아니기 때문에 로그를 기록할 수 없습니다."); - } - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (principal instanceof SecurityUserDetails userDetails) { - saveApiLog(request, response, result, responseAt, logType, customCode, userId); + log.error("로그를 기록할 수 없음"); + }else{ + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (principal instanceof SecurityUserDetails userDetails) { + createMemberLogsUsecase.createMemberLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), userDetails.getUserId()); + } } } } return result; } - private void saveApiLog(HttpServletRequest request, HttpServletResponse response, - Object result, LocalDateTime responseAt, LogTypeEnum logType, - String customCode, String userId) { - ApiLog apiLog = ApiLog.builder() - .serverIp("127.0.0.1") - .clientIp(request.getRemoteAddr()) - .requestUrl(request.getRequestURI()) - .requestMethod(request.getMethod()) - .statusCode(response.getStatus()) - .customStatusCode(customCode) - .request(getRequestBody(request)) - .response(result != null ? result.toString() : "UNKNOWN") - .requestAt(LocalDateTime.now()) - .responseAt(responseAt) - .logType(logType) - .userId(userId) - .build(); - commandLogPort.save(apiLog); - } - - - private HttpServletRequest wrapRequest(HttpServletRequest request) { - if (request instanceof ContentCachingRequestWrapper) { - return request; - } - return new ContentCachingRequestWrapper(request); - } - private LogTypeEnum getLogType(MethodSignature methodSignature) { if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { return LogTypeEnum.fromDescription(methodSignature.getMethod().getAnnotation(LogType.class).value()); diff --git a/src/main/java/clap/server/domain/model/log/AnonymousLog.java b/src/main/java/clap/server/domain/model/log/AnonymousLog.java index 9c48e102..d30ba204 100644 --- a/src/main/java/clap/server/domain/model/log/AnonymousLog.java +++ b/src/main/java/clap/server/domain/model/log/AnonymousLog.java @@ -1,12 +1,36 @@ package clap.server.domain.model.log; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.domain.model.member.Member; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.time.LocalDateTime; + @Getter @SuperBuilder @NoArgsConstructor public class AnonymousLog extends ApiLog { private String loginNickname; + + public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, + LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, String nickName) { + return AnonymousLog.builder() + .serverIp("127.0.0.1") + .clientIp(request.getRemoteAddr()) + .requestUrl(request.getRequestURI()) + .requestMethod(request.getMethod()) + .statusCode(response.getStatus()) + .customStatusCode(customCode) + .request(body) + .response(result != null ? result.toString() : "UNKNOWN") + .requestAt(LocalDateTime.now()) + .responseAt(responseAt) + .logType(logType) + .loginNickname(nickName) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 93335bcf..8fc16fd3 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -1,6 +1,7 @@ package clap.server.domain.model.log; import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.domain.model.common.BaseTime; import lombok.*; import lombok.experimental.SuperBuilder; @@ -10,15 +11,15 @@ @SuperBuilder(toBuilder = true) @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ApiLog { +public class ApiLog extends BaseTime { private Long logId; private Integer statusCode; private LocalDateTime requestAt; private LocalDateTime responseAt; private String dtype; private String request; - private String requestUrl; private String response; + private String requestUrl; private String clientIp; private String customStatusCode; private String serverIp; diff --git a/src/main/java/clap/server/domain/model/log/MemberLog.java b/src/main/java/clap/server/domain/model/log/MemberLog.java index 44483faf..bd4ee3f0 100644 --- a/src/main/java/clap/server/domain/model/log/MemberLog.java +++ b/src/main/java/clap/server/domain/model/log/MemberLog.java @@ -1,13 +1,39 @@ package clap.server.domain.model.log; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.domain.model.member.Member; +import clap.server.domain.model.task.Category; +import clap.server.domain.model.task.Task; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.time.LocalDateTime; + @Getter @SuperBuilder @NoArgsConstructor public class MemberLog extends ApiLog { private Member member; + + public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, + LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Member member) { + return MemberLog.builder() + .serverIp("127.0.0.1") //TODO: 실제 서버 ip 동적 주입 + .clientIp(request.getRemoteAddr()) + .requestUrl(request.getRequestURI()) + .requestMethod(request.getMethod()) + .statusCode(response.getStatus()) + .customStatusCode(customCode) + .request(body) + .response(result != null ? result.toString() : "UNKNOWN") + .requestAt(LocalDateTime.now()) + .responseAt(responseAt) + .logType(logType) + .member(member) + .build(); + } } \ No newline at end of file diff --git a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java index 0e52f396..7d9afc59 100644 --- a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java +++ b/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java @@ -6,7 +6,7 @@ import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; -import clap.server.application.RetrieveApiLogsUsecase; +import clap.server.application.service.log.FindApiLogsService; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.domain.model.log.ApiLog; import org.junit.jupiter.api.Test; @@ -21,7 +21,7 @@ class RetrieveApiLogsUsecaseTest { private final ApiLogRepositoryPort apiLogRepositoryPort = mock(ApiLogRepositoryPort.class); private final LoginDomainService loginDomainService = mock(LoginDomainService.class); - private final RetrieveApiLogsUsecase usecase = new RetrieveApiLogsUsecase(apiLogRepositoryPort, loginDomainService); + private final FindApiLogsService usecase = new FindApiLogsService(apiLogRepositoryPort, loginDomainService); @Test void getMemberLogs_ShouldReturnConvertedResponses() { From 9acdf3a2f09d7969098f3cd6e7e6c22b446b829d Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 00:29:27 +0900 Subject: [PATCH 05/16] =?UTF-8?q?CLAP-81=20Fix:=20=EA=B0=90=EC=82=AC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=A1=B0=ED=9A=8C=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/{admin => log}/LogController.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) rename src/main/java/clap/server/adapter/inbound/web/{admin => log}/LogController.java (75%) diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java similarity index 75% rename from src/main/java/clap/server/adapter/inbound/web/admin/LogController.java rename to src/main/java/clap/server/adapter/inbound/web/log/LogController.java index 85332c98..55303631 100644 --- a/src/main/java/clap/server/adapter/inbound/web/admin/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java @@ -1,4 +1,4 @@ -package clap.server.adapter.inbound.web.admin; +package clap.server.adapter.inbound.web.log; import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; @@ -6,6 +6,7 @@ import clap.server.common.annotation.architecture.WebAdapter; import clap.server.config.annotation.LogType; import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -15,21 +16,21 @@ @WebAdapter @RestController -@RequestMapping("/api/management") +@RequestMapping("/api/logs") @RequiredArgsConstructor public class LogController { private final FindApiLogsUsecase getApiLogsUseCase; - @LogType("로그인 로그") - @GetMapping("/login-logs") + @Secured({"ROLE_ADMIN"}) + @GetMapping("/login") public List getLoginAttempts() { return getApiLogsUseCase.getAnonymousLogs(); } - @LogType("일반 로그") - @GetMapping("/api-logs") - public List getApiCalls(@RequestParam(defaultValue = "") String logType) { + @Secured({"ROLE_ADMIN"}) + @GetMapping("/general") + public List getApiCalls() { return getApiLogsUseCase.getMemberLogs(); } } From 80155a80190254b12037cf5a6a8c57774e59502c Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 09:29:27 +0900 Subject: [PATCH 06/16] =?UTF-8?q?CLAP-81=20Fix:=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/dto/admin/AnonymousLogResponse.java | 3 +- .../web/dto/admin/MemberLogResponse.java | 2 +- .../persistense/ApiLogPersistenceAdapter.java | 27 ++--- .../entity/log/AnonymousLogEntity.java | 12 +-- .../persistense/entity/log/ApiLogEntity.java | 4 +- .../entity/log/MemberLogEntity.java | 14 +-- .../mapper/ApiLogPersistenceMapper.java | 98 ++++++++++++------- .../log/AnonymousLogRepository.java | 2 - .../repository/log/MemberLogRepository.java | 1 - .../mapper/AnonymousLogMapper.java | 21 ---- .../application/mapper/MemberLogMapper.java | 19 ---- .../mapper/response/LogMapper.java | 34 +++++++ .../inbound/domain/LoginDomainService.java | 7 +- .../port/outbound/log/LoadLogPort.java | 8 +- .../service/log/FindApiLogsService.java | 36 +++---- .../clap/server/domain/model/log/ApiLog.java | 16 +-- .../log/FindApiLogsServiceTest.java} | 28 +++--- 17 files changed, 162 insertions(+), 170 deletions(-) delete mode 100644 src/main/java/clap/server/application/mapper/AnonymousLogMapper.java delete mode 100644 src/main/java/clap/server/application/mapper/MemberLogMapper.java create mode 100644 src/main/java/clap/server/application/mapper/response/LogMapper.java rename src/test/java/clap/server/application/{mapper/RetrieveApiLogsUsecaseTest.java => service/log/FindApiLogsServiceTest.java} (81%) diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java index 21e8c9c1..3b34a6c0 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java @@ -22,7 +22,6 @@ public record AnonymousLogResponse( Integer statusCode, @NotNull String customStatusCode, - @NotNull - Integer failedAttempts + int failedAttempts ) { } diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java index a767d3fe..4bc424d3 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java @@ -21,6 +21,6 @@ public record MemberLogResponse( @NotBlank Integer statusCode, @NotNull - String customStatusCode + String customStatusCode //TODO: 작업 구분 속성 추가 ) { } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index 8d4384d1..3187c7c6 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -1,6 +1,5 @@ package clap.server.adapter.outbound.persistense; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; @@ -20,43 +19,45 @@ import java.util.List; -import java.util.stream.Collectors; - @PersistenceAdapter @RequiredArgsConstructor public class ApiLogPersistenceAdapter implements CommandLogPort, LoadLogPort { + private final ApiLogRepository apiLogRepository; private final AnonymousLogRepository anonymousLogRepository; private final MemberLogRepository memberLogRepository; - private final ApiLogRepository apiLogRepository; - private final MemberPersistenceMapper memberPersistenceMapper; private final ApiLogPersistenceMapper apiLogPersistenceMapper; + private final MemberPersistenceMapper memberPersistenceMapper; @Override public void saveMemberLog(MemberLog memberLog) { - apiLogRepository.save(apiLogPersistenceMapper.mapLogToMemberLogEntity(memberLog, memberPersistenceMapper.toEntity(memberLog.getMember()))); + apiLogRepository.save(apiLogPersistenceMapper.mapMemberLogToEntity(memberLog, memberPersistenceMapper.toEntity(memberLog.getMember()))); } @Override public void saveAnonymousLog(AnonymousLog anonymousLog){ - apiLogRepository.save(apiLogPersistenceMapper.mapLogToAnonymousLogEntity(anonymousLog, anonymousLog.getLoginNickname())); + apiLogRepository.save(apiLogPersistenceMapper.mapAnonymousLogToEntity(anonymousLog, anonymousLog.getLoginNickname())); } @Override public List findAllLogs() { return apiLogRepository.findAll().stream() - .map(this::mapToDomain) // 엔티티를 도메인 객체로 매핑 - .collect(Collectors.toList()); + .map(apiLogPersistenceMapper::mapLogEntityToDomain) + .toList(); } @Override - public List findAnonymousLogs(String logType) { - return anonymousLogRepository.findByLogType(logType); + public List findAnonymousLogs() { + return anonymousLogRepository.findAll().stream() + .map(apiLogPersistenceMapper::mapAnonymousLogEntityToDomain) + .toList(); } @Override - public List findMemberLogs() { - return memberLogRepository.findAll(); + public List findMemberLogs() { + return memberLogRepository.findAll().stream() + .map(apiLogPersistenceMapper::mapMemberLogEntityToDomain) + .toList(); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java index 10845486..58f7607e 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java @@ -19,10 +19,10 @@ public class AnonymousLogEntity extends ApiLogEntity { @Column(nullable = false) private String loginNickname; - @Override - public ApiLog toDomain() { - return toCommonDomainBuilder() - .userId(loginNickname) - .build(); - } +// @Override +// public ApiLog toDomain() { +// return toCommonDomainBuilder() +// .userId(loginNickname) +// .build(); +// } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index bb80d53f..61a54518 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -60,7 +60,7 @@ public abstract class ApiLogEntity extends BaseTimeEntity { private LogTypeEnum logType; @Version - private Long version; // 낙관적 락 관리를 위한 버전 + private Long version; protected ApiLog.ApiLogBuilder toCommonDomainBuilder() { return ApiLog.builder() @@ -78,5 +78,5 @@ protected ApiLog.ApiLogBuilder toCommonDomainBuilder() { .logType(logType); } - public abstract ApiLog toDomain(); +// public abstract ApiLog toDomain(); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java index 5a505ff6..0b989808 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java @@ -15,14 +15,14 @@ @SuperBuilder public class MemberLogEntity extends ApiLogEntity { - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "member_id") private MemberEntity member; - @Override - public ApiLog toDomain() { - return toCommonDomainBuilder() - .userId(member != null ? String.valueOf(member.getMemberId()) : null) - .build(); - } +// @Override +// public ApiLog toDomain() { +// return toCommonDomainBuilder() +// .userId(member != null ? String.valueOf(member.getMemberId()) : null) +// .build(); +// } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java index 0cdc514c..2cc7533c 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java @@ -5,13 +5,17 @@ import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; import clap.server.domain.model.log.MemberLog; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @Component +@RequiredArgsConstructor public class ApiLogPersistenceMapper { - public AnonymousLogEntity mapLogToAnonymousLogEntity(ApiLog anonymousLog, String nickName) { + private final MemberPersistenceMapper memberPersistenceMapper; + public AnonymousLogEntity mapAnonymousLogToEntity(ApiLog anonymousLog, String nickName) { return AnonymousLogEntity.builder() .serverIp(anonymousLog.getServerIp()) .clientIp(anonymousLog.getClientIp()) @@ -23,12 +27,12 @@ public AnonymousLogEntity mapLogToAnonymousLogEntity(ApiLog anonymousLog, String .response(anonymousLog.getResponse()) .requestAt(anonymousLog.getRequestAt()) .responseAt(anonymousLog.getResponseAt()) - .loginNickname(nickName != null ? anonymousLog.getUserId() : "UNKNOWN") + .loginNickname(nickName != null ? nickName : "UNKNOWN") .logType(anonymousLog.getLogType()) .build(); } - public MemberLogEntity mapLogToMemberLogEntity(MemberLog memberLog, MemberEntity memberEntity) { + public MemberLogEntity mapMemberLogToEntity(MemberLog memberLog, MemberEntity memberEntity) { return MemberLogEntity.builder() .member(memberEntity) .serverIp(memberLog.getServerIp()) @@ -45,40 +49,58 @@ public MemberLogEntity mapLogToMemberLogEntity(MemberLog memberLog, MemberEntity .build(); } - public ApiLog mapToDomain(ApiLogEntity entity) { - if (entity instanceof MemberLogEntity memberLogEntity) { - return ApiLog.builder() - .logId(memberLogEntity.getLogId()) - .serverIp(memberLogEntity.getServerIp()) - .clientIp(memberLogEntity.getClientIp()) - .requestUrl(memberLogEntity.getRequestUrl()) - .requestMethod(memberLogEntity.getRequestMethod().name()) - .statusCode(memberLogEntity.getStatusCode()) - .customStatusCode(memberLogEntity.getCustomStatusCode()) - .request(memberLogEntity.getRequest()) - .response(memberLogEntity.getResponse()) - .requestAt(memberLogEntity.getRequestAt()) - .responseAt(memberLogEntity.getResponseAt()) - .userId(memberLogEntity.getMember().getMemberId().toString()) // 회원 ID - .logType(memberLogEntity.getLogType()) - .build(); - } else if (entity instanceof AnonymousLogEntity anonymousLogEntity) { - return ApiLog.builder() - .logId(anonymousLogEntity.getLogId()) - .serverIp(anonymousLogEntity.getServerIp()) - .clientIp(anonymousLogEntity.getClientIp()) - .requestUrl(anonymousLogEntity.getRequestUrl()) - .requestMethod(anonymousLogEntity.getRequestMethod().name()) - .statusCode(anonymousLogEntity.getStatusCode()) - .customStatusCode(anonymousLogEntity.getCustomStatusCode()) - .request(anonymousLogEntity.getRequest()) - .response(anonymousLogEntity.getResponse()) - .requestAt(anonymousLogEntity.getRequestAt()) - .responseAt(anonymousLogEntity.getResponseAt()) - .userId(anonymousLogEntity.getLoginNickname()) // 로그인 시도 ID - .logType(anonymousLogEntity.getLogType()) - .build(); - } - throw new IllegalStateException("Unknown log entity type"); + public AnonymousLog mapAnonymousLogEntityToDomain(AnonymousLogEntity anonymousLogEntity) { + return AnonymousLog.builder() + .logId(anonymousLogEntity.getLogId()) + .serverIp(anonymousLogEntity.getServerIp()) + .clientIp(anonymousLogEntity.getClientIp()) + .requestUrl(anonymousLogEntity.getRequestUrl()) + .requestMethod(anonymousLogEntity.getRequestMethod().name()) + .statusCode(anonymousLogEntity.getStatusCode()) + .customStatusCode(anonymousLogEntity.getCustomStatusCode()) + .request(anonymousLogEntity.getRequest()) + .response(anonymousLogEntity.getResponse()) + .requestAt(anonymousLogEntity.getRequestAt()) + .responseAt(anonymousLogEntity.getResponseAt()) + .logType(anonymousLogEntity.getLogType()) + .loginNickname(anonymousLogEntity.getLoginNickname()) + .build(); + } + + public MemberLog mapMemberLogEntityToDomain(MemberLogEntity memberLogEntity) { + return MemberLog.builder() + .logId(memberLogEntity.getLogId()) + .serverIp(memberLogEntity.getServerIp()) + .clientIp(memberLogEntity.getClientIp()) + .requestUrl(memberLogEntity.getRequestUrl()) + .requestMethod(memberLogEntity.getRequestMethod().name()) + .statusCode(memberLogEntity.getStatusCode()) + .customStatusCode(memberLogEntity.getCustomStatusCode()) + .request(memberLogEntity.getRequest()) + .response(memberLogEntity.getResponse()) + .requestAt(memberLogEntity.getRequestAt()) + .responseAt(memberLogEntity.getResponseAt()) + .logType(memberLogEntity.getLogType()) + .member(memberLogEntity.getMember() != null + ? memberPersistenceMapper.toDomain(memberLogEntity.getMember()) + : null) + .build(); + } + + public ApiLog mapLogEntityToDomain(ApiLogEntity logEntity) { + return ApiLog.builder() + .logId(logEntity.getLogId()) + .serverIp(logEntity.getServerIp()) + .clientIp(logEntity.getClientIp()) + .requestUrl(logEntity.getRequestUrl()) + .requestMethod(logEntity.getRequestMethod().name()) + .statusCode(logEntity.getStatusCode()) + .customStatusCode(logEntity.getCustomStatusCode()) + .request(logEntity.getRequest()) + .response(logEntity.getResponse()) + .requestAt(logEntity.getRequestAt()) + .responseAt(logEntity.getResponseAt()) + .logType(logEntity.getLogType()) + .build(); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java index c59ac6a9..273aa26d 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java @@ -8,6 +8,4 @@ @Repository public interface AnonymousLogRepository extends JpaRepository { - // 비회원 로그 - List findByLogType(String logType); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java index 8ccaf8cd..f9ab34ab 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java @@ -8,6 +8,5 @@ @Repository public interface MemberLogRepository extends JpaRepository { - // 회원 List findAll(); } diff --git a/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java b/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java deleted file mode 100644 index deb656e9..00000000 --- a/src/main/java/clap/server/application/mapper/AnonymousLogMapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package clap.server.application.mapper; - -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.domain.model.log.ApiLog; - -public class AnonymousLogMapper { - - public static AnonymousLogResponse toDto(ApiLog log, Integer failedAttempts) { - return new AnonymousLogResponse( - log.getLogId(), - log.getMemberId(), // 비회원 닉네임 - log.getRequestAt(), - log.getResponseAt(), - log.getRequestUrl(), - log.getRequestMethod(), - log.getStatusCode(), - log.getCustomStatusCode(), - failedAttempts != null ? failedAttempts : 0 // 실패 시도 수 기본값 - ); - } -} diff --git a/src/main/java/clap/server/application/mapper/MemberLogMapper.java b/src/main/java/clap/server/application/mapper/MemberLogMapper.java deleted file mode 100644 index 0cfc0e29..00000000 --- a/src/main/java/clap/server/application/mapper/MemberLogMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package clap.server.application.mapper; - -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; -import clap.server.domain.model.log.ApiLog; - -public class MemberLogMapper { - public static MemberLogResponse toDto(ApiLog log) { - return new MemberLogResponse( - log.getLogId(), - Long.valueOf(log.getMemberId()), // memberId는 항상 값을 가져야 함 - log.getRequestAt(), - log.getResponseAt(), - log.getRequestUrl(), - log.getRequestMethod(), - log.getStatusCode(), - log.getCustomStatusCode() - ); - } -} diff --git a/src/main/java/clap/server/application/mapper/response/LogMapper.java b/src/main/java/clap/server/application/mapper/response/LogMapper.java new file mode 100644 index 00000000..44654ec0 --- /dev/null +++ b/src/main/java/clap/server/application/mapper/response/LogMapper.java @@ -0,0 +1,34 @@ +package clap.server.application.mapper.response; + +import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.domain.model.log.AnonymousLog; +import clap.server.domain.model.log.MemberLog; + +public class LogMapper { + public static AnonymousLogResponse toAnonymounsLogResponse(AnonymousLog anonymousLog, int failedAttempts) { + return new AnonymousLogResponse( + anonymousLog.getLogId(), + anonymousLog.getLoginNickname(), + anonymousLog.getRequestAt(), + anonymousLog.getResponseAt(), + anonymousLog.getRequestUrl(), + anonymousLog.getRequestMethod(), + anonymousLog.getStatusCode(), + anonymousLog.getCustomStatusCode(), + failedAttempts + ); + } + public static MemberLogResponse toMemberLogResponse(MemberLog memberLog) { + return new MemberLogResponse( + memberLog.getLogId(), + memberLog.getMember().getMemberId(), + memberLog.getRequestAt(), + memberLog.getResponseAt(), + memberLog.getRequestUrl(), + memberLog.getRequestMethod(), + memberLog.getStatusCode(), + memberLog.getCustomStatusCode() + ); + } +} diff --git a/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java b/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java index 02293e21..d2362601 100644 --- a/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java +++ b/src/main/java/clap/server/application/port/inbound/domain/LoginDomainService.java @@ -2,12 +2,11 @@ import org.springframework.stereotype.Service; -// TODO: 로그인 도메인 실패횟수 예제 코드 (나중에 삭제할 예정) @Service public class LoginDomainService { - public Integer getFailedAttemptCount(String loginNickname) { - // 로그인 실패 횟수 계산 로직 추가 - return 3; // 테스트값 + public int getFailedAttemptCount(String loginNickname) { + //TODO: 로그인 실패 횟수 계산 로직 추가 + return 3; } } diff --git a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java index fbc114b4..1c338084 100644 --- a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java @@ -1,13 +1,13 @@ package clap.server.application.port.outbound.log; -import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; import java.util.List; public interface LoadLogPort { List findAllLogs(); - List findAnonymousLogs(String logType); - List findMemberLogs(); + List findAnonymousLogs(); + List findMemberLogs(); } diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index fd3fc021..d91c2aec 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -2,53 +2,47 @@ import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; -import clap.server.application.mapper.AnonymousLogMapper; -import clap.server.application.mapper.MemberLogMapper; +import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; +import clap.server.application.mapper.response.LogMapper; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @ApplicationService @RequiredArgsConstructor +@Transactional(readOnly = true) public class FindApiLogsService implements FindApiLogsUsecase { - private final ApiLogRepositoryPort apiLogRepositoryPort; + private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; private final LoginDomainService loginDomainService; @Override public List getAnonymousLogs() { - // 비회원 로그에서 '로그인 시도' 로그를 조회하여 DTO로 변환 - return apiLogRepositoryPort.findAnonymousLogs("로그인 시도").stream() - .map(entity -> { - ApiLog log = entity.toDomain(); // 엔티티를 도메인 객체로 변환 - int failedAttempts = loginDomainService.getFailedAttemptCount(log.getMemberId()); - return AnonymousLogMapper.toDto(log, failedAttempts); + return apiLogPersistenceAdapter.findAnonymousLogs().stream() + .map(anonymousLog -> { + int failedAttempts = loginDomainService.getFailedAttemptCount(anonymousLog.getLoginNickname()); + return LogMapper.toAnonymounsLogResponse(anonymousLog, failedAttempts); }) .toList(); } + //TODO: Paging으로 수정 @Override public List getMemberLogs() { - // 회원 로그를 조회하여 DTO로 변환 - return apiLogRepositoryPort.findMemberLogs().stream() - .map(entity -> { - ApiLog log = entity.toDomain(); // 엔티티를 도메인 객체로 변환 - return MemberLogMapper.toDto(log); - }) + return apiLogPersistenceAdapter.findMemberLogs().stream() + .map(LogMapper::toMemberLogResponse) .toList(); } + //테스트용 @Override public List getApiLogs() { - // 모든 로그 조회 - return apiLogRepositoryPort.findAllLogs(); - } - - public void save(ApiLog apiLog) { - apiLogRepositoryPort.save(apiLog); + return apiLogPersistenceAdapter.findAllLogs(); } } diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 8fc16fd3..5b54a59b 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -8,7 +8,7 @@ import java.time.LocalDateTime; @Getter -@SuperBuilder(toBuilder = true) +@SuperBuilder @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ApiLog extends BaseTime { @@ -16,7 +16,6 @@ public class ApiLog extends BaseTime { private Integer statusCode; private LocalDateTime requestAt; private LocalDateTime responseAt; - private String dtype; private String request; private String response; private String requestUrl; @@ -25,17 +24,4 @@ public class ApiLog extends BaseTime { private String serverIp; private String requestMethod; private LogTypeEnum logType; - private String userId; - - public boolean isLoginAttempt() { - return "로그인 시도".equals(this.logType); - } - - public boolean isAnonymousUser() { - return this.userId == null || this.userId.isBlank(); - } - - public boolean isMemberLog() { - return !isAnonymousUser() && !"로그인 시도".equals(this.logType); - } } diff --git a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java b/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java similarity index 81% rename from src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java rename to src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java index 7d9afc59..2c2ebe13 100644 --- a/src/test/java/clap/server/application/mapper/RetrieveApiLogsUsecaseTest.java +++ b/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java @@ -1,12 +1,12 @@ -package clap.server.application.mapper; +package clap.server.application.service.log; import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; -import clap.server.application.service.log.FindApiLogsService; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.domain.model.log.ApiLog; import org.junit.jupiter.api.Test; @@ -17,11 +17,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; -class RetrieveApiLogsUsecaseTest { +class FindApiLogsServiceTest { - private final ApiLogRepositoryPort apiLogRepositoryPort = mock(ApiLogRepositoryPort.class); + private final ApiLogPersistenceAdapter apiLogPersistenceAdapter = mock(ApiLogPersistenceAdapter.class); private final LoginDomainService loginDomainService = mock(LoginDomainService.class); - private final FindApiLogsService usecase = new FindApiLogsService(apiLogRepositoryPort, loginDomainService); + private final FindApiLogsService findApiLogsService = new FindApiLogsService(apiLogPersistenceAdapter, loginDomainService); @Test void getMemberLogs_ShouldReturnConvertedResponses() { @@ -46,10 +46,10 @@ void getMemberLogs_ShouldReturnConvertedResponses() { System.out.println("DEBUG: Created mockEntity - Member ID: " + mockEntity.getMember().getMemberId()); // Mock 리포지토리 설정 - when(apiLogRepositoryPort.findMemberLogs()).thenReturn(List.of(mockEntity)); + when(apiLogPersistenceAdapter.findMemberLogs()).thenReturn(List.of(mockEntity)); // 테스트 실행 - List responses = usecase.getMemberLogs(); + List responses = findApiLogsService.getMemberLogs(); // 디버깅: 반환된 responses 확인 // 결과 검증 @@ -57,7 +57,7 @@ void getMemberLogs_ShouldReturnConvertedResponses() { MemberLogResponse response = responses.get(0); assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 assertThat(response.memberId()).isEqualTo(5L); // memberId 검증 - verify(apiLogRepositoryPort, times(1)).findMemberLogs(); // 리포지토리 호출 검증 + verify(apiLogPersistenceAdapter, times(1)).findMemberLogs(); // 리포지토리 호출 검증 } @Test @@ -78,11 +78,11 @@ void getAnonymousLogs_ShouldReturnConvertedResponses() { System.out.println("DEBUG: Created mockEntity - Login Nickname: " + mockEntity.getLoginNickname()); // Mock 서비스 설정 - when(apiLogRepositoryPort.findAnonymousLogs("로그인 시도")).thenReturn(List.of(mockEntity)); + when(apiLogPersistenceAdapter.findAnonymousLogs("로그인 시도")).thenReturn(List.of(mockEntity)); when(loginDomainService.getFailedAttemptCount("testUser")).thenReturn(3); // 실패 시도 수 설정 // 테스트 실행 - List responses = usecase.getAnonymousLogs(); + List responses = findApiLogsService.getAnonymousLogs(); // 디버깅: 반환된 responses 확인 System.out.println("DEBUG: Received responses: " + responses); @@ -92,7 +92,7 @@ void getAnonymousLogs_ShouldReturnConvertedResponses() { AnonymousLogResponse response = responses.get(0); assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 assertThat(response.failedAttempts()).isEqualTo(3); // 실패 시도 검증 - verify(apiLogRepositoryPort, times(1)).findAnonymousLogs("로그인 시도"); + verify(apiLogPersistenceAdapter, times(1)).findAnonymousLogs("로그인 시도"); verify(loginDomainService, times(1)).getFailedAttemptCount("testUser"); } @@ -131,10 +131,10 @@ void getAllLogs_ShouldReturnAllLogs() { System.out.println("DEBUG: Created memberLogEntity - Member ID: " + memberLogEntity.getMember().getMemberId()); // Mock 리포지토리 설정 - when(apiLogRepositoryPort.findAllLogs()).thenReturn(List.of(anonymousLogEntity.toDomain(), memberLogEntity.toDomain())); + when(apiLogPersistenceAdapter.findAllLogs()).thenReturn(List.of(anonymousLogEntity.toDomain(), memberLogEntity.toDomain())); // 테스트 실행 - List logs = usecase.getApiLogs(); + List logs = findApiLogsService.getApiLogs(); // 디버깅: 반환된 logs 확인 System.out.println("DEBUG: Received logs: " + logs); @@ -143,6 +143,6 @@ void getAllLogs_ShouldReturnAllLogs() { assertThat(logs).hasSize(2); assertThat(logs.get(0).getLogId()).isEqualTo(1L); // AnonymousLogEntity 검증 assertThat(logs.get(1).getLogId()).isEqualTo(2L); // MemberLogEntity 검증 - verify(apiLogRepositoryPort, times(1)).findAllLogs(); + verify(apiLogPersistenceAdapter, times(1)).findAllLogs(); } } From 36b9bbe0f4caabf4582e9b21e1d4e81d167f4514 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 10:30:23 +0900 Subject: [PATCH 07/16] =?UTF-8?q?CLAP-81=20Add:=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EA=B5=AC=EB=B6=84=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20enum=20=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/dto/admin/MemberLogResponse.java | 26 ---------------- .../{admin => log}/AnonymousLogResponse.java | 2 +- .../web/dto/log/MemberLogResponse.java | 20 +++++++++++++ .../inbound/web/log/LogController.java | 6 ++-- .../web/task/FindTaskHistoryController.java | 2 +- .../entity/log/AnonymousLogEntity.java | 8 ----- .../persistense/entity/log/ApiLogEntity.java | 23 ++------------ .../entity/log/MemberLogEntity.java | 7 ----- .../entity/log/constant/LogStatus.java | 23 ++++++++++++++ .../entity/log/constant/LogTypeEnum.java | 22 -------------- .../entity/member/MemberEntity.java | 3 -- .../mapper/ApiLogPersistenceMapper.java | 10 +++---- .../repository/log/MemberLogRepository.java | 1 - .../mapper/response/LogMapper.java | 14 ++++----- .../log/CreateAnonymousLogsUsecase.java | 4 +-- .../inbound/log/CreateMemberLogsUsecase.java | 4 +-- .../port/inbound/log/FindApiLogsUsecase.java | 4 +-- .../log/CreateAnonymousLogsService.java | 4 +-- .../service/log/CreateMemberLogsService.java | 5 ++-- .../service/log/FindApiLogsService.java | 5 ++-- .../clap/server/config/aop/LoggingAspect.java | 30 ++++++++++--------- .../server/domain/model/log/AnonymousLog.java | 7 ++--- .../clap/server/domain/model/log/ApiLog.java | 6 ++-- .../server/domain/model/log/MemberLog.java | 9 ++---- .../service/log/FindApiLogsServiceTest.java | 4 +-- 25 files changed, 99 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java rename src/main/java/clap/server/adapter/inbound/web/dto/{admin => log}/AnonymousLogResponse.java (91%) create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogStatus.java delete mode 100644 src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java deleted file mode 100644 index 4bc424d3..00000000 --- a/src/main/java/clap/server/adapter/inbound/web/dto/admin/MemberLogResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package clap.server.adapter.inbound.web.dto.admin; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - -import java.time.LocalDateTime; - -public record MemberLogResponse( - @NotBlank - Long logId, - @NotBlank - Long memberId, - @NotBlank - LocalDateTime requestAt, - @NotBlank - LocalDateTime responseAt, - @NotBlank - String requestUrl, - @NotBlank - String requestMethod, - @NotBlank - Integer statusCode, - @NotNull - String customStatusCode //TODO: 작업 구분 속성 추가 -) { -} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java similarity index 91% rename from src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java rename to src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java index 3b34a6c0..e3439a40 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AnonymousLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java @@ -1,4 +1,4 @@ -package clap.server.adapter.inbound.web.dto.admin; +package clap.server.adapter.inbound.web.dto.log; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java new file mode 100644 index 00000000..f9cb8183 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java @@ -0,0 +1,20 @@ +package clap.server.adapter.inbound.web.dto.log; + +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.time.LocalDateTime; + +public record MemberLogResponse( + @NotBlank + Long logId, + LogStatus logStatus, + @NotBlank + LocalDateTime requestAt, + String nickName, + String serverIp, + @NotBlank + Integer statusCode +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java index 55303631..247b635c 100644 --- a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java @@ -1,15 +1,13 @@ package clap.server.adapter.inbound.web.log; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.WebAdapter; -import clap.server.config.annotation.LogType; import lombok.RequiredArgsConstructor; import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; diff --git a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java index 5dfc22be..6dc6f929 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java @@ -27,7 +27,7 @@ public class FindTaskHistoryController { private final FindTaskHistoriesUsecase findTaskHistoriesUsecase; - @LogType("일반 로그") + @LogType("TASK_VIEWED") @Operation(summary = "작업 히스토리 조회") @Secured({"ROLE_MANAGER","ROLE_USER"}) @GetMapping("/{taskId}/histories") diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java index 58f7607e..91b566c7 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java @@ -1,6 +1,5 @@ package clap.server.adapter.outbound.persistense.entity.log; -import clap.server.domain.model.log.ApiLog; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; @@ -18,11 +17,4 @@ public class AnonymousLogEntity extends ApiLogEntity { @Column(nullable = false) private String loginNickname; - -// @Override -// public ApiLog toDomain() { -// return toCommonDomainBuilder() -// .userId(loginNickname) -// .build(); -// } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index 61a54518..658bc09f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -2,8 +2,7 @@ import clap.server.adapter.outbound.persistense.entity.common.BaseTimeEntity; import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; -import clap.server.domain.model.log.ApiLog; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -57,26 +56,8 @@ public abstract class ApiLogEntity extends BaseTimeEntity { @Column(nullable = false) @Enumerated(EnumType.STRING) - private LogTypeEnum logType; + private LogStatus logStatus; @Version private Long version; - - protected ApiLog.ApiLogBuilder toCommonDomainBuilder() { - return ApiLog.builder() - .logId(logId) - .serverIp(serverIp) - .clientIp(clientIp) - .requestUrl(requestUrl) - .requestMethod(requestMethod.name()) - .statusCode(statusCode) - .customStatusCode(customStatusCode) - .request(request) - .response(response) - .requestAt(requestAt) - .responseAt(responseAt) - .logType(logType); - } - -// public abstract ApiLog toDomain(); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java index 0b989808..aaa0f0cd 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/MemberLogEntity.java @@ -18,11 +18,4 @@ public class MemberLogEntity extends ApiLogEntity { @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "member_id") private MemberEntity member; - -// @Override -// public ApiLog toDomain() { -// return toCommonDomainBuilder() -// .userId(member != null ? String.valueOf(member.getMemberId()) : null) -// .build(); -// } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogStatus.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogStatus.java new file mode 100644 index 00000000..1824c085 --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogStatus.java @@ -0,0 +1,23 @@ +package clap.server.adapter.outbound.persistense.entity.log.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum LogStatus { + LOGIN("로그인"), + REQUEST_CREATED("요청 생성"), + REQUEST_UPDATED("요청 수정"), + REQUEST_CANCELLED("요청 취소"), + REQUEST_APPROVED("요청 승인"), + ASSIGNER_CHANGED("처리자 변경"), + COMMENT_ADDED("댓글 추가"), + COMMENT_UPDATED("댓글 수정"), + STATUS_CHANGED("작업 상태 변경"), + TASK_COMPLETED("작업 완료"), + TASK_FAILED("작업 실패"), + TASK_VIEWED("작업 조회"); + + private final String description; +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java deleted file mode 100644 index 7ac80d44..00000000 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/constant/LogTypeEnum.java +++ /dev/null @@ -1,22 +0,0 @@ -package clap.server.adapter.outbound.persistense.entity.log.constant; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum LogTypeEnum { - LOGIN("로그인 로그"), - GENERAL("일반 로그"); - - private final String description; - - public static LogTypeEnum fromDescription(String description) { - for (LogTypeEnum logType : LogTypeEnum.values()) { - if (logType.getDescription().equals(description)) { - return logType; - } - } - throw new IllegalArgumentException("해당하는 로그 타입이 없습니다: " + description); - } -} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java index eb914f17..ba9862ad 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java @@ -58,7 +58,4 @@ public class MemberEntity extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "admin_id") private MemberEntity admin; - - @Version - private Long version; // 낙관적 락을 위한 버전 관리 } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java index 2cc7533c..427cbcea 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java @@ -28,7 +28,7 @@ public AnonymousLogEntity mapAnonymousLogToEntity(ApiLog anonymousLog, String ni .requestAt(anonymousLog.getRequestAt()) .responseAt(anonymousLog.getResponseAt()) .loginNickname(nickName != null ? nickName : "UNKNOWN") - .logType(anonymousLog.getLogType()) + .logStatus(anonymousLog.getLogStatus()) .build(); } @@ -45,7 +45,7 @@ public MemberLogEntity mapMemberLogToEntity(MemberLog memberLog, MemberEntity me .response(memberLog.getResponse()) .requestAt(memberLog.getRequestAt()) .responseAt(memberLog.getResponseAt()) - .logType(memberLog.getLogType()) + .logStatus(memberLog.getLogStatus()) .build(); } @@ -62,7 +62,7 @@ public AnonymousLog mapAnonymousLogEntityToDomain(AnonymousLogEntity anonymousLo .response(anonymousLogEntity.getResponse()) .requestAt(anonymousLogEntity.getRequestAt()) .responseAt(anonymousLogEntity.getResponseAt()) - .logType(anonymousLogEntity.getLogType()) + .logStatus(anonymousLogEntity.getLogStatus()) .loginNickname(anonymousLogEntity.getLoginNickname()) .build(); } @@ -80,7 +80,7 @@ public MemberLog mapMemberLogEntityToDomain(MemberLogEntity memberLogEntity) { .response(memberLogEntity.getResponse()) .requestAt(memberLogEntity.getRequestAt()) .responseAt(memberLogEntity.getResponseAt()) - .logType(memberLogEntity.getLogType()) + .logStatus(memberLogEntity.getLogStatus()) .member(memberLogEntity.getMember() != null ? memberPersistenceMapper.toDomain(memberLogEntity.getMember()) : null) @@ -100,7 +100,7 @@ public ApiLog mapLogEntityToDomain(ApiLogEntity logEntity) { .response(logEntity.getResponse()) .requestAt(logEntity.getRequestAt()) .responseAt(logEntity.getResponseAt()) - .logType(logEntity.getLogType()) + .logStatus(logEntity.getLogStatus()) .build(); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java index f9ab34ab..a65ac57f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java @@ -8,5 +8,4 @@ @Repository public interface MemberLogRepository extends JpaRepository { - List findAll(); } diff --git a/src/main/java/clap/server/application/mapper/response/LogMapper.java b/src/main/java/clap/server/application/mapper/response/LogMapper.java index 44654ec0..d86cfad6 100644 --- a/src/main/java/clap/server/application/mapper/response/LogMapper.java +++ b/src/main/java/clap/server/application/mapper/response/LogMapper.java @@ -1,7 +1,7 @@ package clap.server.application.mapper.response; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.MemberLog; @@ -22,13 +22,11 @@ public static AnonymousLogResponse toAnonymounsLogResponse(AnonymousLog anonymou public static MemberLogResponse toMemberLogResponse(MemberLog memberLog) { return new MemberLogResponse( memberLog.getLogId(), - memberLog.getMember().getMemberId(), + memberLog.getLogStatus(), memberLog.getRequestAt(), - memberLog.getResponseAt(), - memberLog.getRequestUrl(), - memberLog.getRequestMethod(), - memberLog.getStatusCode(), - memberLog.getCustomStatusCode() + memberLog.getMember().getNickname(), + memberLog.getServerIp(), + memberLog.getStatusCode() ); } } diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java index 31bea12e..97b495ee 100644 --- a/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java @@ -1,12 +1,12 @@ package clap.server.application.port.inbound.log; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.time.LocalDateTime; public interface CreateAnonymousLogsUsecase { - void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogTypeEnum logType, String customCode, String requestBody, String nicknameFromRequestBody); + void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogStatus logType, String customCode, String requestBody, String nicknameFromRequestBody); } diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java index c85b0108..503f3ec1 100644 --- a/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java @@ -1,6 +1,6 @@ package clap.server.application.port.inbound.log; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -9,5 +9,5 @@ public interface CreateMemberLogsUsecase { void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Long userId); + LocalDateTime responseAt, LogStatus logType, String customCode, String body, Long userId); } diff --git a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java index 08ead2a0..5c07b921 100644 --- a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java @@ -1,8 +1,8 @@ package clap.server.application.port.inbound.log; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.ApiLog; import java.util.List; diff --git a/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java index 50fd2e3a..29a24935 100644 --- a/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java +++ b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java @@ -1,7 +1,7 @@ package clap.server.application.service.log; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.application.port.inbound.log.CreateAnonymousLogsUsecase; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.AnonymousLog; @@ -18,7 +18,7 @@ public class CreateAnonymousLogsService implements CreateAnonymousLogsUsecase { private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; @Override - public void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, String nickName) { + public void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogStatus logType, String customCode, String body, String nickName) { AnonymousLog anonymousLog = AnonymousLog.createAnonymousLog(request, response, result, responseAt, logType, customCode, body, nickName); apiLogPersistenceAdapter.saveAnonymousLog(anonymousLog); } diff --git a/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java index 75989cac..e869180c 100644 --- a/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java +++ b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java @@ -1,11 +1,10 @@ package clap.server.application.service.log; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.application.port.inbound.domain.MemberService; import clap.server.application.port.inbound.log.CreateMemberLogsUsecase; import clap.server.common.annotation.architecture.ApplicationService; -import clap.server.domain.model.log.ApiLog; import clap.server.domain.model.log.MemberLog; import clap.server.domain.model.member.Member; import jakarta.servlet.http.HttpServletRequest; @@ -25,7 +24,7 @@ public class CreateMemberLogsService implements CreateMemberLogsUsecase { @Override @Transactional public void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Long userId) { + LocalDateTime responseAt, LogStatus logType, String customCode, String body, Long userId) { Member member = memberService.findById(userId); MemberLog memberLog = MemberLog.createMemberLog(request, response, result, responseAt, logType, customCode, body, member); apiLogPersistenceAdapter.saveMemberLog(memberLog); diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index d91c2aec..f244369f 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -1,14 +1,13 @@ package clap.server.application.service.log; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; import clap.server.application.mapper.response.LogMapper; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.ApiLog; -import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index 573dc5c8..edb08383 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -2,7 +2,7 @@ import clap.server.adapter.inbound.security.SecurityUserDetails; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.application.port.inbound.log.CreateAnonymousLogsUsecase; import clap.server.application.port.inbound.log.CreateMemberLogsUsecase; import clap.server.config.annotation.LogType; @@ -56,18 +56,20 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { } finally { LocalDateTime responseAt = LocalDateTime.now(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - LogTypeEnum logType = getLogType(methodSignature); + LogStatus logType = getLogType(methodSignature); String customCode = getCustomCode(response); - if (LogTypeEnum.LOGIN.equals(logType)) { - createAnonymousLogsUsecase.createAnonymousLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); - } else if (LogTypeEnum.GENERAL.equals(logType)) { - if (!isUserAuthenticated()) { - log.error("로그를 기록할 수 없음"); - }else{ - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (principal instanceof SecurityUserDetails userDetails) { - createMemberLogsUsecase.createMemberLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), userDetails.getUserId()); + if (logType != null) { + if (LogStatus.LOGIN.equals(logType)) { + createAnonymousLogsUsecase.createAnonymousLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); + } else { + if (!isUserAuthenticated()) { + log.error("로그인 시도 로그를 기록할 수 없음"); + } else { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (principal instanceof SecurityUserDetails userDetails) { + createMemberLogsUsecase.createMemberLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), userDetails.getUserId()); + } } } } @@ -75,11 +77,11 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { return result; } - private LogTypeEnum getLogType(MethodSignature methodSignature) { + private LogStatus getLogType(MethodSignature methodSignature) { if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { - return LogTypeEnum.fromDescription(methodSignature.getMethod().getAnnotation(LogType.class).value()); + return LogStatus.valueOf(methodSignature.getMethod().getAnnotation(LogType.class).value()); } else { - throw new IllegalArgumentException("Log 추적이 허용되지 않은 엔드포인트"); + return null; } } diff --git a/src/main/java/clap/server/domain/model/log/AnonymousLog.java b/src/main/java/clap/server/domain/model/log/AnonymousLog.java index d30ba204..4ce270f7 100644 --- a/src/main/java/clap/server/domain/model/log/AnonymousLog.java +++ b/src/main/java/clap/server/domain/model/log/AnonymousLog.java @@ -1,7 +1,6 @@ package clap.server.domain.model.log; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; -import clap.server.domain.model.member.Member; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; @@ -17,7 +16,7 @@ public class AnonymousLog extends ApiLog { private String loginNickname; public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, String nickName) { + LocalDateTime responseAt, LogStatus logStatus, String customCode, String body, String nickName) { return AnonymousLog.builder() .serverIp("127.0.0.1") .clientIp(request.getRemoteAddr()) @@ -29,7 +28,7 @@ public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpSe .response(result != null ? result.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) .responseAt(responseAt) - .logType(logType) + .logStatus(logStatus) .loginNickname(nickName) .build(); } diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 5b54a59b..938a07ae 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -1,6 +1,6 @@ package clap.server.domain.model.log; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.domain.model.common.BaseTime; import lombok.*; import lombok.experimental.SuperBuilder; @@ -20,8 +20,8 @@ public class ApiLog extends BaseTime { private String response; private String requestUrl; private String clientIp; - private String customStatusCode; private String serverIp; + private String customStatusCode; private String requestMethod; - private LogTypeEnum logType; + private LogStatus logStatus; } diff --git a/src/main/java/clap/server/domain/model/log/MemberLog.java b/src/main/java/clap/server/domain/model/log/MemberLog.java index bd4ee3f0..4819a3c5 100644 --- a/src/main/java/clap/server/domain/model/log/MemberLog.java +++ b/src/main/java/clap/server/domain/model/log/MemberLog.java @@ -1,10 +1,7 @@ package clap.server.domain.model.log; -import clap.server.adapter.outbound.persistense.entity.log.constant.LogTypeEnum; -import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.domain.model.member.Member; -import clap.server.domain.model.task.Category; -import clap.server.domain.model.task.Task; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; @@ -20,7 +17,7 @@ public class MemberLog extends ApiLog { private Member member; public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogTypeEnum logType, String customCode, String body, Member member) { + LocalDateTime responseAt, LogStatus logStatus, String customCode, String body, Member member) { return MemberLog.builder() .serverIp("127.0.0.1") //TODO: 실제 서버 ip 동적 주입 .clientIp(request.getRemoteAddr()) @@ -32,7 +29,7 @@ public static MemberLog createMemberLog(HttpServletRequest request, HttpServletR .response(result != null ? result.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) .responseAt(responseAt) - .logType(logType) + .logStatus(logStatus) .member(member) .build(); } diff --git a/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java b/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java index 2c2ebe13..a09e7f04 100644 --- a/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java +++ b/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java @@ -1,7 +1,7 @@ package clap.server.application.service.log; -import clap.server.adapter.inbound.web.dto.admin.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.admin.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; From d2341651023e8e43b6472d398ef0212d9e6df0e0 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 11:32:10 +0900 Subject: [PATCH 08/16] =?UTF-8?q?CLAP-81=20modify:=20=EC=95=A0=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=9E=85=EB=A0=A5=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20LogStatus=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/inbound/web/task/FindTaskHistoryController.java | 3 ++- src/main/java/clap/server/config/annotation/LogType.java | 4 +++- src/main/java/clap/server/config/aop/LoggingAspect.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java index 6dc6f929..9352bce9 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskHistoryController.java @@ -3,6 +3,7 @@ import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.task.FindTaskDetailsForManagerResponse; import clap.server.adapter.inbound.web.dto.task.response.FindTaskHistoryResponse; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.application.port.inbound.task.FindTaskHistoriesUsecase; import clap.server.common.annotation.architecture.WebAdapter; import clap.server.config.annotation.LogType; @@ -27,7 +28,7 @@ public class FindTaskHistoryController { private final FindTaskHistoriesUsecase findTaskHistoriesUsecase; - @LogType("TASK_VIEWED") + @LogType(LogStatus.TASK_VIEWED) @Operation(summary = "작업 히스토리 조회") @Secured({"ROLE_MANAGER","ROLE_USER"}) @GetMapping("/{taskId}/histories") diff --git a/src/main/java/clap/server/config/annotation/LogType.java b/src/main/java/clap/server/config/annotation/LogType.java index e8683bd9..fc39f225 100644 --- a/src/main/java/clap/server/config/annotation/LogType.java +++ b/src/main/java/clap/server/config/annotation/LogType.java @@ -1,5 +1,7 @@ package clap.server.config.annotation; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -8,5 +10,5 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogType { - String value(); + LogStatus value(); } diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index edb08383..12134b6d 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -79,7 +79,7 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { private LogStatus getLogType(MethodSignature methodSignature) { if (methodSignature.getMethod().isAnnotationPresent(LogType.class)) { - return LogStatus.valueOf(methodSignature.getMethod().getAnnotation(LogType.class).value()); + return methodSignature.getMethod().getAnnotation(LogType.class).value(); } else { return null; } From f62131b2632390186e5259904b451d581b69c833 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 13:04:18 +0900 Subject: [PATCH 09/16] =?UTF-8?q?CLAP-81=20Add:=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EB=A1=9C=EA=B7=B8=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/dto/log/MemberLogRequest.java | 19 +++ .../inbound/web/log/LogController.java | 24 ++- .../persistense/ApiLogPersistenceAdapter.java | 16 +- .../log/MemberLogCustomRepository.java | 10 ++ .../log/MemberLogCustomRepositoryImpl.java | 59 +++++++ .../repository/log/MemberLogRepository.java | 2 +- .../task/TaskCustomRepositoryImpl.java | 74 +++++---- .../port/inbound/log/FindApiLogsUsecase.java | 5 +- .../port/outbound/log/LoadLogPort.java | 7 +- .../service/log/FindApiLogsService.java | 13 +- .../service/log/FindApiLogsServiceTest.java | 148 ------------------ 11 files changed, 170 insertions(+), 207 deletions(-) create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java delete mode 100644 src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java new file mode 100644 index 00000000..3b938c1c --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java @@ -0,0 +1,19 @@ +package clap.server.adapter.inbound.web.dto.log; + +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +import java.util.List; + +public record MemberLogRequest( + @Schema(description = "검색 기간 (단위: 시간)", example = "1, 24, 168, 730, 2190 (1시간, 24시간, 1주일, 1개월, 3개월)") + Integer term, + @NotNull + List logStatus, + @NotNull + String nickName, + @NotNull + String ipAddress + ) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java index 247b635c..fbc17c13 100644 --- a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java @@ -1,14 +1,19 @@ package clap.server.adapter.inbound.web.log; +import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.WebAdapter; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; import java.util.List; @@ -18,17 +23,22 @@ @RequiredArgsConstructor public class LogController { - private final FindApiLogsUsecase getApiLogsUseCase; + private final FindApiLogsUsecase findApiLogsUsecase; @Secured({"ROLE_ADMIN"}) @GetMapping("/login") public List getLoginAttempts() { - return getApiLogsUseCase.getAnonymousLogs(); + return findApiLogsUsecase.getAnonymousLogs(); } @Secured({"ROLE_ADMIN"}) @GetMapping("/general") - public List getApiCalls() { - return getApiLogsUseCase.getMemberLogs(); + public Page getApiCalls( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int pageSize, + @ModelAttribute MemberLogRequest memberLogRequest, + @AuthenticationPrincipal SecurityUserDetails userInfo) { + Pageable pageable = PageRequest.of(page, pageSize); + return findApiLogsUsecase.filterMemberLogs(memberLogRequest, pageable); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index 3187c7c6..3a2b75cb 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -1,13 +1,15 @@ package clap.server.adapter.outbound.persistense; -import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.mapper.ApiLogPersistenceMapper; import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository; import clap.server.adapter.outbound.persistense.repository.log.ApiLogRepository; import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository; +import clap.server.application.mapper.response.LogMapper; import clap.server.application.port.outbound.log.CommandLogPort; import clap.server.application.port.outbound.log.LoadLogPort; import clap.server.common.annotation.architecture.PersistenceAdapter; @@ -16,6 +18,8 @@ import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.List; @@ -55,9 +59,9 @@ public List findAnonymousLogs() { } @Override - public List findMemberLogs() { - return memberLogRepository.findAll().stream() - .map(apiLogPersistenceMapper::mapMemberLogEntityToDomain) - .toList(); + public Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) { + Page memberLogs = memberLogRepository.filterMemberLogs(memberLogRequest, pageable) + .map(apiLogPersistenceMapper::mapMemberLogEntityToDomain); + return memberLogs.map(LogMapper::toMemberLogResponse); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java new file mode 100644 index 00000000..68998db5 --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java @@ -0,0 +1,10 @@ +package clap.server.adapter.outbound.persistense.repository.log; + +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface MemberLogCustomRepository { + Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java new file mode 100644 index 00000000..bc7f18bc --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java @@ -0,0 +1,59 @@ +package clap.server.adapter.outbound.persistense.repository.log; + + +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +import static clap.server.adapter.outbound.persistense.entity.log.QMemberLogEntity.memberLogEntity; +import static clap.server.adapter.outbound.persistense.entity.member.QMemberEntity.memberEntity; + + +@Repository +@RequiredArgsConstructor +public class MemberLogCustomRepositoryImpl implements MemberLogCustomRepository{ + + private final JPAQueryFactory queryFactory; + + @Override + public Page filterMemberLogs(MemberLogRequest request, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder(); + + if (request.term() != null) { + LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term()); + builder.and(memberLogEntity.createdAt.after(fromDate)); + } + if (request.logStatus().isEmpty()) { + builder.and(memberLogEntity.logStatus.in(request.logStatus())); + } + if (!request.nickName().isEmpty()) { + builder.and(memberEntity.nickname.contains(request.nickName())); + } + if (!request.ipAddress().isEmpty()) { + builder.and(memberLogEntity.serverIp.eq(request.ipAddress())); + } + + List result = queryFactory + .selectFrom(memberLogEntity) + .where(builder) + .leftJoin(memberLogEntity.member, memberEntity) + .orderBy(memberLogEntity.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + long total = queryFactory + .selectFrom(memberLogEntity) + .where(builder) + .fetch().size(); + return new PageImpl<>(result, pageable, total); + } +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java index a65ac57f..8f6500c9 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java @@ -7,5 +7,5 @@ import java.util.List; @Repository -public interface MemberLogRepository extends JpaRepository { +public interface MemberLogRepository extends JpaRepository, MemberLogCustomRepository { } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java index 991efd1d..f02ddd55 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java @@ -9,8 +9,9 @@ import com.querydsl.core.types.dsl.DateTimePath; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; @@ -19,7 +20,6 @@ import static clap.server.adapter.outbound.persistense.entity.task.QTaskEntity.taskEntity; import static com.querydsl.core.types.Order.*; -@Slf4j @Repository @RequiredArgsConstructor public class TaskCustomRepositoryImpl implements TaskCustomRepository { @@ -28,13 +28,13 @@ public class TaskCustomRepositoryImpl implements TaskCustomRepository { @Override public Page findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest filterTaskListRequest) { - BooleanBuilder whereClause = createFilter(filterTaskListRequest); + BooleanBuilder builder = createFilter(filterTaskListRequest); if (!filterTaskListRequest.nickName().isEmpty()) { - whereClause.and(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName())); + builder.and(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName())); } - whereClause.and(taskEntity.requester.memberId.eq(requesterId)); + builder.and(taskEntity.requester.memberId.eq(requesterId)); - return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); + return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); } @Override @@ -50,32 +50,32 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa @Override public Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) { - BooleanBuilder whereClause = createFilter(filterTaskListRequest); + BooleanBuilder builder = createFilter(filterTaskListRequest); if (!filterTaskListRequest.nickName().isEmpty()) { - whereClause.and(taskEntity.requester.nickname.eq(filterTaskListRequest.nickName())); + builder.and(taskEntity.requester.nickname.eq(filterTaskListRequest.nickName())); } - whereClause.and(taskEntity.taskStatus.eq(TaskStatus.REQUESTED)); - return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); + builder.and(taskEntity.taskStatus.eq(TaskStatus.REQUESTED)); + return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); } @Override public Page findAllTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) { - BooleanBuilder whereClause = createFilter(filterTaskListRequest); + BooleanBuilder builder = createFilter(filterTaskListRequest); if (!filterTaskListRequest.nickName().isEmpty()) { - whereClause.and( + builder.and( taskEntity.requester.nickname.eq(filterTaskListRequest.nickName()) .or(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName())) ); } - return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); + return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection()); } @Override public List findTasksByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable) { - BooleanBuilder whereClause = createTaskBoardFilter(processorId, statuses, untilDateTime, request); + BooleanBuilder builder = createTaskBoardFilter(processorId, statuses, untilDateTime, request); return queryFactory .selectFrom(taskEntity) - .where(whereClause) + .where(builder) .orderBy(taskEntity.processorOrder.asc()) .limit(pageable.getPageSize() + 1) .offset(pageable.getOffset()) @@ -83,69 +83,67 @@ public List findTasksByFilter(Long processorId, List sta } private BooleanBuilder createTaskBoardFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request) { - BooleanBuilder whereClause = new BooleanBuilder(); + BooleanBuilder builder = new BooleanBuilder(); - whereClause.and(taskEntity.processor.memberId.eq(processorId)); - whereClause.and(taskEntity.taskStatus.in(statuses)); - whereClause.and(taskEntity.finishedAt.isNull().or(taskEntity.finishedAt.loe(untilDateTime))); + builder.and(taskEntity.processor.memberId.eq(processorId)); + builder.and(taskEntity.taskStatus.in(statuses)); + builder.and(taskEntity.finishedAt.isNull().or(taskEntity.finishedAt.loe(untilDateTime))); if (request.labelId() != null) { - whereClause.and(taskEntity.label.labelId.eq(request.labelId())); + builder.and(taskEntity.label.labelId.eq(request.labelId())); } if (request.mainCategoryId() != null) { - whereClause.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId())); + builder.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId())); } if (request.subCategoryId() != null) { - whereClause.and(taskEntity.category.categoryId.eq(request.subCategoryId())); + builder.and(taskEntity.category.categoryId.eq(request.subCategoryId())); } if (request.title() != null && !request.title().isEmpty()) { String titleFilter = "%" + request.title() + "%"; - whereClause.and(taskEntity.title.like(titleFilter)); + builder.and(taskEntity.title.like(titleFilter)); } if (request.requesterNickname() != null && !request.requesterNickname().isEmpty()) { String nicknameFilter = "%" + request.requesterNickname().toLowerCase() + "%"; - whereClause.and(taskEntity.requester.nickname.lower().like(nicknameFilter)); + builder.and(taskEntity.requester.nickname.lower().like(nicknameFilter)); } - - return whereClause; + return builder; } private BooleanBuilder createFilter(FilterTaskListRequest request) { - BooleanBuilder whereClause = new BooleanBuilder(); + BooleanBuilder builder = new BooleanBuilder(); if (request.term() != null) { LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term()); - whereClause.and(taskEntity.createdAt.after(fromDate)); + builder.and(taskEntity.createdAt.after(fromDate)); } if (!request.categoryIds().isEmpty()) { - whereClause.and(taskEntity.category.categoryId.in(request.categoryIds())); + builder.and(taskEntity.category.categoryId.in(request.categoryIds())); } if (!request.mainCategoryIds().isEmpty()) { - whereClause.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds())); + builder.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds())); } if (!request.title().isEmpty()) { - whereClause.and(taskEntity.title.containsIgnoreCase(request.title())); + builder.and(taskEntity.title.containsIgnoreCase(request.title())); } if (!request.taskStatus().isEmpty()) { - whereClause.and(taskEntity.taskStatus.in(request.taskStatus())); + builder.and(taskEntity.taskStatus.in(request.taskStatus())); } - return whereClause; + return builder; } - - private Page getTasksPage(Pageable pageable, BooleanBuilder whereClause, String sortBy, String sortDirection) { + private Page getTasksPage(Pageable pageable, BooleanBuilder builder, String sortBy, String sortDirection) { OrderSpecifier orderSpecifier = getOrderSpecifier(sortBy, sortDirection); List result = queryFactory .selectFrom(taskEntity) - .where(whereClause) + .where(builder) .orderBy(orderSpecifier) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); long total = queryFactory .selectFrom(taskEntity) - .where(whereClause) + .where(builder) .fetch().size(); return new PageImpl<>(result, pageable, total); } diff --git a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java index 5c07b921..7c4e42f8 100644 --- a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java @@ -2,13 +2,16 @@ import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.ApiLog; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.List; public interface FindApiLogsUsecase { List getAnonymousLogs(); - List getMemberLogs(); List getApiLogs(); + Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java index 1c338084..728e2f2b 100644 --- a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java @@ -1,13 +1,18 @@ package clap.server.application.port.outbound.log; +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; import clap.server.domain.model.log.MemberLog; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import java.util.List; public interface LoadLogPort { List findAllLogs(); List findAnonymousLogs(); - List findMemberLogs(); + + Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); } diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index f244369f..bbfe36fc 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -1,14 +1,19 @@ package clap.server.application.service.log; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; import clap.server.application.mapper.response.LogMapper; import clap.server.application.port.inbound.domain.LoginDomainService; import clap.server.application.port.inbound.log.FindApiLogsUsecase; +import clap.server.application.port.outbound.log.LoadLogPort; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -20,6 +25,7 @@ public class FindApiLogsService implements FindApiLogsUsecase { private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; private final LoginDomainService loginDomainService; + private final LoadLogPort loadLogPort; @Override public List getAnonymousLogs() { @@ -31,12 +37,9 @@ public List getAnonymousLogs() { .toList(); } - //TODO: Paging으로 수정 @Override - public List getMemberLogs() { - return apiLogPersistenceAdapter.findMemberLogs().stream() - .map(LogMapper::toMemberLogResponse) - .toList(); + public Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) { + return loadLogPort.filterMemberLogs(memberLogRequest, pageable); } //테스트용 diff --git a/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java b/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java deleted file mode 100644 index a09e7f04..00000000 --- a/src/test/java/clap/server/application/service/log/FindApiLogsServiceTest.java +++ /dev/null @@ -1,148 +0,0 @@ -package clap.server.application.service.log; - -import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; -import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; -import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; -import clap.server.adapter.outbound.persistense.entity.log.constant.ApiHttpMethod; -import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; -import clap.server.application.port.inbound.domain.LoginDomainService; -import clap.server.domain.model.log.ApiLog; -import org.junit.jupiter.api.Test; - -import java.time.LocalDateTime; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - -class FindApiLogsServiceTest { - - private final ApiLogPersistenceAdapter apiLogPersistenceAdapter = mock(ApiLogPersistenceAdapter.class); - private final LoginDomainService loginDomainService = mock(LoginDomainService.class); - private final FindApiLogsService findApiLogsService = new FindApiLogsService(apiLogPersistenceAdapter, loginDomainService); - - @Test - void getMemberLogs_ShouldReturnConvertedResponses() { - // MemberEntity 생성 - MemberEntity mockMemberEntity = MemberEntity.builder() - .memberId(5L) // 정확한 memberId 설정 - .build(); - - // MemberLogEntity 생성 - MemberLogEntity mockEntity = MemberLogEntity.builder() - .logId(2L) - .member(mockMemberEntity) // member 필드에 mockMemberEntity 설정 - .requestAt(LocalDateTime.now()) - .responseAt(LocalDateTime.now()) - .requestUrl("/api/data") - .requestMethod(ApiHttpMethod.GET) - .statusCode(200) - .customStatusCode("SUCCESS") - .build(); - - // 디버깅: mockEntity 생성 확인 - System.out.println("DEBUG: Created mockEntity - Member ID: " + mockEntity.getMember().getMemberId()); - - // Mock 리포지토리 설정 - when(apiLogPersistenceAdapter.findMemberLogs()).thenReturn(List.of(mockEntity)); - - // 테스트 실행 - List responses = findApiLogsService.getMemberLogs(); - - // 디버깅: 반환된 responses 확인 - // 결과 검증 - assertThat(responses).hasSize(1); // 리스트 크기 확인 - MemberLogResponse response = responses.get(0); - assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 - assertThat(response.memberId()).isEqualTo(5L); // memberId 검증 - verify(apiLogPersistenceAdapter, times(1)).findMemberLogs(); // 리포지토리 호출 검증 - } - - @Test - void getAnonymousLogs_ShouldReturnConvertedResponses() { - // AnonymousLogEntity 생성 - AnonymousLogEntity mockEntity = AnonymousLogEntity.builder() - .logId(1L) - .loginNickname("testUser") - .requestAt(LocalDateTime.now()) - .responseAt(LocalDateTime.now()) - .requestUrl("/api/login") - .requestMethod(ApiHttpMethod.POST) - .statusCode(200) - .customStatusCode("OK") - .build(); - - // 디버깅: mockEntity 생성 확인 - System.out.println("DEBUG: Created mockEntity - Login Nickname: " + mockEntity.getLoginNickname()); - - // Mock 서비스 설정 - when(apiLogPersistenceAdapter.findAnonymousLogs("로그인 시도")).thenReturn(List.of(mockEntity)); - when(loginDomainService.getFailedAttemptCount("testUser")).thenReturn(3); // 실패 시도 수 설정 - - // 테스트 실행 - List responses = findApiLogsService.getAnonymousLogs(); - - // 디버깅: 반환된 responses 확인 - System.out.println("DEBUG: Received responses: " + responses); - - // 결과 검증 - assertThat(responses).hasSize(1); // 리스트 크기 확인 - AnonymousLogResponse response = responses.get(0); - assertThat(response.logId()).isEqualTo(mockEntity.getLogId()); // logId 검증 - assertThat(response.failedAttempts()).isEqualTo(3); // 실패 시도 검증 - verify(apiLogPersistenceAdapter, times(1)).findAnonymousLogs("로그인 시도"); - verify(loginDomainService, times(1)).getFailedAttemptCount("testUser"); - } - - @Test - void getAllLogs_ShouldReturnAllLogs() { - // AnonymousLogEntity 생성 - AnonymousLogEntity anonymousLogEntity = AnonymousLogEntity.builder() - .logId(1L) - .loginNickname("testUser") - .requestAt(LocalDateTime.now()) - .responseAt(LocalDateTime.now()) - .requestUrl("/api/login") - .requestMethod(ApiHttpMethod.POST) - .statusCode(200) - .customStatusCode("OK") - .build(); - - // MemberLogEntity 생성 - MemberEntity mockMemberEntity = MemberEntity.builder() - .memberId(5L) - .build(); - - MemberLogEntity memberLogEntity = MemberLogEntity.builder() - .logId(2L) - .member(mockMemberEntity) - .requestAt(LocalDateTime.now()) - .responseAt(LocalDateTime.now()) - .requestUrl("/api/data") - .requestMethod(ApiHttpMethod.GET) - .statusCode(200) - .customStatusCode("SUCCESS") - .build(); - - // 디버깅: mockEntities 생성 확인 - System.out.println("DEBUG: Created anonymousLogEntity - Login Nickname: " + anonymousLogEntity.getLoginNickname()); - System.out.println("DEBUG: Created memberLogEntity - Member ID: " + memberLogEntity.getMember().getMemberId()); - - // Mock 리포지토리 설정 - when(apiLogPersistenceAdapter.findAllLogs()).thenReturn(List.of(anonymousLogEntity.toDomain(), memberLogEntity.toDomain())); - - // 테스트 실행 - List logs = findApiLogsService.getApiLogs(); - - // 디버깅: 반환된 logs 확인 - System.out.println("DEBUG: Received logs: " + logs); - - // 결과 검증 - assertThat(logs).hasSize(2); - assertThat(logs.get(0).getLogId()).isEqualTo(1L); // AnonymousLogEntity 검증 - assertThat(logs.get(1).getLogId()).isEqualTo(2L); // MemberLogEntity 검증 - verify(apiLogPersistenceAdapter, times(1)).findAllLogs(); - } -} From a9f6ee807f4e6831bfb023216c381e9fa2dff513 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 13:26:43 +0900 Subject: [PATCH 10/16] =?UTF-8?q?CLAP-81=20Add:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B8=B0=EB=A1=9D=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...rLogRequest.java => FilterLogRequest.java} | 2 +- .../inbound/web/log/LogController.java | 14 +++-- .../persistense/ApiLogPersistenceAdapter.java | 21 ++++---- .../log/AnonymousLogCustomRepository.java | 10 ++++ .../log/AnonymousLogCustomRepositoryImpl.java | 54 +++++++++++++++++++ .../log/AnonymousLogRepository.java | 7 ++- .../log/MemberLogCustomRepository.java | 4 +- .../log/MemberLogCustomRepositoryImpl.java | 6 +-- .../port/inbound/log/FindApiLogsUsecase.java | 6 +-- .../port/outbound/log/LoadLogPort.java | 8 +-- .../service/log/FindApiLogsService.java | 19 ++++--- 11 files changed, 111 insertions(+), 40 deletions(-) rename src/main/java/clap/server/adapter/inbound/web/dto/log/{MemberLogRequest.java => FilterLogRequest.java} (94%) create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepository.java create mode 100644 src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java similarity index 94% rename from src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java rename to src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java index 3b938c1c..380a589c 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java @@ -6,7 +6,7 @@ import java.util.List; -public record MemberLogRequest( +public record FilterLogRequest( @Schema(description = "검색 기간 (단위: 시간)", example = "1, 24, 168, 730, 2190 (1시간, 24시간, 1주일, 1개월, 3개월)") Integer term, @NotNull diff --git a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java index fbc17c13..bbb1f0d3 100644 --- a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java @@ -2,9 +2,8 @@ import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; -import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.common.annotation.architecture.WebAdapter; import lombok.RequiredArgsConstructor; @@ -27,8 +26,13 @@ public class LogController { @Secured({"ROLE_ADMIN"}) @GetMapping("/login") - public List getLoginAttempts() { - return findApiLogsUsecase.getAnonymousLogs(); + public Page getLoginAttempts( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int pageSize, + @ModelAttribute FilterLogRequest anonymousLogRequest, + @AuthenticationPrincipal SecurityUserDetails userInfo) { + Pageable pageable = PageRequest.of(page, pageSize); + return findApiLogsUsecase.filterAnonymousLogs(anonymousLogRequest, pageable); } @Secured({"ROLE_ADMIN"}) @@ -36,7 +40,7 @@ public List getLoginAttempts() { public Page getApiCalls( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int pageSize, - @ModelAttribute MemberLogRequest memberLogRequest, + @ModelAttribute FilterLogRequest memberLogRequest, @AuthenticationPrincipal SecurityUserDetails userInfo) { Pageable pageable = PageRequest.of(page, pageSize); return findApiLogsUsecase.filterMemberLogs(memberLogRequest, pageable); diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index 3a2b75cb..6c45529e 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -1,9 +1,9 @@ package clap.server.adapter.outbound.persistense; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; -import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import clap.server.adapter.outbound.persistense.mapper.ApiLogPersistenceMapper; import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository; @@ -40,7 +40,7 @@ public void saveMemberLog(MemberLog memberLog) { } @Override - public void saveAnonymousLog(AnonymousLog anonymousLog){ + public void saveAnonymousLog(AnonymousLog anonymousLog) { apiLogRepository.save(apiLogPersistenceMapper.mapAnonymousLogToEntity(anonymousLog, anonymousLog.getLoginNickname())); } @@ -52,16 +52,15 @@ public List findAllLogs() { } @Override - public List findAnonymousLogs() { - return anonymousLogRepository.findAll().stream() - .map(apiLogPersistenceMapper::mapAnonymousLogEntityToDomain) - .toList(); - } - - @Override - public Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) { + public Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { Page memberLogs = memberLogRepository.filterMemberLogs(memberLogRequest, pageable) .map(apiLogPersistenceMapper::mapMemberLogEntityToDomain); return memberLogs.map(LogMapper::toMemberLogResponse); } + + @Override + public Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable) { + return anonymousLogRepository.filterAnonymousLogs(anonymousLogRequest, pageable) + .map(apiLogPersistenceMapper::mapAnonymousLogEntityToDomain); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepository.java new file mode 100644 index 00000000..580959ff --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepository.java @@ -0,0 +1,10 @@ +package clap.server.adapter.outbound.persistense.repository.log; + +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface AnonymousLogCustomRepository { + Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable); +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java new file mode 100644 index 00000000..901f699b --- /dev/null +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java @@ -0,0 +1,54 @@ +package clap.server.adapter.outbound.persistense.repository.log; + +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; +import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +import static clap.server.adapter.outbound.persistense.entity.log.QAnonymousLogEntity.anonymousLogEntity; + +@Repository +@RequiredArgsConstructor +public class AnonymousLogCustomRepositoryImpl implements AnonymousLogCustomRepository{ + + private final JPAQueryFactory queryFactory; + @Override + public Page filterAnonymousLogs(FilterLogRequest request, Pageable pageable) { + BooleanBuilder builder = new BooleanBuilder(); + + if (request.term() != null) { + LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term()); + builder.and(anonymousLogEntity.createdAt.after(fromDate)); + } + if (!request.logStatus().isEmpty()) { + builder.and(anonymousLogEntity.logStatus.in(request.logStatus())); + } + if (!request.nickName().isEmpty()) { + builder.and(anonymousLogEntity.loginNickname.contains(request.nickName())); + } + if (!request.ipAddress().isEmpty()) { + builder.and(anonymousLogEntity.serverIp.eq(request.ipAddress())); + } + + List result = queryFactory + .selectFrom(anonymousLogEntity) + .where(builder) + .orderBy(anonymousLogEntity.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + long total = queryFactory + .selectFrom(anonymousLogEntity) + .where(builder) + .fetch().size(); + return new PageImpl<>(result, pageable, total); + } +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java index 273aa26d..65b684c6 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogRepository.java @@ -1,11 +1,16 @@ package clap.server.adapter.outbound.persistense.repository.log; +import aj.org.objectweb.asm.commons.Remapper; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity; +import clap.server.domain.model.log.AnonymousLog; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository -public interface AnonymousLogRepository extends JpaRepository { +public interface AnonymousLogRepository extends JpaRepository, AnonymousLogCustomRepository{ } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java index 68998db5..4ed6242f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepository.java @@ -1,10 +1,10 @@ package clap.server.adapter.outbound.persistense.repository.log; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface MemberLogCustomRepository { - Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); + Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java index bc7f18bc..fe78d0ee 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java @@ -1,7 +1,7 @@ package clap.server.adapter.outbound.persistense.repository.log; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity; import com.querydsl.core.BooleanBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -25,14 +25,14 @@ public class MemberLogCustomRepositoryImpl implements MemberLogCustomRepository{ private final JPAQueryFactory queryFactory; @Override - public Page filterMemberLogs(MemberLogRequest request, Pageable pageable) { + public Page filterMemberLogs(FilterLogRequest request, Pageable pageable) { BooleanBuilder builder = new BooleanBuilder(); if (request.term() != null) { LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term()); builder.and(memberLogEntity.createdAt.after(fromDate)); } - if (request.logStatus().isEmpty()) { + if (!request.logStatus().isEmpty()) { builder.and(memberLogEntity.logStatus.in(request.logStatus())); } if (!request.nickName().isEmpty()) { diff --git a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java index 7c4e42f8..2cf08c59 100644 --- a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java @@ -2,7 +2,7 @@ import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.ApiLog; import org.springframework.data.domain.Page; @@ -11,7 +11,7 @@ import java.util.List; public interface FindApiLogsUsecase { - List getAnonymousLogs(); + Page filterAnonymousLogs(FilterLogRequest anonymousLogsRequest, Pageable pageable); + Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); List getApiLogs(); - Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java index 728e2f2b..f27bbdb6 100644 --- a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java @@ -1,10 +1,10 @@ package clap.server.application.port.outbound.log; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; -import clap.server.domain.model.log.MemberLog; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -12,7 +12,7 @@ public interface LoadLogPort { List findAllLogs(); - List findAnonymousLogs(); + Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable); - Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable); + Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); } diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index bbfe36fc..af9032ea 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -1,7 +1,7 @@ package clap.server.application.service.log; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; -import clap.server.adapter.inbound.web.dto.log.MemberLogRequest; +import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter; import clap.server.application.mapper.response.LogMapper; @@ -9,8 +9,8 @@ import clap.server.application.port.inbound.log.FindApiLogsUsecase; import clap.server.application.port.outbound.log.LoadLogPort; import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; -import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -28,17 +28,16 @@ public class FindApiLogsService implements FindApiLogsUsecase { private final LoadLogPort loadLogPort; @Override - public List getAnonymousLogs() { - return apiLogPersistenceAdapter.findAnonymousLogs().stream() - .map(anonymousLog -> { - int failedAttempts = loginDomainService.getFailedAttemptCount(anonymousLog.getLoginNickname()); - return LogMapper.toAnonymounsLogResponse(anonymousLog, failedAttempts); - }) - .toList(); + public Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable) { + Page anonymousLogs = loadLogPort.filterAnonymousLogs(anonymousLogRequest, pageable); + return anonymousLogs.map(anonymousLog -> { + int failedAttempts = loginDomainService.getFailedAttemptCount(anonymousLog.getLoginNickname()); + return LogMapper.toAnonymounsLogResponse(anonymousLog, failedAttempts); + }); } @Override - public Page filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) { + public Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { return loadLogPort.filterMemberLogs(memberLogRequest, pageable); } From 3dff83775d60c1121321ac92b7b1b40e14da3649 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 15:45:46 +0900 Subject: [PATCH 11/16] =?UTF-8?q?CLAP-81=20Refactor:=20PageResponse?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/dto/common/PageResponse.java | 27 +++++++++++++++++++ .../inbound/web/log/LogController.java | 10 ++++--- .../persistense/ApiLogPersistenceAdapter.java | 5 ++-- .../port/inbound/log/FindApiLogsUsecase.java | 5 ++-- .../port/outbound/log/LoadLogPort.java | 3 ++- .../service/log/FindApiLogsService.java | 13 ++++++--- 6 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/common/PageResponse.java diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/common/PageResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/common/PageResponse.java new file mode 100644 index 00000000..7ecf8697 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/common/PageResponse.java @@ -0,0 +1,27 @@ +package clap.server.adapter.inbound.web.dto.common; + +import org.springframework.data.domain.Page; + +import java.util.List; + +public record PageResponse( + List content, + long totalElements, + int totalPages, + int pageNumber, + int pageSize, + boolean isFirst, + boolean isLast +) { + public static PageResponse from(Page page) { + return new PageResponse<>( + page.getContent(), + page.getTotalElements(), + page.getTotalPages(), + page.getNumber() + 1, + page.getSize(), + page.isFirst(), + page.isLast() + ); + } +} \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java index bbb1f0d3..66ba981d 100644 --- a/src/main/java/clap/server/adapter/inbound/web/log/LogController.java +++ b/src/main/java/clap/server/adapter/inbound/web/log/LogController.java @@ -1,6 +1,7 @@ package clap.server.adapter.inbound.web.log; import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.common.PageResponse; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; @@ -10,6 +11,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -26,23 +28,23 @@ public class LogController { @Secured({"ROLE_ADMIN"}) @GetMapping("/login") - public Page getLoginAttempts( + public ResponseEntity> getLoginAttempts( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int pageSize, @ModelAttribute FilterLogRequest anonymousLogRequest, @AuthenticationPrincipal SecurityUserDetails userInfo) { Pageable pageable = PageRequest.of(page, pageSize); - return findApiLogsUsecase.filterAnonymousLogs(anonymousLogRequest, pageable); + return ResponseEntity.ok(findApiLogsUsecase.filterAnonymousLogs(anonymousLogRequest, pageable)); } @Secured({"ROLE_ADMIN"}) @GetMapping("/general") - public Page getApiCalls( + public ResponseEntity> getApiCalls( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int pageSize, @ModelAttribute FilterLogRequest memberLogRequest, @AuthenticationPrincipal SecurityUserDetails userInfo) { Pageable pageable = PageRequest.of(page, pageSize); - return findApiLogsUsecase.filterMemberLogs(memberLogRequest, pageable); + return ResponseEntity.ok(findApiLogsUsecase.filterMemberLogs(memberLogRequest, pageable)); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java index 6c45529e..dd3d9f25 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/ApiLogPersistenceAdapter.java @@ -52,10 +52,9 @@ public List findAllLogs() { } @Override - public Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { - Page memberLogs = memberLogRepository.filterMemberLogs(memberLogRequest, pageable) + public Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { + return memberLogRepository.filterMemberLogs(memberLogRequest, pageable) .map(apiLogPersistenceMapper::mapMemberLogEntityToDomain); - return memberLogs.map(LogMapper::toMemberLogResponse); } @Override diff --git a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java index 2cf08c59..35c7f644 100644 --- a/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java @@ -1,6 +1,7 @@ package clap.server.application.port.inbound.log; +import clap.server.adapter.inbound.web.dto.common.PageResponse; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; @@ -11,7 +12,7 @@ import java.util.List; public interface FindApiLogsUsecase { - Page filterAnonymousLogs(FilterLogRequest anonymousLogsRequest, Pageable pageable); - Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); + PageResponse filterAnonymousLogs(FilterLogRequest anonymousLogsRequest, Pageable pageable); + PageResponse filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); List getApiLogs(); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java index f27bbdb6..4ee39ae2 100644 --- a/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java +++ b/src/main/java/clap/server/application/port/outbound/log/LoadLogPort.java @@ -5,6 +5,7 @@ import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -14,5 +15,5 @@ public interface LoadLogPort { List findAllLogs(); Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable); - Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); + Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable); } diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index af9032ea..90d2de77 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -1,5 +1,6 @@ package clap.server.application.service.log; +import clap.server.adapter.inbound.web.dto.common.PageResponse; import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse; import clap.server.adapter.inbound.web.dto.log.FilterLogRequest; import clap.server.adapter.inbound.web.dto.log.MemberLogResponse; @@ -11,6 +12,7 @@ import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.log.AnonymousLog; import clap.server.domain.model.log.ApiLog; +import clap.server.domain.model.log.MemberLog; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -28,17 +30,20 @@ public class FindApiLogsService implements FindApiLogsUsecase { private final LoadLogPort loadLogPort; @Override - public Page filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable) { + public PageResponse filterAnonymousLogs(FilterLogRequest anonymousLogRequest, Pageable pageable) { Page anonymousLogs = loadLogPort.filterAnonymousLogs(anonymousLogRequest, pageable); - return anonymousLogs.map(anonymousLog -> { + Page anonymousLogResponses = anonymousLogs.map(anonymousLog -> { int failedAttempts = loginDomainService.getFailedAttemptCount(anonymousLog.getLoginNickname()); return LogMapper.toAnonymounsLogResponse(anonymousLog, failedAttempts); }); + return PageResponse.from(anonymousLogResponses); } @Override - public Page filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { - return loadLogPort.filterMemberLogs(memberLogRequest, pageable); + public PageResponse filterMemberLogs(FilterLogRequest memberLogRequest, Pageable pageable) { + Page memberLogs = loadLogPort.filterMemberLogs(memberLogRequest, pageable); + Page memberLogResponses = memberLogs.map(LogMapper::toMemberLogResponse); + return PageResponse.from(memberLogResponses); } //테스트용 From 126baa89d90d101b3c14d3f0562944d35f68f2f1 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 18:44:23 +0900 Subject: [PATCH 12/16] =?UTF-8?q?CLAP-81=20Fix:=20server=20ip=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/dto/log/AnonymousLogResponse.java | 11 +++----- .../inbound/web/dto/log/FilterLogRequest.java | 2 +- .../web/dto/log/MemberLogResponse.java | 2 +- .../persistense/entity/log/ApiLogEntity.java | 7 ++---- .../mapper/ApiLogPersistenceMapper.java | 25 ++++++++----------- .../log/AnonymousLogCustomRepositoryImpl.java | 4 +-- .../log/MemberLogCustomRepositoryImpl.java | 4 +-- .../mapper/response/LogMapper.java | 11 ++++---- .../service/log/FindApiLogsService.java | 2 +- .../server/domain/model/log/AnonymousLog.java | 9 +++---- .../clap/server/domain/model/log/ApiLog.java | 5 ++-- .../server/domain/model/log/MemberLog.java | 9 +++---- 12 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java index e3439a40..bc74da6b 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/AnonymousLogResponse.java @@ -1,5 +1,6 @@ package clap.server.adapter.inbound.web.dto.log; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -8,16 +9,12 @@ public record AnonymousLogResponse( @NotBlank Long logId, - @NotBlank - String loginNickname, + LogStatus logStatus, @NotBlank LocalDateTime requestAt, @NotBlank - LocalDateTime responseAt, - @NotBlank - String requestUrl, - @NotBlank - String requestMethod, + String nickName, + String clientIp, @NotBlank Integer statusCode, @NotNull diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java index 380a589c..65a9a70c 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/FilterLogRequest.java @@ -14,6 +14,6 @@ public record FilterLogRequest( @NotNull String nickName, @NotNull - String ipAddress + String clientIp ) { } diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java index f9cb8183..950695e6 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java @@ -13,7 +13,7 @@ public record MemberLogResponse( @NotBlank LocalDateTime requestAt, String nickName, - String serverIp, + String clientIp, @NotBlank Integer statusCode ) { diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index 658bc09f..f9638c34 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -23,9 +23,6 @@ public abstract class ApiLogEntity extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long logId; - @Column(nullable = false) - private String serverIp; - @Column(nullable = false) private String clientIp; @@ -43,10 +40,10 @@ public abstract class ApiLogEntity extends BaseTimeEntity { private String customStatusCode; @Column(length = 4096, nullable = false) - private String request; + private String requestBody; @Column(length = 4096, nullable = false) - private String response; + private String responseBody; @Column(nullable = false) private LocalDateTime requestAt; diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java index 427cbcea..494901e5 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java @@ -17,14 +17,13 @@ public class ApiLogPersistenceMapper { private final MemberPersistenceMapper memberPersistenceMapper; public AnonymousLogEntity mapAnonymousLogToEntity(ApiLog anonymousLog, String nickName) { return AnonymousLogEntity.builder() - .serverIp(anonymousLog.getServerIp()) .clientIp(anonymousLog.getClientIp()) .requestUrl(anonymousLog.getRequestUrl()) .requestMethod(ApiHttpMethod.valueOf(anonymousLog.getRequestMethod())) .statusCode(anonymousLog.getStatusCode()) .customStatusCode(anonymousLog.getCustomStatusCode()) - .request(anonymousLog.getRequest()) - .response(anonymousLog.getResponse()) + .requestBody(anonymousLog.getRequestBody()) + .responseBody(anonymousLog.getResponseBody()) .requestAt(anonymousLog.getRequestAt()) .responseAt(anonymousLog.getResponseAt()) .loginNickname(nickName != null ? nickName : "UNKNOWN") @@ -35,14 +34,13 @@ public AnonymousLogEntity mapAnonymousLogToEntity(ApiLog anonymousLog, String ni public MemberLogEntity mapMemberLogToEntity(MemberLog memberLog, MemberEntity memberEntity) { return MemberLogEntity.builder() .member(memberEntity) - .serverIp(memberLog.getServerIp()) .clientIp(memberLog.getClientIp()) .requestUrl(memberLog.getRequestUrl()) .requestMethod(ApiHttpMethod.valueOf(memberLog.getRequestMethod())) .statusCode(memberLog.getStatusCode()) .customStatusCode(memberLog.getCustomStatusCode()) - .request(memberLog.getRequest()) - .response(memberLog.getResponse()) + .requestBody(memberLog.getRequestBody()) + .responseBody(memberLog.getRequestBody()) .requestAt(memberLog.getRequestAt()) .responseAt(memberLog.getResponseAt()) .logStatus(memberLog.getLogStatus()) @@ -52,14 +50,13 @@ public MemberLogEntity mapMemberLogToEntity(MemberLog memberLog, MemberEntity me public AnonymousLog mapAnonymousLogEntityToDomain(AnonymousLogEntity anonymousLogEntity) { return AnonymousLog.builder() .logId(anonymousLogEntity.getLogId()) - .serverIp(anonymousLogEntity.getServerIp()) .clientIp(anonymousLogEntity.getClientIp()) .requestUrl(anonymousLogEntity.getRequestUrl()) .requestMethod(anonymousLogEntity.getRequestMethod().name()) .statusCode(anonymousLogEntity.getStatusCode()) .customStatusCode(anonymousLogEntity.getCustomStatusCode()) - .request(anonymousLogEntity.getRequest()) - .response(anonymousLogEntity.getResponse()) + .requestBody(anonymousLogEntity.getRequestBody()) + .responseBody(anonymousLogEntity.getResponseBody()) .requestAt(anonymousLogEntity.getRequestAt()) .responseAt(anonymousLogEntity.getResponseAt()) .logStatus(anonymousLogEntity.getLogStatus()) @@ -70,14 +67,13 @@ public AnonymousLog mapAnonymousLogEntityToDomain(AnonymousLogEntity anonymousLo public MemberLog mapMemberLogEntityToDomain(MemberLogEntity memberLogEntity) { return MemberLog.builder() .logId(memberLogEntity.getLogId()) - .serverIp(memberLogEntity.getServerIp()) .clientIp(memberLogEntity.getClientIp()) .requestUrl(memberLogEntity.getRequestUrl()) .requestMethod(memberLogEntity.getRequestMethod().name()) .statusCode(memberLogEntity.getStatusCode()) .customStatusCode(memberLogEntity.getCustomStatusCode()) - .request(memberLogEntity.getRequest()) - .response(memberLogEntity.getResponse()) + .requestBody(memberLogEntity.getRequestBody()) + .responseBody(memberLogEntity.getResponseBody()) .requestAt(memberLogEntity.getRequestAt()) .responseAt(memberLogEntity.getResponseAt()) .logStatus(memberLogEntity.getLogStatus()) @@ -90,14 +86,13 @@ public MemberLog mapMemberLogEntityToDomain(MemberLogEntity memberLogEntity) { public ApiLog mapLogEntityToDomain(ApiLogEntity logEntity) { return ApiLog.builder() .logId(logEntity.getLogId()) - .serverIp(logEntity.getServerIp()) .clientIp(logEntity.getClientIp()) .requestUrl(logEntity.getRequestUrl()) .requestMethod(logEntity.getRequestMethod().name()) .statusCode(logEntity.getStatusCode()) .customStatusCode(logEntity.getCustomStatusCode()) - .request(logEntity.getRequest()) - .response(logEntity.getResponse()) + .requestBody(logEntity.getRequestBody()) + .responseBody(logEntity.getResponseBody()) .requestAt(logEntity.getRequestAt()) .responseAt(logEntity.getResponseAt()) .logStatus(logEntity.getLogStatus()) diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java index 901f699b..62058480 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/AnonymousLogCustomRepositoryImpl.java @@ -34,8 +34,8 @@ public Page filterAnonymousLogs(FilterLogRequest request, Pa if (!request.nickName().isEmpty()) { builder.and(anonymousLogEntity.loginNickname.contains(request.nickName())); } - if (!request.ipAddress().isEmpty()) { - builder.and(anonymousLogEntity.serverIp.eq(request.ipAddress())); + if (!request.clientIp().isEmpty()) { + builder.and(anonymousLogEntity.clientIp.eq(request.clientIp())); } List result = queryFactory diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java index fe78d0ee..7eebfff9 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogCustomRepositoryImpl.java @@ -38,8 +38,8 @@ public Page filterMemberLogs(FilterLogRequest request, Pageable if (!request.nickName().isEmpty()) { builder.and(memberEntity.nickname.contains(request.nickName())); } - if (!request.ipAddress().isEmpty()) { - builder.and(memberLogEntity.serverIp.eq(request.ipAddress())); + if (!request.clientIp().isEmpty()) { + builder.and(memberLogEntity.clientIp.eq(request.clientIp())); } List result = queryFactory diff --git a/src/main/java/clap/server/application/mapper/response/LogMapper.java b/src/main/java/clap/server/application/mapper/response/LogMapper.java index d86cfad6..b5759cf4 100644 --- a/src/main/java/clap/server/application/mapper/response/LogMapper.java +++ b/src/main/java/clap/server/application/mapper/response/LogMapper.java @@ -6,14 +6,13 @@ import clap.server.domain.model.log.MemberLog; public class LogMapper { - public static AnonymousLogResponse toAnonymounsLogResponse(AnonymousLog anonymousLog, int failedAttempts) { + public static AnonymousLogResponse toAnonymousLogResponse(AnonymousLog anonymousLog, int failedAttempts) { return new AnonymousLogResponse( anonymousLog.getLogId(), - anonymousLog.getLoginNickname(), + anonymousLog.getLogStatus(), anonymousLog.getRequestAt(), - anonymousLog.getResponseAt(), - anonymousLog.getRequestUrl(), - anonymousLog.getRequestMethod(), + anonymousLog.getLoginNickname(), + anonymousLog.getClientIp(), anonymousLog.getStatusCode(), anonymousLog.getCustomStatusCode(), failedAttempts @@ -25,7 +24,7 @@ public static MemberLogResponse toMemberLogResponse(MemberLog memberLog) { memberLog.getLogStatus(), memberLog.getRequestAt(), memberLog.getMember().getNickname(), - memberLog.getServerIp(), + memberLog.getClientIp(), memberLog.getStatusCode() ); } diff --git a/src/main/java/clap/server/application/service/log/FindApiLogsService.java b/src/main/java/clap/server/application/service/log/FindApiLogsService.java index 90d2de77..0798c598 100644 --- a/src/main/java/clap/server/application/service/log/FindApiLogsService.java +++ b/src/main/java/clap/server/application/service/log/FindApiLogsService.java @@ -34,7 +34,7 @@ public PageResponse filterAnonymousLogs(FilterLogRequest a Page anonymousLogs = loadLogPort.filterAnonymousLogs(anonymousLogRequest, pageable); Page anonymousLogResponses = anonymousLogs.map(anonymousLog -> { int failedAttempts = loginDomainService.getFailedAttemptCount(anonymousLog.getLoginNickname()); - return LogMapper.toAnonymounsLogResponse(anonymousLog, failedAttempts); + return LogMapper.toAnonymousLogResponse(anonymousLog, failedAttempts); }); return PageResponse.from(anonymousLogResponses); } diff --git a/src/main/java/clap/server/domain/model/log/AnonymousLog.java b/src/main/java/clap/server/domain/model/log/AnonymousLog.java index 4ce270f7..6aeb044a 100644 --- a/src/main/java/clap/server/domain/model/log/AnonymousLog.java +++ b/src/main/java/clap/server/domain/model/log/AnonymousLog.java @@ -15,17 +15,16 @@ public class AnonymousLog extends ApiLog { private String loginNickname; - public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogStatus logStatus, String customCode, String body, String nickName) { + public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, + LocalDateTime responseAt, LogStatus logStatus, String customCode, String requestBody, String nickName) { return AnonymousLog.builder() - .serverIp("127.0.0.1") .clientIp(request.getRemoteAddr()) .requestUrl(request.getRequestURI()) .requestMethod(request.getMethod()) .statusCode(response.getStatus()) .customStatusCode(customCode) - .request(body) - .response(result != null ? result.toString() : "UNKNOWN") + .requestBody(requestBody) + .responseBody(responseResult != null ? responseResult.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) .responseAt(responseAt) .logStatus(logStatus) diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 938a07ae..5e359fd0 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -16,11 +16,10 @@ public class ApiLog extends BaseTime { private Integer statusCode; private LocalDateTime requestAt; private LocalDateTime responseAt; - private String request; - private String response; + private String requestBody; + private String responseBody; private String requestUrl; private String clientIp; - private String serverIp; private String customStatusCode; private String requestMethod; private LogStatus logStatus; diff --git a/src/main/java/clap/server/domain/model/log/MemberLog.java b/src/main/java/clap/server/domain/model/log/MemberLog.java index 4819a3c5..bdc208bf 100644 --- a/src/main/java/clap/server/domain/model/log/MemberLog.java +++ b/src/main/java/clap/server/domain/model/log/MemberLog.java @@ -16,17 +16,16 @@ public class MemberLog extends ApiLog { private Member member; - public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogStatus logStatus, String customCode, String body, Member member) { + public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, + LocalDateTime responseAt, LogStatus logStatus, String customCode, String requestBody, Member member) { return MemberLog.builder() - .serverIp("127.0.0.1") //TODO: 실제 서버 ip 동적 주입 .clientIp(request.getRemoteAddr()) .requestUrl(request.getRequestURI()) .requestMethod(request.getMethod()) .statusCode(response.getStatus()) .customStatusCode(customCode) - .request(body) - .response(result != null ? result.toString() : "UNKNOWN") + .requestBody(requestBody) + .responseBody(responseResult != null ? responseResult.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) .responseAt(responseAt) .logStatus(logStatus) From 92c35905292ce97021f31088b23c40c79b6ec3f4 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 19:07:27 +0900 Subject: [PATCH 13/16] =?UTF-8?q?CLAP-81=20Fix:=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=9C=20saveComment=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../outbound/persistense/CommentPersistenceAdapter.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java index b50ca262..aec21821 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java @@ -29,10 +29,4 @@ public Comment saveComment(Comment comment) { CommentEntity commentEntity = commentRepository.save(commentPersistenceMapper.toEntity(comment)); return commentPersistenceMapper.toDomain(commentEntity); } - - @Override - public Comment saveComment(Comment comment) { - CommentEntity commentEntity = commentRepository.save(commentPersistenceMapper.toEntity(comment)); - return commentPersistenceMapper.toDomain(commentEntity); - } } From 65b9b6dab619bb56f2f40dee86fba81cf377d7e7 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 19:34:59 +0900 Subject: [PATCH 14/16] =?UTF-8?q?CLAP-81=20Fix:=20api=5Flog=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B6=88=ED=95=84=EC=9A=94=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inbound/web/dto/log/MemberLogResponse.java | 2 +- .../persistense/entity/log/AnonymousLogEntity.java | 2 +- .../outbound/persistense/entity/log/ApiLogEntity.java | 3 --- .../persistense/mapper/ApiLogPersistenceMapper.java | 7 +------ .../port/inbound/log/CreateAnonymousLogsUsecase.java | 2 +- .../port/inbound/log/CreateMemberLogsUsecase.java | 3 +-- .../service/log/CreateAnonymousLogsService.java | 4 ++-- .../service/log/CreateMemberLogsService.java | 5 ++--- .../java/clap/server/config/aop/LoggingAspect.java | 5 ++--- .../clap/server/domain/model/log/AnonymousLog.java | 4 +--- .../java/clap/server/domain/model/log/ApiLog.java | 11 +++++------ .../java/clap/server/domain/model/log/MemberLog.java | 4 +--- 12 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java index 950695e6..1c499ce7 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/log/MemberLogResponse.java @@ -11,7 +11,7 @@ public record MemberLogResponse( Long logId, LogStatus logStatus, @NotBlank - LocalDateTime requestAt, + LocalDateTime responseAt, String nickName, String clientIp, @NotBlank diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java index 91b566c7..efb24ac7 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/AnonymousLogEntity.java @@ -15,6 +15,6 @@ @SuperBuilder public class AnonymousLogEntity extends ApiLogEntity { - @Column(nullable = false) + @Column private String loginNickname; } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java index f9638c34..cbeda0fd 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/log/ApiLogEntity.java @@ -48,9 +48,6 @@ public abstract class ApiLogEntity extends BaseTimeEntity { @Column(nullable = false) private LocalDateTime requestAt; - @Column(nullable = false) - private LocalDateTime responseAt; - @Column(nullable = false) @Enumerated(EnumType.STRING) private LogStatus logStatus; diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java index 494901e5..1b986eb1 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/ApiLogPersistenceMapper.java @@ -25,7 +25,6 @@ public AnonymousLogEntity mapAnonymousLogToEntity(ApiLog anonymousLog, String ni .requestBody(anonymousLog.getRequestBody()) .responseBody(anonymousLog.getResponseBody()) .requestAt(anonymousLog.getRequestAt()) - .responseAt(anonymousLog.getResponseAt()) .loginNickname(nickName != null ? nickName : "UNKNOWN") .logStatus(anonymousLog.getLogStatus()) .build(); @@ -40,9 +39,8 @@ public MemberLogEntity mapMemberLogToEntity(MemberLog memberLog, MemberEntity me .statusCode(memberLog.getStatusCode()) .customStatusCode(memberLog.getCustomStatusCode()) .requestBody(memberLog.getRequestBody()) - .responseBody(memberLog.getRequestBody()) + .responseBody(memberLog.getResponseBody()) .requestAt(memberLog.getRequestAt()) - .responseAt(memberLog.getResponseAt()) .logStatus(memberLog.getLogStatus()) .build(); } @@ -58,7 +56,6 @@ public AnonymousLog mapAnonymousLogEntityToDomain(AnonymousLogEntity anonymousLo .requestBody(anonymousLogEntity.getRequestBody()) .responseBody(anonymousLogEntity.getResponseBody()) .requestAt(anonymousLogEntity.getRequestAt()) - .responseAt(anonymousLogEntity.getResponseAt()) .logStatus(anonymousLogEntity.getLogStatus()) .loginNickname(anonymousLogEntity.getLoginNickname()) .build(); @@ -75,7 +72,6 @@ public MemberLog mapMemberLogEntityToDomain(MemberLogEntity memberLogEntity) { .requestBody(memberLogEntity.getRequestBody()) .responseBody(memberLogEntity.getResponseBody()) .requestAt(memberLogEntity.getRequestAt()) - .responseAt(memberLogEntity.getResponseAt()) .logStatus(memberLogEntity.getLogStatus()) .member(memberLogEntity.getMember() != null ? memberPersistenceMapper.toDomain(memberLogEntity.getMember()) @@ -94,7 +90,6 @@ public ApiLog mapLogEntityToDomain(ApiLogEntity logEntity) { .requestBody(logEntity.getRequestBody()) .responseBody(logEntity.getResponseBody()) .requestAt(logEntity.getRequestAt()) - .responseAt(logEntity.getResponseAt()) .logStatus(logEntity.getLogStatus()) .build(); } diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java index 97b495ee..e21c6f5c 100644 --- a/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/CreateAnonymousLogsUsecase.java @@ -7,6 +7,6 @@ import java.time.LocalDateTime; public interface CreateAnonymousLogsUsecase { - void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogStatus logType, String customCode, String requestBody, String nicknameFromRequestBody); + void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LogStatus logType, String customCode, String requestBody, String nicknameFromRequestBody); } diff --git a/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java index 503f3ec1..b6870930 100644 --- a/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/log/CreateMemberLogsUsecase.java @@ -8,6 +8,5 @@ public interface CreateMemberLogsUsecase { - void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogStatus logType, String customCode, String body, Long userId); + void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, LogStatus logType, String customCode, String body, Long userId); } diff --git a/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java index 29a24935..f2152d64 100644 --- a/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java +++ b/src/main/java/clap/server/application/service/log/CreateAnonymousLogsService.java @@ -18,8 +18,8 @@ public class CreateAnonymousLogsService implements CreateAnonymousLogsUsecase { private final ApiLogPersistenceAdapter apiLogPersistenceAdapter; @Override - public void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LocalDateTime responseAt, LogStatus logType, String customCode, String body, String nickName) { - AnonymousLog anonymousLog = AnonymousLog.createAnonymousLog(request, response, result, responseAt, logType, customCode, body, nickName); + public void createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object result, LogStatus logType, String customCode, String body, String nickName) { + AnonymousLog anonymousLog = AnonymousLog.createAnonymousLog(request, response, result, logType, customCode, body, nickName); apiLogPersistenceAdapter.saveAnonymousLog(anonymousLog); } } diff --git a/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java index e869180c..9026e8f5 100644 --- a/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java +++ b/src/main/java/clap/server/application/service/log/CreateMemberLogsService.java @@ -23,10 +23,9 @@ public class CreateMemberLogsService implements CreateMemberLogsUsecase { @Override @Transactional - public void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, - LocalDateTime responseAt, LogStatus logType, String customCode, String body, Long userId) { + public void createMemberLog(HttpServletRequest request, HttpServletResponse response, Object result, LogStatus logType, String customCode, String body, Long userId) { Member member = memberService.findById(userId); - MemberLog memberLog = MemberLog.createMemberLog(request, response, result, responseAt, logType, customCode, body, member); + MemberLog memberLog = MemberLog.createMemberLog(request, response, result, logType, customCode, body, member); apiLogPersistenceAdapter.saveMemberLog(memberLog); } } diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index 12134b6d..617adccf 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -54,21 +54,20 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { try { result = joinPoint.proceed(); } finally { - LocalDateTime responseAt = LocalDateTime.now(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); LogStatus logType = getLogType(methodSignature); String customCode = getCustomCode(response); if (logType != null) { if (LogStatus.LOGIN.equals(logType)) { - createAnonymousLogsUsecase.createAnonymousLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); + createAnonymousLogsUsecase.createAnonymousLog(request, response, result, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); } else { if (!isUserAuthenticated()) { log.error("로그인 시도 로그를 기록할 수 없음"); } else { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof SecurityUserDetails userDetails) { - createMemberLogsUsecase.createMemberLog(request, response, result, responseAt, logType, customCode, getRequestBody(request), userDetails.getUserId()); + createMemberLogsUsecase.createMemberLog(request, response, result, logType, customCode, getRequestBody(request), userDetails.getUserId()); } } } diff --git a/src/main/java/clap/server/domain/model/log/AnonymousLog.java b/src/main/java/clap/server/domain/model/log/AnonymousLog.java index 6aeb044a..5599059e 100644 --- a/src/main/java/clap/server/domain/model/log/AnonymousLog.java +++ b/src/main/java/clap/server/domain/model/log/AnonymousLog.java @@ -15,8 +15,7 @@ public class AnonymousLog extends ApiLog { private String loginNickname; - public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, - LocalDateTime responseAt, LogStatus logStatus, String customCode, String requestBody, String nickName) { + public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, LogStatus logStatus, String customCode, String requestBody, String nickName) { return AnonymousLog.builder() .clientIp(request.getRemoteAddr()) .requestUrl(request.getRequestURI()) @@ -26,7 +25,6 @@ public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpSe .requestBody(requestBody) .responseBody(responseResult != null ? responseResult.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) - .responseAt(responseAt) .logStatus(logStatus) .loginNickname(nickName) .build(); diff --git a/src/main/java/clap/server/domain/model/log/ApiLog.java b/src/main/java/clap/server/domain/model/log/ApiLog.java index 5e359fd0..061b5764 100644 --- a/src/main/java/clap/server/domain/model/log/ApiLog.java +++ b/src/main/java/clap/server/domain/model/log/ApiLog.java @@ -13,14 +13,13 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ApiLog extends BaseTime { private Long logId; + private String clientIp; + private String requestUrl; + private String requestMethod; private Integer statusCode; - private LocalDateTime requestAt; - private LocalDateTime responseAt; + private String customStatusCode; private String requestBody; private String responseBody; - private String requestUrl; - private String clientIp; - private String customStatusCode; - private String requestMethod; + private LocalDateTime requestAt; private LogStatus logStatus; } diff --git a/src/main/java/clap/server/domain/model/log/MemberLog.java b/src/main/java/clap/server/domain/model/log/MemberLog.java index bdc208bf..33039fa5 100644 --- a/src/main/java/clap/server/domain/model/log/MemberLog.java +++ b/src/main/java/clap/server/domain/model/log/MemberLog.java @@ -16,8 +16,7 @@ public class MemberLog extends ApiLog { private Member member; - public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, - LocalDateTime responseAt, LogStatus logStatus, String customCode, String requestBody, Member member) { + public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, LogStatus logStatus, String customCode, String requestBody, Member member) { return MemberLog.builder() .clientIp(request.getRemoteAddr()) .requestUrl(request.getRequestURI()) @@ -27,7 +26,6 @@ public static MemberLog createMemberLog(HttpServletRequest request, HttpServletR .requestBody(requestBody) .responseBody(responseResult != null ? responseResult.toString() : "UNKNOWN") .requestAt(LocalDateTime.now()) - .responseAt(responseAt) .logStatus(logStatus) .member(member) .build(); From cb5b3c209773bfeaae274f212ee6e7a9130beaf3 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Fri, 31 Jan 2025 20:01:01 +0900 Subject: [PATCH 15/16] =?UTF-8?q?CLAP-81=20Fix:=20ClientIpParseUtil=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20ipv4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../clap/server/adapter/inbound/web/auth/AuthController.java | 3 +++ src/main/java/clap/server/config/aop/LoggingAspect.java | 5 ++++- src/main/java/clap/server/domain/model/log/AnonymousLog.java | 5 +++-- src/main/java/clap/server/domain/model/log/MemberLog.java | 3 ++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/auth/AuthController.java b/src/main/java/clap/server/adapter/inbound/web/auth/AuthController.java index 79d26333..ae60c027 100644 --- a/src/main/java/clap/server/adapter/inbound/web/auth/AuthController.java +++ b/src/main/java/clap/server/adapter/inbound/web/auth/AuthController.java @@ -3,9 +3,11 @@ import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.auth.LoginRequest; import clap.server.adapter.inbound.web.dto.auth.LoginResponse; +import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; import clap.server.application.port.inbound.auth.LoginUsecase; import clap.server.application.port.inbound.auth.LogoutUsecase; import clap.server.common.annotation.architecture.WebAdapter; +import clap.server.config.annotation.LogType; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -27,6 +29,7 @@ public class AuthController { private final LoginUsecase loginUsecase; private final LogoutUsecase logoutUsecase; + @LogType(LogStatus.LOGIN) @Operation(summary = "로그인 API") @PostMapping("/login") public ResponseEntity login(@RequestHeader(name = "sessionId") String sessionId, diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index 617adccf..36c00fed 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -57,7 +57,7 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); LogStatus logType = getLogType(methodSignature); String customCode = getCustomCode(response); - + //TODO: ipv4 형식으로 변환 if (logType != null) { if (LogStatus.LOGIN.equals(logType)) { createAnonymousLogsUsecase.createAnonymousLog(request, response, result, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); @@ -84,11 +84,13 @@ private LogStatus getLogType(MethodSignature methodSignature) { } } + //TODO: 로그인 시도에 대한 에러 잡도록 수정 private String getCustomCode(HttpServletResponse response) { String customCode = ErrorContext.getCustomCode(); return customCode != null ? customCode : "CUSTOM" + (response != null ? response.getStatus() : 500); } + //TODO: 로그인 시도 시 닉네임 파싱하도록 수정 private String getNicknameFromRequestBody(HttpServletRequest request) { try { String requestBody = getRequestBody(request); @@ -99,6 +101,7 @@ private String getNicknameFromRequestBody(HttpServletRequest request) { } } + //TODO: 제거 private String getRequestBody(HttpServletRequest request) { try { ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request; diff --git a/src/main/java/clap/server/domain/model/log/AnonymousLog.java b/src/main/java/clap/server/domain/model/log/AnonymousLog.java index 5599059e..c0324740 100644 --- a/src/main/java/clap/server/domain/model/log/AnonymousLog.java +++ b/src/main/java/clap/server/domain/model/log/AnonymousLog.java @@ -1,6 +1,7 @@ package clap.server.domain.model.log; import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; +import clap.server.common.utils.ClientIpParseUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; @@ -17,13 +18,13 @@ public class AnonymousLog extends ApiLog { public static AnonymousLog createAnonymousLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, LogStatus logStatus, String customCode, String requestBody, String nickName) { return AnonymousLog.builder() - .clientIp(request.getRemoteAddr()) + .clientIp(ClientIpParseUtil.getClientIp(request)) .requestUrl(request.getRequestURI()) .requestMethod(request.getMethod()) .statusCode(response.getStatus()) .customStatusCode(customCode) .requestBody(requestBody) - .responseBody(responseResult != null ? responseResult.toString() : "UNKNOWN") + .responseBody(responseResult != null ? responseResult.toString() : "로그인 실패") .requestAt(LocalDateTime.now()) .logStatus(logStatus) .loginNickname(nickName) diff --git a/src/main/java/clap/server/domain/model/log/MemberLog.java b/src/main/java/clap/server/domain/model/log/MemberLog.java index 33039fa5..566887d9 100644 --- a/src/main/java/clap/server/domain/model/log/MemberLog.java +++ b/src/main/java/clap/server/domain/model/log/MemberLog.java @@ -1,6 +1,7 @@ package clap.server.domain.model.log; import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus; +import clap.server.common.utils.ClientIpParseUtil; import clap.server.domain.model.member.Member; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -18,7 +19,7 @@ public class MemberLog extends ApiLog { public static MemberLog createMemberLog(HttpServletRequest request, HttpServletResponse response, Object responseResult, LogStatus logStatus, String customCode, String requestBody, Member member) { return MemberLog.builder() - .clientIp(request.getRemoteAddr()) + .clientIp(ClientIpParseUtil.getClientIp(request)) .requestUrl(request.getRequestURI()) .requestMethod(request.getMethod()) .statusCode(response.getStatus()) From e28e8507fc91be5adf6ab9a1fdef43bb6f80a8a7 Mon Sep 17 00:00:00 2001 From: parkjaehak Date: Sat, 1 Feb 2025 08:45:27 +0900 Subject: [PATCH 16/16] =?UTF-8?q?CLAP-184=20Add:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=20=EC=8B=9C=20response=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=A1=9C=EA=B9=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/clap/server/config/aop/LoggingAspect.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/clap/server/config/aop/LoggingAspect.java b/src/main/java/clap/server/config/aop/LoggingAspect.java index 36c00fed..5355d373 100644 --- a/src/main/java/clap/server/config/aop/LoggingAspect.java +++ b/src/main/java/clap/server/config/aop/LoggingAspect.java @@ -53,13 +53,21 @@ public Object logApiRequests(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; try { result = joinPoint.proceed(); + } catch (Exception ex) { + log.error("Exception occurred: {}", ex.getMessage()); + log.info("response.getStatus()={}",response.getStatus()); + log.info("getRequestBody()={}", getRequestBody(request)); + throw ex; } finally { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); LogStatus logType = getLogType(methodSignature); String customCode = getCustomCode(response); - //TODO: ipv4 형식으로 변환 if (logType != null) { if (LogStatus.LOGIN.equals(logType)) { + log.info("result={}",result); + log.info("response.getStatus()={}",response.getStatus()); + log.info("getRequestBody()={}", getRequestBody(request)); + log.info("getNicknameFromRequestBody()={}", getNicknameFromRequestBody(request)); createAnonymousLogsUsecase.createAnonymousLog(request, response, result, logType, customCode, getRequestBody(request), getNicknameFromRequestBody(request)); } else { if (!isUserAuthenticated()) {