recommend

This commit is contained in:
youbeen
2025-06-12 16:03:48 +09:00
parent 1a0d3c5268
commit ff127c1edc
10 changed files with 632 additions and 94 deletions
+54
View File
@@ -0,0 +1,54 @@
plugins {
id 'java-library'
}
description = 'Common utilities and shared components'
dependencies {
// Spring Boot Starters
api 'org.springframework.boot:spring-boot-starter-web'
api 'org.springframework.boot:spring-boot-starter-data-jpa'
api 'org.springframework.boot:spring-boot-starter-validation'
api 'org.springframework.boot:spring-boot-starter-security'
// AOP (AspectJ) - 명시적 의존성 추가
api 'org.springframework.boot:spring-boot-starter-aop'
api 'org.aspectj:aspectjweaver:1.9.21'
api 'org.aspectj:aspectjrt:1.9.21'
// JSON Processing
api 'com.fasterxml.jackson.core:jackson-databind'
api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
// JWT
api 'io.jsonwebtoken:jjwt-api:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
// Apache Commons
api 'org.apache.commons:commons-lang3:3.13.0'
api 'org.apache.commons:commons-collections4:4.4'
// OpenAPI/Swagger
api 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 테스트
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
// JAR 생성 설정 (plain jar도 생성)
jar {
archiveBaseName = 'common'
archiveVersion = '1.0.0'
enabled = true
}
// bootJar 비활성화 (라이브러리이므로 실행 가능한 JAR 불필요)
bootJar {
enabled = false
}
@@ -1,4 +1,52 @@
package com.ktds.hi.common.config;
public class AsyncConfig {
}
package com.ktds.hi.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* 비동기 처리 설정 클래스
* @Async 어노테이션을 위한 스레드 풀 설정
*
* @author 하이오더 개발팀
* @version 1.0.0
*/
@Configuration
@EnableAsync
public class AsyncConfig {
/**
* 감사 로그용 스레드 풀
*/
@Bean("auditTaskExecutor")
public Executor auditTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("audit-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.initialize();
return executor;
}
/**
* 일반적인 비동기 작업용 스레드 풀
*/
@Bean("generalTaskExecutor")
public Executor generalTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.initialize();
return executor;
}
}
@@ -1,4 +1,18 @@
package com.ktds.hi.common.config;
public class CommonAopConfig {
}
package com.ktds.hi.common.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* AOP 설정 클래스
* AspectJ 자동 프록시를 활성화
*
* @author 하이오더 개발팀
* @version 1.0.0
*/
@Configuration
@EnableAspectJAutoProxy
public class CommonAopConfig {
// AOP 자동 설정을 위한 설정 클래스
// @EnableAspectJAutoProxy 어노테이션으로 AspectJ 자동 프록시 활성화
}
@@ -1,4 +1,76 @@
package com.ktds.hi.common.service;
public class AuditAction {
}
package com.ktds.hi.common.service;
/**
* 감사 액션 열거형
* 시스템에서 발생하는 주요 액션들을 정의
*
* @author 하이오더 개발팀
* @version 1.0.0
*/
public enum AuditAction {
/**
* 생성 액션
*/
CREATE("생성"),
/**
* 수정 액션
*/
UPDATE("수정"),
/**
* 삭제 액션
*/
DELETE("삭제"),
/**
* 조회 액션
*/
ACCESS("조회"),
/**
* 로그인 액션
*/
LOGIN("로그인"),
/**
* 로그아웃 액션
*/
LOGOUT("로그아웃"),
/**
* 승인 액션
*/
APPROVE("승인"),
/**
* 거부 액션
*/
REJECT("거부"),
/**
* 활성화 액션
*/
ACTIVATE("활성화"),
/**
* 비활성화 액션
*/
DEACTIVATE("비활성화");
private final String description;
AuditAction(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return description;
}
}
@@ -1,51 +1,41 @@
package com.ktds.hi.common.service;
import com.ktds.hi.common.audit.AuditAction;
import com.ktds.hi.common.audit.AuditLog;
import com.ktds.hi.common.audit.AuditLogger;
import com.ktds.hi.common.repository.AuditLogRepository;
import com.ktds.hi.common.security.SecurityUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 감사 로그 서비스
* 시스템 중요한 액션들을 비동기적으로 로깅
* 시스템 중요한 작업들을 로깅
*
* @author 하이오더 개발팀
* @version 1.0.0
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AuditLogService {
private final AuditLogRepository auditLogRepository;
/**
* 감사 로그 기록 (비동기)
* 비동기 로그 기록
*/
@Async
@Transactional
public void logAsync(AuditAction action, String entityType, String entityId, String description) {
log(action, entityType, entityId, description);
}
/**
* 감사 로그 기록 (동기)
*/
@Transactional
public void log(AuditAction action, String entityType, String entityId, String description) {
try {
Long userId = SecurityUtil.getCurrentUserId().orElse(null);
String username = SecurityUtil.getCurrentUsername().orElse("SYSTEM");
// 현재 사용자 정보 가져오기 (SecurityContext에서)
String userId = getCurrentUserId();
String username = getCurrentUsername();
AuditLog auditLog = AuditLogger.create(userId, username, action, entityType, entityId, description);
auditLogRepository.save(auditLog);
// 감사 로그 생성 및 저장
log.info("AUDIT_LOG: action={}, entityType={}, entityId={}, userId={}, username={}, description={}",
action, entityType, entityId, userId, username, description);
// 실제 환경에서는 데이터베이스에 저장
// AuditLog auditLog = AuditLog.create(userId, username, action, entityType, entityId, description);
// auditLogRepository.save(auditLog);
} catch (Exception e) {
log.error("Failed to save audit log: action={}, entityType={}, entityId={}",
action, entityType, entityId, e);
action, entityType, entityId, e);
}
}
@@ -81,13 +71,38 @@ public class AuditLogService {
* 로그인 로그
*/
public void logLogin(String description) {
logAsync(AuditAction.LOGIN, "USER", SecurityUtil.getCurrentUserId().map(String::valueOf).orElse("UNKNOWN"), description);
logAsync(AuditAction.LOGIN, "USER", getCurrentUserId(), description);
}
/**
* 로그아웃 로그
*/
public void logLogout(String description) {
logAsync(AuditAction.LOGOUT, "USER", SecurityUtil.getCurrentUserId().map(String::valueOf).orElse("UNKNOWN"), description);
logAsync(AuditAction.LOGOUT, "USER", getCurrentUserId(), description);
}
}
/**
* 현재 사용자 ID 조회
*/
private String getCurrentUserId() {
try {
// SecurityContext에서 사용자 ID 추출
// 실제 구현에서는 SecurityContextHolder 사용
return "SYSTEM"; // 임시값
} catch (Exception e) {
return "UNKNOWN";
}
}
/**
* 현재 사용자명 조회
*/
private String getCurrentUsername() {
try {
// SecurityContext에서 사용자명 추출
return "system"; // 임시값
} catch (Exception e) {
return "unknown";
}
}
}