spring 버전 변경
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>2.7.18</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.spring</groupId>
|
||||
@@ -27,7 +27,7 @@
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.spring.common.util;
|
||||
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ApplicationUtil {
|
||||
|
||||
public static ServletRequestAttributes getServletRequestAttributes(){
|
||||
return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||
}
|
||||
//Request 객체 얻기
|
||||
public static HttpServletRequest getRequest(){
|
||||
return getServletRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
//Set Request Attribute
|
||||
public static void setRequestAttributes(String key, Object obj){
|
||||
getServletRequestAttributes().setAttribute(key, obj, RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
//Get Request Attribute
|
||||
public static Object getRequestAttributes(String key){
|
||||
return getServletRequestAttributes().getAttribute(key, RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
//Session 객체 얻기
|
||||
public static HttpSession getSession(){
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
//Set Session Attributes
|
||||
public static void setSessionAttributes(String key, Object obj){
|
||||
getServletRequestAttributes().setAttribute(key, obj, RequestAttributes.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
//Get Session Attributes
|
||||
public static Object getSessionAttributes(String key){
|
||||
return getServletRequestAttributes().getAttribute(key, RequestAttributes.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
//HttpServletResponse 객체 얻기
|
||||
public static HttpServletResponse getResponse(){
|
||||
return getServletRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
//beanName을 통해서 Bean을 얻을 수 있다.
|
||||
public static Object getBean(String beanName){
|
||||
var context = ContextLoader.getCurrentWebApplicationContext();
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("WebApplicationContext를 찾을 수 없습니다.");
|
||||
}
|
||||
return context.getBean(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.spring.common.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Spring ApplicationContext에 접근하기 위한 유틸리티 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 ApplicationContextAware를 구현하여 ApplicationContext를 저장하고,
|
||||
* 애플리케이션 전역에서 Spring 빈에 접근할 수 있는 정적 메소드를 제공합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
public class ContextUtils implements ApplicationContextAware {
|
||||
|
||||
private static final AtomicReference<ApplicationContext> applicationContext = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Spring에 의해 호출되어 ApplicationContext를 설정합니다.
|
||||
*
|
||||
* @param context 설정할 ApplicationContext
|
||||
* @throws BeansException 빈 예외 발생 시
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext context) throws BeansException {
|
||||
applicationContext.set(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 지정된 이름과 타입의 빈을 반환합니다.
|
||||
*
|
||||
* @param <T> 반환될 빈의 타입
|
||||
* @param beanName 찾을 빈의 이름
|
||||
* @param clazz 반환될 빈의 클래스
|
||||
* @return 지정된 이름과 타입의 빈
|
||||
* @throws IllegalStateException ApplicationContext가 설정되지 않은 경우
|
||||
*/
|
||||
public static <T> T getBean(String beanName, Class<T> clazz) {
|
||||
ApplicationContext context = applicationContext.get();
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("ApplicationContext가 설정되지 않았습니다.");
|
||||
}
|
||||
return context.getBean(beanName, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 지정된 타입의 빈을 반환합니다.
|
||||
*
|
||||
* @param <T> 반환될 빈의 타입
|
||||
* @param clazz 반환될 빈의 클래스
|
||||
* @return 지정된 타입의 빈
|
||||
* @throws IllegalStateException ApplicationContext가 설정되지 않은 경우
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
ApplicationContext context = applicationContext.get();
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("ApplicationContext가 설정되지 않았습니다.");
|
||||
}
|
||||
return context.getBean(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.spring.common.util;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 서블릿 관련 유틸리티 클래스입니다.
|
||||
*
|
||||
* <p>현재 요청의 HttpServletRequest, HttpServletResponse, HttpSession 등에 접근하는 메서드를 제공합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ServletUtils {
|
||||
|
||||
/**
|
||||
* 현재 요청의 ServletRequestAttributes를 Optional로 반환합니다.
|
||||
*
|
||||
* @return ServletRequestAttributes를 포함한 Optional 객체
|
||||
*/
|
||||
private static Optional<ServletRequestAttributes> getAttributes() {
|
||||
return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
|
||||
.filter(ServletRequestAttributes.class::isInstance)
|
||||
.map(ServletRequestAttributes.class::cast);
|
||||
}
|
||||
|
||||
/**
|
||||
* ServletRequestAttributes에서 특정 속성을 추출합니다.
|
||||
*
|
||||
* @param getter ServletRequestAttributes에서 원하는 속성을 추출하는 함수
|
||||
* @param <T> 반환될 속성의 타입
|
||||
* @return 추출된 속성 또는 null
|
||||
*/
|
||||
private static <T> T getAttribute(Function<ServletRequestAttributes, T> getter) {
|
||||
return getAttributes().map(getter).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 요청의 HttpServletRequest를 반환합니다.
|
||||
*
|
||||
* @return 현재 HttpServletRequest 또는 null
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return getAttribute(ServletRequestAttributes::getRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 요청의 속성을 설정합니다.
|
||||
*
|
||||
* @param key 속성의 키
|
||||
* @param obj 설정할 속성 값
|
||||
*/
|
||||
public static void setRequestAttribute(String key, Object obj) {
|
||||
getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_REQUEST));
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 요청의 속성을 반환합니다.
|
||||
*
|
||||
* @param key 속성의 키
|
||||
* @return 요청 속성 값 또는 null
|
||||
*/
|
||||
public static Object getRequestAttribute(String key) {
|
||||
return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_REQUEST));
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 요청의 HttpSession을 반환합니다.
|
||||
*
|
||||
* @return 현재 HttpSession 또는 null
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
return Optional.ofNullable(getRequest()).map(HttpServletRequest::getSession).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 세션의 속성을 설정합니다.
|
||||
*
|
||||
* @param key 속성의 키
|
||||
* @param obj 설정할 속성 값
|
||||
*/
|
||||
public static void setSessionAttribute(String key, Object obj) {
|
||||
getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_SESSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 세션의 속성을 반환합니다.
|
||||
*
|
||||
* @param key 속성의 키
|
||||
* @return 세션 속성 값 또는 null
|
||||
*/
|
||||
public static Object getSessionAttribute(String key) {
|
||||
return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_SESSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 요청의 HttpServletResponse를 반환합니다.
|
||||
*
|
||||
* @return 현재 HttpServletResponse 또는 null
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
return getAttribute(ServletRequestAttributes::getResponse);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,15 +2,14 @@ package com.spring.domain.batch.entity;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// @Entity
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.spring.domain.batch.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// @Entity
|
||||
|
||||
@@ -2,14 +2,13 @@ package com.spring.domain.batch.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -37,7 +36,7 @@ public class BatchJobExecutionParams {
|
||||
@Embeddable
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
public class BatchJobExecutionParamsId implements Serializable {
|
||||
public static class BatchJobExecutionParamsId implements Serializable {
|
||||
@Column(name = "JOB_EXECUTION_ID")
|
||||
private Long jobExecutionId;
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package com.spring.domain.batch.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// @Entity
|
||||
|
||||
@@ -2,15 +2,14 @@ package com.spring.domain.batch.entity;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// @Entity
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.spring.domain.batch.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// @Entity
|
||||
|
||||
@@ -3,45 +3,40 @@ package com.spring.domain.email.batch;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import com.spring.infra.quartz.QuartzJob;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class EmailSendBatch {
|
||||
|
||||
private final JobRepository jobRepository;
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
|
||||
@QuartzJob(name = "emailSendJob", cronExpression = "*/3 * * * * ?")
|
||||
@QuartzJob(group = "EMAIL", name = "emailSendJob", cronExpression = "*/3 * * * * ?")
|
||||
// @JobScope
|
||||
@Bean(name = "emailSendJob")
|
||||
Job emailSendJob(Step emailSendStep) {
|
||||
log.info(">>> emailSendJob");
|
||||
return new JobBuilder("emailSendJob", jobRepository)
|
||||
return new JobBuilder("emailSendJob")
|
||||
.start(emailSendStep)
|
||||
.build();
|
||||
}
|
||||
|
||||
// @StepScope
|
||||
@Bean("emailSendStep")
|
||||
Step emailSendStep(Tasklet emailSendTasklet) {
|
||||
log.info(">>> emailSendStep");
|
||||
return new StepBuilder("emailSendStep", jobRepository)
|
||||
.tasklet(emailSendTasklet, transactionManager).build();
|
||||
return new StepBuilder("emailSendStep")
|
||||
.tasklet(emailSendTasklet).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Tasklet emailSendTasklet(){
|
||||
Tasklet emailSendTasklet() {
|
||||
return ((contribution, chunkContext) -> {
|
||||
log.info(">>>>> emailSendTasklet");
|
||||
return RepeatStatus.FINISHED;
|
||||
|
||||
@@ -3,15 +3,16 @@ package com.spring.domain.email.entity;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.spring.domain.email.entity;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.spring.domain.post.entity;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.spring.infra.db.SecondaryDataSourceConfig;
|
||||
import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
|
||||
@DatabaseSelector(SecondaryDataSourceConfig.DATABASE)
|
||||
|
||||
@@ -2,13 +2,12 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinColumns;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinColumns;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@ package com.spring.domain.quartz.entity;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinColumns;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinColumns;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.spring.domain.user.api;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -11,7 +13,6 @@ import com.spring.domain.user.dto.SignInRequest;
|
||||
import com.spring.domain.user.service.AuthService;
|
||||
import com.spring.infra.security.jwt.JwtTokenService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
|
||||
@@ -2,15 +2,16 @@ package com.spring.domain.user.entity;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Entity
|
||||
|
||||
@@ -2,15 +2,16 @@ package com.spring.domain.user.entity;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Entity
|
||||
|
||||
@@ -2,14 +2,15 @@ package com.spring.domain.user.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MapsId;
|
||||
import jakarta.persistence.Table;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.spring.infra.batch;
|
||||
|
||||
public class BatchConfig {
|
||||
|
||||
|
||||
}
|
||||
@@ -11,6 +11,18 @@ import org.springframework.context.annotation.Primary;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
/**
|
||||
* 주 데이터 소스 설정을 위한 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 애플리케이션의 주 데이터 소스를 설정하며, 다음과 같은 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>주 데이터 소스 속성 설정</li>
|
||||
* <li>HikariCP를 사용한 주 데이터 소스 생성</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class PrimaryDataSourceConfig {
|
||||
|
||||
@@ -19,6 +31,14 @@ public class PrimaryDataSourceConfig {
|
||||
private static final String DATASOURCE_PROPERTIES = "primaryDataSourceProperties";
|
||||
private static final String DATASOURCE_PROPERTIES_PREFIX = "spring.datasource.primary";
|
||||
|
||||
/**
|
||||
* 주 데이터 소스의 속성을 설정합니다.
|
||||
*
|
||||
* <p>이 메소드는 'spring.datasource.primary' 접두사로 시작하는 설정을 읽어
|
||||
* DataSourceProperties 객체를 생성합니다.</p>
|
||||
*
|
||||
* @return 설정된 DataSourceProperties 객체
|
||||
*/
|
||||
@Primary
|
||||
@Bean(name = DATASOURCE_PROPERTIES)
|
||||
@ConfigurationProperties(prefix = DATASOURCE_PROPERTIES_PREFIX)
|
||||
@@ -26,6 +46,14 @@ public class PrimaryDataSourceConfig {
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* 주 데이터 소스를 생성합니다.
|
||||
*
|
||||
* <p>이 메소드는 설정된 DataSourceProperties를 사용하여 HikariCP 데이터 소스를 생성합니다.</p>
|
||||
*
|
||||
* @param properties 데이터 소스 속성
|
||||
* @return 생성된 DataSource 객체
|
||||
*/
|
||||
@Primary
|
||||
@Bean(name = DATASOURCE)
|
||||
DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) {
|
||||
|
||||
@@ -10,6 +10,18 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
/**
|
||||
* 보조 데이터 소스 설정을 위한 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 애플리케이션의 보조 데이터 소스를 설정하며, 다음과 같은 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>보조 데이터 소스 속성 설정</li>
|
||||
* <li>HikariCP를 사용한 보조 데이터 소스 생성</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class SecondaryDataSourceConfig {
|
||||
|
||||
@@ -18,12 +30,28 @@ public class SecondaryDataSourceConfig {
|
||||
private static final String DATASOURCE_PROPERTIES = "secondaryDataSourceProperties";
|
||||
private static final String DATASOURCE_PROPERTIES_PREFIX = "spring.datasource.secondary";
|
||||
|
||||
/**
|
||||
* 보조 데이터 소스의 속성을 설정합니다.
|
||||
*
|
||||
* <p>이 메소드는 'spring.datasource.secondary' 접두사로 시작하는 설정을 읽어
|
||||
* DataSourceProperties 객체를 생성합니다.</p>
|
||||
*
|
||||
* @return 설정된 DataSourceProperties 객체
|
||||
*/
|
||||
@Bean(name = DATASOURCE_PROPERTIES)
|
||||
@ConfigurationProperties(prefix = DATASOURCE_PROPERTIES_PREFIX)
|
||||
DataSourceProperties dataSourceProperties() {
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* 보조 데이터 소스를 생성합니다.
|
||||
*
|
||||
* <p>이 메소드는 설정된 DataSourceProperties를 사용하여 HikariCP 데이터 소스를 생성합니다.</p>
|
||||
*
|
||||
* @param properties 데이터 소스 속성
|
||||
* @return 생성된 DataSource 객체
|
||||
*/
|
||||
@Bean(name = DATASOURCE)
|
||||
DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) {
|
||||
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.spring.infra.db.orm.jpa;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -25,9 +26,21 @@ import com.spring.infra.db.PrimaryDataSourceConfig;
|
||||
import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector;
|
||||
import com.spring.infra.db.orm.jpa.util.EntityScanner;
|
||||
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 주 데이터베이스에 대한 JPA 설정을 담당하는 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 다음과 같은 주요 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>주 데이터베이스용 EntityManagerFactory 설정</li>
|
||||
* <li>주 데이터베이스용 TransactionManager 설정</li>
|
||||
* <li>JPA 리포지토리 활성화 및 필터링</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableJpaRepositories(
|
||||
basePackages = PrimaryJpaConfig.BASE_PACKAGE,
|
||||
@@ -46,6 +59,13 @@ public class PrimaryJpaConfig {
|
||||
private final JpaProperties jpaProperties;
|
||||
private final HibernateProperties hibernateProperties;
|
||||
|
||||
/**
|
||||
* 주 데이터베이스용 EntityManagerFactory를 생성합니다.
|
||||
*
|
||||
* @param builder EntityManagerFactory 빌더
|
||||
* @param dataSource 주 데이터 소스
|
||||
* @return 구성된 LocalContainerEntityManagerFactoryBean
|
||||
*/
|
||||
@Primary
|
||||
@Bean(name = ENTITY_MANAGER_FACTORY)
|
||||
LocalContainerEntityManagerFactoryBean entityManagerFactory(
|
||||
@@ -62,12 +82,23 @@ public class PrimaryJpaConfig {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 주 데이터베이스용 TransactionManager를 생성합니다.
|
||||
*
|
||||
* @param factory 주 EntityManagerFactory
|
||||
* @return 구성된 PlatformTransactionManager
|
||||
*/
|
||||
@Primary
|
||||
@Bean(TRANSACTION_MANAGER)
|
||||
PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) {
|
||||
return new JpaTransactionManager(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 선택을 위한 커스텀 필터 클래스입니다.
|
||||
*
|
||||
* <p>이 필터는 DatabaseSelector 어노테이션이 없는 엔티티만 선택합니다.</p>
|
||||
*/
|
||||
public static class DatabaseFilter implements TypeFilter {
|
||||
@Override
|
||||
public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.spring.infra.db.orm.jpa;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -26,9 +27,21 @@ import com.spring.infra.db.SecondaryDataSourceConfig;
|
||||
import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector;
|
||||
import com.spring.infra.db.orm.jpa.util.EntityScanner;
|
||||
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스에 대한 JPA 설정을 담당하는 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 다음과 같은 주요 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>보조 데이터베이스용 EntityManagerFactory 설정</li>
|
||||
* <li>보조 데이터베이스용 TransactionManager 설정</li>
|
||||
* <li>JPA 리포지토리 활성화 및 필터링</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableJpaRepositories(
|
||||
basePackages = SecondaryJpaConfig.BASE_PACKAGE,
|
||||
@@ -47,6 +60,13 @@ public class SecondaryJpaConfig {
|
||||
private final JpaProperties jpaProperties;
|
||||
private final HibernateProperties hibernateProperties;
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스용 EntityManagerFactory를 생성합니다.
|
||||
*
|
||||
* @param builder EntityManagerFactory 빌더
|
||||
* @param dataSource 보조 데이터 소스
|
||||
* @return 구성된 LocalContainerEntityManagerFactoryBean
|
||||
*/
|
||||
@Bean(name = ENTITY_MANAGER_FACTORY)
|
||||
LocalContainerEntityManagerFactoryBean entityManagerFactory(
|
||||
EntityManagerFactoryBuilder builder,
|
||||
@@ -62,11 +82,22 @@ public class SecondaryJpaConfig {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스용 TransactionManager를 생성합니다.
|
||||
*
|
||||
* @param factory 보조 EntityManagerFactory
|
||||
* @return 구성된 PlatformTransactionManager
|
||||
*/
|
||||
@Bean(TRANSACTION_MANAGER)
|
||||
PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) {
|
||||
return new JpaTransactionManager(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 선택을 위한 커스텀 필터 클래스입니다.
|
||||
*
|
||||
* <p>이 필터는 DatabaseSelector 어노테이션이 있고, 그 값이 보조 데이터베이스와 일치하는 엔티티만 선택합니다.</p>
|
||||
*/
|
||||
public static class DatabaseFilter implements TypeFilter {
|
||||
@Override
|
||||
public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) {
|
||||
|
||||
@@ -5,8 +5,34 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 엔티티 클래스가 사용할 데이터베이스를 지정하는 어노테이션입니다.
|
||||
*
|
||||
* <p>이 어노테이션은 JPA 엔티티 클래스에 적용되며, 해당 엔티티가 어떤 데이터베이스에서
|
||||
* 사용될지를 지정합니다. 주로 다중 데이터베이스 환경에서 엔티티를 특정 데이터베이스에
|
||||
* 매핑하는 데 사용됩니다.</p>
|
||||
*
|
||||
* <p>사용 예:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* @Entity
|
||||
* @DatabaseSelector("secondary")
|
||||
* public class MyEntity {
|
||||
* // 엔티티 필드 및 메소드
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DatabaseSelector {
|
||||
/**
|
||||
* 엔티티가 사용할 데이터베이스의 이름을 지정합니다.
|
||||
*
|
||||
* @return 데이터베이스 이름
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.spring.infra.db.orm.jpa.util;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
@@ -8,17 +10,38 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* JPA 엔티티 클래스를 스캔하는 유틸리티 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 지정된 패키지 내의 엔티티 클래스를 스캔하고,
|
||||
* 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링할 수 있습니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class EntityScanner {
|
||||
|
||||
/**
|
||||
* 지정된 기본 패키지 내의 모든 엔티티를 스캔합니다.
|
||||
*
|
||||
* @param basePackage 스캔할 기본 패키지
|
||||
* @return 스캔된 엔티티 클래스의 패키지 이름 배열
|
||||
*/
|
||||
public static String[] scanEntities(String basePackage) {
|
||||
return scanEntities(basePackage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 지정된 기본 패키지 내의 엔티티를 스캔하고, 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링합니다.
|
||||
*
|
||||
* @param basePackage 스캔할 기본 패키지
|
||||
* @param dbName 필터링할 데이터베이스 이름 (null이면 모든 엔티티 반환)
|
||||
* @return 스캔된 엔티티 클래스의 패키지 이름 배열
|
||||
*/
|
||||
public static String[] scanEntities(String basePackage, String dbName) {
|
||||
var scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||
scanner.addIncludeFilter((MetadataReader reader, MetadataReaderFactory factory) -> {
|
||||
|
||||
@@ -16,6 +16,19 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import com.spring.infra.db.PrimaryDataSourceConfig;
|
||||
import com.spring.infra.db.orm.mybatis.annotation.PrimaryMapper;
|
||||
|
||||
/**
|
||||
* 주 데이터베이스에 대한 MyBatis 설정을 담당하는 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 다음과 같은 주요 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>주 데이터베이스용 SqlSessionFactory 설정</li>
|
||||
* <li>MyBatis 매퍼 스캔 설정</li>
|
||||
* <li>MyBatis 관련 설정 (카멜 케이스 변환, null 처리 등)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan(
|
||||
basePackages = {PrimaryMybatisConfig.BASE_PACKAGE},
|
||||
@@ -29,6 +42,13 @@ public class PrimaryMybatisConfig {
|
||||
private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml";
|
||||
private static final String SESSION_FACTORY = "primarySqlSessionFactory";
|
||||
|
||||
/**
|
||||
* 주 데이터베이스용 SqlSessionFactory를 생성합니다.
|
||||
*
|
||||
* @param dataSource 주 데이터 소스
|
||||
* @return 구성된 SqlSessionFactory 객체
|
||||
* @throws Exception SqlSessionFactory 생성 중 발생할 수 있는 예외
|
||||
*/
|
||||
@Primary
|
||||
@Bean(name = SESSION_FACTORY)
|
||||
SqlSessionFactory sqlSessionFactory(@Qualifier(PrimaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception {
|
||||
@@ -41,6 +61,11 @@ public class PrimaryMybatisConfig {
|
||||
return sessionFactory.getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* MyBatis 설정을 구성합니다.
|
||||
*
|
||||
* @return 구성된 MyBatis Configuration 객체
|
||||
*/
|
||||
private org.apache.ibatis.session.Configuration mybatisConfiguration() {
|
||||
var configuration = new org.apache.ibatis.session.Configuration();
|
||||
configuration.setMapUnderscoreToCamelCase(true);
|
||||
|
||||
@@ -15,6 +15,19 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import com.spring.infra.db.SecondaryDataSourceConfig;
|
||||
import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper;
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스에 대한 MyBatis 설정을 담당하는 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 다음과 같은 주요 기능을 제공합니다:</p>
|
||||
* <ul>
|
||||
* <li>보조 데이터베이스용 SqlSessionFactory 설정</li>
|
||||
* <li>MyBatis 매퍼 스캔 설정</li>
|
||||
* <li>MyBatis 관련 설정 (카멜 케이스 변환, null 처리 등)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan(
|
||||
basePackages = {SecondaryMybatisConfig.BASE_PACKAGE},
|
||||
@@ -28,6 +41,13 @@ public class SecondaryMybatisConfig {
|
||||
private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml";
|
||||
private static final String SESSION_FACTORY = "secondarySqlSessionFactory";
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스용 SqlSessionFactory를 생성합니다.
|
||||
*
|
||||
* @param dataSource 보조 데이터 소스
|
||||
* @return 구성된 SqlSessionFactory 객체
|
||||
* @throws Exception SqlSessionFactory 생성 중 발생할 수 있는 예외
|
||||
*/
|
||||
@Bean(name = SESSION_FACTORY)
|
||||
SqlSessionFactory sqlSessionFactory(@Qualifier(SecondaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception {
|
||||
var sessionFactory = new SqlSessionFactoryBean();
|
||||
@@ -39,6 +59,11 @@ public class SecondaryMybatisConfig {
|
||||
return sessionFactory.getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* MyBatis 설정을 구성합니다.
|
||||
*
|
||||
* @return 구성된 MyBatis Configuration 객체
|
||||
*/
|
||||
private org.apache.ibatis.session.Configuration mybatisConfiguration() {
|
||||
var configuration = new org.apache.ibatis.session.Configuration();
|
||||
configuration.setMapUnderscoreToCamelCase(true);
|
||||
|
||||
@@ -5,6 +5,26 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 주 데이터베이스용 MyBatis 매퍼를 지정하는 어노테이션입니다.
|
||||
*
|
||||
* <p>이 어노테이션은 인터페이스 레벨에서 사용되며, 해당 인터페이스가 주 데이터베이스와
|
||||
* 연결된 MyBatis 매퍼임을 나타냅니다. 주로 다중 데이터베이스 환경에서 매퍼 인터페이스를
|
||||
* 특정 데이터베이스에 연결하는 데 사용됩니다.</p>
|
||||
*
|
||||
* <p>사용 예:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* @PrimaryMapper
|
||||
* public interface UserMapper {
|
||||
* // 매퍼 메소드 정의
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PrimaryMapper {
|
||||
|
||||
@@ -5,6 +5,26 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 보조 데이터베이스용 MyBatis 매퍼를 지정하는 어노테이션입니다.
|
||||
*
|
||||
* <p>이 어노테이션은 인터페이스 레벨에서 사용되며, 해당 인터페이스가 보조 데이터베이스와
|
||||
* 연결된 MyBatis 매퍼임을 나타냅니다. 주로 다중 데이터베이스 환경에서 매퍼 인터페이스를
|
||||
* 특정 데이터베이스에 연결하는 데 사용됩니다.</p>
|
||||
*
|
||||
* <p>사용 예:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* @SecondaryMapper
|
||||
* public interface UserMapper {
|
||||
* // 매퍼 메소드 정의
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SecondaryMapper {
|
||||
|
||||
@@ -14,6 +14,19 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Quartz 스케줄러 설정을 위한 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 Quartz 스케줄러의 기본 설정을 제공하며, 다음과 같은 기능을 수행합니다:</p>
|
||||
* <ul>
|
||||
* <li>JobFactory 빈 생성 및 설정</li>
|
||||
* <li>SchedulerFactoryBean 생성 및 설정</li>
|
||||
* <li>Quartz 작업에 대한 의존성 주입 지원</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class QuartzConfig {
|
||||
@@ -23,10 +36,13 @@ public class QuartzConfig {
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
|
||||
/**
|
||||
* Quartz Schedule Job 에 의존성 주입
|
||||
* Quartz Schedule Job에 의존성을 주입하기 위한 JobFactory를 생성합니다.
|
||||
*
|
||||
* @param beanFactory application context beanFactory
|
||||
* @return the job factory
|
||||
* <p>이 메소드는 Spring의 AutowireCapableBeanFactory를 사용하여
|
||||
* Quartz Job 인스턴스에 자동으로 의존성을 주입합니다.</p>
|
||||
*
|
||||
* @param beanFactory Spring의 AutowireCapableBeanFactory
|
||||
* @return 생성된 JobFactory 인스턴스
|
||||
*/
|
||||
@Bean
|
||||
JobFactory jobFactory(AutowireCapableBeanFactory beanFactory) {
|
||||
@@ -38,10 +54,19 @@ public class QuartzConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler 전체를 관리하는 Manager.
|
||||
* Quartz Scheduler를 생성하고 설정하는 SchedulerFactoryBean을 구성합니다.
|
||||
*
|
||||
* <p>이 메소드는 다음과 같은 설정을 수행합니다:</p>
|
||||
* <ul>
|
||||
* <li>스케줄러 이름 설정</li>
|
||||
* <li>Quartz 속성 설정</li>
|
||||
* <li>데이터 소스 및 트랜잭션 매니저 설정</li>
|
||||
* <li>JobFactory 설정</li>
|
||||
* <li>자동 시작 및 종료 시 작업 완료 대기 설정</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param jobFactory job factory
|
||||
* @return the scheduler factory bean
|
||||
* @param jobFactory 사용할 JobFactory 인스턴스
|
||||
* @return 구성된 SchedulerFactoryBean 인스턴스
|
||||
*/
|
||||
@Bean
|
||||
SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) {
|
||||
|
||||
@@ -5,9 +5,50 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Quartz 스케줄러를 통해 실행될 작업을 정의하는 어노테이션입니다.
|
||||
*
|
||||
* <p>이 어노테이션은 메소드 레벨에서 사용되며, 해당 메소드를 Quartz 작업으로 등록합니다.</p>
|
||||
*
|
||||
* <p>주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>작업의 이름 지정</li>
|
||||
* <li>작업의 실행 주기를 Cron 표현식으로 정의</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>사용 예:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* @QuartzJob(name = "myJob", cronExpression = "0 0 12 * * ?")
|
||||
* public void myScheduledJob() {
|
||||
* // 작업 내용
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface QuartzJob {
|
||||
/**
|
||||
* Quartz 작업의 그룹을 지정합니다.
|
||||
*
|
||||
* @return 그룹의 이름
|
||||
*/
|
||||
String group() default "DEFAULT";
|
||||
/**
|
||||
* Quartz 작업의 이름을 지정합니다.
|
||||
*
|
||||
* @return 작업의 이름
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 작업의 실행 주기를 Cron 표현식으로 지정합니다.
|
||||
*
|
||||
* @return Cron 표현식
|
||||
*/
|
||||
String cronExpression();
|
||||
}
|
||||
|
||||
@@ -13,19 +13,49 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Quartz 작업을 실행하는 Spring Batch Job 실행기 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 Quartz 스케줄러에 의해 호출되며, 지정된 Spring Batch Job을 실행합니다.</p>
|
||||
*
|
||||
* <p>주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>JobRegistry에서 지정된 이름의 Job을 조회</li>
|
||||
* <li>Job 실행을 위한 JobParameters 생성</li>
|
||||
* <li>JobLauncher를 통한 Job 실행</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
* @see QuartzJobBean
|
||||
* @see JobLauncher
|
||||
* @see JobRegistry
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class QuartzJobLauncher extends QuartzJobBean {
|
||||
|
||||
private final JobLauncher jobLauncher;
|
||||
private final JobRegistry jobRegistry;
|
||||
|
||||
|
||||
private String jobName;
|
||||
|
||||
public void setJobName(String jobName) {
|
||||
this.jobName = jobName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quartz 스케줄러에 의해 호출되는 메소드로, 실제 Job을 실행합니다.
|
||||
*
|
||||
* <p>이 메소드는 다음과 같은 작업을 수행합니다:</p>
|
||||
* <ol>
|
||||
* <li>JobRegistry에서 지정된 이름의 Job을 조회</li>
|
||||
* <li>현재 시간을 기반으로 한 고유한 JobParameters 생성</li>
|
||||
* <li>JobLauncher를 사용하여 Job 실행</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param context Quartz JobExecutionContext 객체
|
||||
*/
|
||||
@Override
|
||||
protected void executeInternal(@NonNull JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
|
||||
@@ -19,6 +19,29 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Quartz 작업 등록기 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 애플리케이션 컨텍스트가 리프레시될 때 실행되며,
|
||||
* {@link QuartzJob} 어노테이션이 붙은 모든 메소드를 찾아 Quartz 스케줄러에 등록합니다.</p>
|
||||
*
|
||||
* <p>주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>애플리케이션 컨텍스트 내의 모든 빈을 검사</li>
|
||||
* <li>QuartzJob 어노테이션이 붙은 메소드 식별</li>
|
||||
* <li>식별된 메소드를 Quartz 작업으로 등록</li>
|
||||
* <li>각 작업에 대한 JobDetail 및 Trigger 생성</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>이 클래스는 {@link ApplicationListener}를 구현하여
|
||||
* {@link ContextRefreshedEvent}가 발생할 때 자동으로 실행됩니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
* @see QuartzJob
|
||||
* @see ApplicationListener
|
||||
* @see ContextRefreshedEvent
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class QuartzJobRegistrar implements ApplicationListener<ContextRefreshedEvent> {
|
||||
@@ -26,6 +49,12 @@ public class QuartzJobRegistrar implements ApplicationListener<ContextRefreshedE
|
||||
private final Scheduler scheduler;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 애플리케이션 컨텍스트가 리프레시될 때 호출되는 메소드.
|
||||
* 모든 빈을 순회하며 QuartzJob 어노테이션이 붙은 메소드를 찾아 Quartz 작업으로 등록합니다.
|
||||
*
|
||||
* @param event ContextRefreshedEvent 객체
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
|
||||
for (String beanName : applicationContext.getBeanDefinitionNames()) {
|
||||
@@ -40,31 +69,50 @@ public class QuartzJobRegistrar implements ApplicationListener<ContextRefreshedE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* QuartzJob 어노테이션 정보를 바탕으로 Quartz 작업을 등록합니다.
|
||||
*
|
||||
* @param quartzJobAnnotation QuartzJob 어노테이션 객체
|
||||
* @param jobName 등록할 작업의 이름
|
||||
*/
|
||||
private void registerQuartzJob(QuartzJob quartzJobAnnotation, String jobName) {
|
||||
try {
|
||||
JobDetail jobDetail = createJobDetail(quartzJobAnnotation, jobName);
|
||||
Trigger trigger = createTrigger(quartzJobAnnotation, jobDetail);
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
} catch (SchedulerException e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalStateException("Error scheduling Quartz job", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* QuartzJob 어노테이션 정보를 바탕으로 JobDetail 객체를 생성합니다.
|
||||
*
|
||||
* @param quartzJobAnnotation QuartzJob 어노테이션 객체
|
||||
* @param jobName 작업의 이름
|
||||
* @return 생성된 JobDetail 객체
|
||||
*/
|
||||
private JobDetail createJobDetail(QuartzJob quartzJobAnnotation, String jobName) {
|
||||
JobDataMap jobDataMap = new JobDataMap();
|
||||
jobDataMap.put("jobName", jobName);
|
||||
return JobBuilder.newJob(QuartzJobLauncher.class)
|
||||
.withIdentity(quartzJobAnnotation.name())
|
||||
.withIdentity(quartzJobAnnotation.name(), quartzJobAnnotation.group())
|
||||
.setJobData(jobDataMap)
|
||||
.storeDurably()
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* QuartzJob 어노테이션 정보를 바탕으로 Trigger 객체를 생성합니다.
|
||||
*
|
||||
* @param quartzJobAnnotation QuartzJob 어노테이션 객체
|
||||
* @param jobDetail 연관된 JobDetail 객체
|
||||
* @return 생성된 Trigger 객체
|
||||
*/
|
||||
private Trigger createTrigger(QuartzJob quartzJobAnnotation, JobDetail jobDetail) {
|
||||
return TriggerBuilder.newTrigger()
|
||||
.forJob(jobDetail)
|
||||
.withIdentity(quartzJobAnnotation.name() + "Trigger")
|
||||
.withIdentity(quartzJobAnnotation.name() + "Trigger", quartzJobAnnotation.group())
|
||||
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJobAnnotation.cronExpression()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,21 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Quartz 스케줄러의 설정 속성을 관리하는 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 application.properties 또는 application.yml 파일에서
|
||||
* "spring.quartz.properties.org.quartz" 접두사로 시작하는 설정을 읽어옵니다.</p>
|
||||
*
|
||||
* <p>주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>JobStore, Scheduler, ThreadPool 관련 설정 관리</li>
|
||||
* <li>설정값을 Properties 객체로 변환</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@@ -45,18 +60,38 @@ public class QuartzProperties {
|
||||
private final int threadPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 객체의 모든 속성을 Properties 객체로 변환합니다.
|
||||
*
|
||||
* @return 변환된 Properties 객체
|
||||
*/
|
||||
public Properties toProperties() {
|
||||
Properties properties = new Properties();
|
||||
addProperties(PREFIX, this, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 주어진 객체의 모든 필드를 재귀적으로 순회하며 Properties 객체에 추가합니다.
|
||||
*
|
||||
* @param prefix 속성 키의 접두사
|
||||
* @param object 속성을 추출할 객체
|
||||
* @param properties 속성을 저장할 Properties 객체
|
||||
*/
|
||||
private void addProperties(String prefix, Object object, Properties properties) {
|
||||
Arrays.stream(object.getClass().getDeclaredFields())
|
||||
.filter(field -> !Modifier.isStatic(field.getModifiers()))
|
||||
.forEach(field -> setProperties(prefix, object, properties, field));
|
||||
}
|
||||
|
||||
/**
|
||||
* 주어진 필드의 값을 Properties 객체에 추가합니다.
|
||||
*
|
||||
* @param prefix 속성 키의 접두사
|
||||
* @param object 속성을 추출할 객체
|
||||
* @param properties 속성을 저장할 Properties 객체
|
||||
* @param field 처리할 필드
|
||||
*/
|
||||
private void setProperties(String prefix, Object object, Properties properties, Field field) {
|
||||
try {
|
||||
Object value = field.get(object);
|
||||
@@ -74,6 +109,12 @@ public class QuartzProperties {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 주어진 타입이 단순 타입(primitive, String, Number, Boolean, Character)인지 확인합니다.
|
||||
*
|
||||
* @param type 확인할 클래스 타입
|
||||
* @return 단순 타입이면 true, 그렇지 않으면 false
|
||||
*/
|
||||
private boolean isSimpleType(Class<?> type) {
|
||||
return type.isPrimitive()
|
||||
|| String.class == type
|
||||
|
||||
@@ -23,16 +23,30 @@ import com.spring.infra.security.handler.JwtAccessDeniedHandler;
|
||||
import com.spring.infra.security.handler.JwtAuthenticationEntryPoint;
|
||||
import com.spring.infra.security.jwt.JwtTokenService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 애플리케이션의 보안 설정을 담당하는 구성 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 Spring Security를 구성하고 JWT 기반의 인증을 설정합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private static final String[] PERMITTED_URI = {"/favicon.ico", "/api/auth/**", "/signIn", "/h2-console/**"};
|
||||
|
||||
/**
|
||||
* Spring Security의 필터 체인을 구성합니다.
|
||||
*
|
||||
* @param http HttpSecurity 객체
|
||||
* @param tokenService JWT 토큰 서비스
|
||||
* @param authenticationEntryPoint JWT 인증 진입점
|
||||
* @param accessDeniedHandler JWT 접근 거부 핸들러
|
||||
* @return 구성된 SecurityFilterChain
|
||||
*/
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(
|
||||
HttpSecurity http,
|
||||
@@ -46,7 +60,7 @@ public class SecurityConfig {
|
||||
.httpBasic(HttpBasicConfigurer::disable)
|
||||
.formLogin(FormLoginConfigurer::disable)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(PERMITTED_URI).permitAll()
|
||||
.antMatchers(PERMITTED_URI).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.logout(logout -> logout
|
||||
@@ -63,11 +77,21 @@ public class SecurityConfig {
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 요청에 대해 보안 검사를 무시하도록 설정합니다.
|
||||
*
|
||||
* @return WebSecurityCustomizer 객체
|
||||
*/
|
||||
@Bean
|
||||
WebSecurityCustomizer ignoringCustomizer() {
|
||||
return web -> web.ignoring().requestMatchers("/h2-console/**");
|
||||
return web -> web.ignoring().antMatchers("/h2-console/**");
|
||||
}
|
||||
|
||||
/**
|
||||
* 비밀번호 인코더를 구성합니다.
|
||||
*
|
||||
* @return PasswordEncoder 객체
|
||||
*/
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
|
||||
@@ -12,16 +12,34 @@ import com.spring.domain.user.entity.AppUser;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Spring Security의 UserDetails 인터페이스를 구현한 사용자 주체(Principal) 클래스입니다.
|
||||
* <p>애플리케이션의 사용자 정보를 Spring Security에서 사용할 수 있는 형태로 변환합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class UserPrincipal implements UserDetails {
|
||||
|
||||
private final AppUser appUser;
|
||||
private final transient AppUser appUser;
|
||||
|
||||
/**
|
||||
* AppUser 객체로부터 UserPrincipal 객체를 생성합니다.
|
||||
*
|
||||
* @param appUser 변환할 AppUser 객체
|
||||
* @return 생성된 UserPrincipal 객체
|
||||
*/
|
||||
public static UserPrincipal valueOf(AppUser appUser) {
|
||||
return new UserPrincipal(appUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자의 권한 목록을 반환합니다.
|
||||
*
|
||||
* @return 사용자의 GrantedAuthority 컬렉션
|
||||
*/
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return appUser.getAppUserRoleMap().stream()
|
||||
@@ -30,31 +48,62 @@ public final class UserPrincipal implements UserDetails {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 계정이 만료되지 않았는지 확인합니다.
|
||||
*
|
||||
* @return 계정 만료 여부 (true: 만료되지 않음)
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 계정이 잠기지 않았는지 확인합니다.
|
||||
*
|
||||
* @return 계정 잠금 여부 (true: 잠기지 않음)
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 자격 증명(비밀번호)이 만료되지 않았는지 확인합니다.
|
||||
*
|
||||
* @return 자격 증명 만료 여부 (true: 만료되지 않음)
|
||||
*/
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 계정이 활성화되어 있는지 확인합니다.
|
||||
*
|
||||
* @return 계정 활성화 여부 (true: 활성화됨)
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자의 비밀번호를 반환합니다.
|
||||
* 이 구현에서는 null을 반환합니다.
|
||||
*
|
||||
* @return 사용자 비밀번호 (null)
|
||||
*/
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자의 로그인 ID를 반환합니다.
|
||||
*
|
||||
* @return 사용자 로그인 ID
|
||||
*/
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return appUser.getLoginId();
|
||||
|
||||
@@ -3,6 +3,11 @@ package com.spring.infra.security.filter;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@@ -11,21 +16,39 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import com.spring.infra.security.jwt.JwtTokenRule;
|
||||
import com.spring.infra.security.jwt.JwtTokenService;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* JWT 인증을 처리하는 필터 클래스입니다.
|
||||
*
|
||||
* <p>이 필터는 요청마다 JWT 토큰을 검증하고, 필요한 경우 토큰을 갱신합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public final class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
private final List<String> permitAllUrls;
|
||||
|
||||
/**
|
||||
* 요청마다 실행되는 필터 메소드입니다.
|
||||
*
|
||||
* <p>JWT 토큰을 검증하고, 필요한 경우 토큰을 갱신합니다.</p>
|
||||
*
|
||||
* @param request HTTP 요청 객체
|
||||
* @param response HTTP 응답 객체
|
||||
* @param filterChain 필터 체인
|
||||
* @throws ServletException 서블릿 예외
|
||||
* @throws IOException 입출력 예외
|
||||
*/
|
||||
@Override
|
||||
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
protected void doFilterInternal(
|
||||
@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
|
||||
String requestURI = request.getRequestURI();
|
||||
if (permitAllUrls.stream().anyMatch(requestURI::startsWith)) {
|
||||
@@ -51,11 +74,15 @@ public final class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
jwtTokenService.deleteCookie(response);
|
||||
|
||||
}
|
||||
|
||||
private void setAuthenticationToContext(String accessToken) {
|
||||
Authentication authentication = jwtTokenService.getAuthentication(accessToken);
|
||||
/**
|
||||
* 인증 정보를 SecurityContext에 설정합니다.
|
||||
*
|
||||
* @param token 토큰
|
||||
*/
|
||||
private void setAuthenticationToContext(String token) {
|
||||
Authentication authentication = jwtTokenService.getAuthentication(token);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,38 @@ package com.spring.infra.security.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 유저 정보는 있으나 자원에 접근할 수 있는 권한이 없는 경우 : SC_FORBIDDEN (403) 응답
|
||||
* JWT 인증에서 접근 거부 상황을 처리하는 핸들러 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 사용자가 인증은 되었지만 특정 리소스에 대한 접근 권한이 없는 경우를 처리합니다.
|
||||
* 이런 경우 SC_FORBIDDEN (403) 응답을 반환합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
/**
|
||||
* 접근 거부 상황을 처리합니다.
|
||||
*
|
||||
* <p>사용자가 접근 권한이 없는 리소스에 접근을 시도할 때 호출됩니다.
|
||||
* 이 메소드는 SC_FORBIDDEN (403) 상태 코드를 응답으로 전송합니다.</p>
|
||||
*
|
||||
* @param request 현재 HTTP 요청
|
||||
* @param response 현재 HTTP 응답
|
||||
* @param accessDeniedException 발생한 접근 거부 예외
|
||||
* @throws IOException 입출력 예외 발생 시
|
||||
* @throws ServletException 서블릿 예외 발생 시
|
||||
*/
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) throws IOException, ServletException {
|
||||
|
||||
@@ -2,20 +2,38 @@ package com.spring.infra.security.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 유저 정보 없이 접근한 경우 : SC_UNAUTHORIZED (401) 응답
|
||||
* JWT 인증에서 인증되지 않은 접근을 처리하는 진입점 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 사용자가 인증되지 않은 상태에서 보호된 리소스에 접근을 시도할 때 호출됩니다.
|
||||
* 이런 경우 SC_UNAUTHORIZED (401) 응답을 반환합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
/**
|
||||
* 인증되지 않은 접근을 처리합니다.
|
||||
*
|
||||
* <p>인증되지 않은 사용자가 보호된 리소스에 접근을 시도할 때 호출됩니다.
|
||||
* 이 메소드는 SC_UNAUTHORIZED (401) 상태 코드를 응답으로 전송합니다.</p>
|
||||
*
|
||||
* @param request 현재 HTTP 요청
|
||||
* @param response 현재 HTTP 응답
|
||||
* @param authException 발생한 인증 예외
|
||||
* @throws IOException 입출력 예외 발생 시
|
||||
* @throws ServletException 서블릿 예외 발생 시
|
||||
*/
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException, ServletException {
|
||||
|
||||
@@ -5,6 +5,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* JWT(JSON Web Token) 관련 설정 속성을 관리하는 클래스입니다.
|
||||
* 'jwt' 접두사로 시작하는 설정 속성을 바인딩합니다.
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Getter
|
||||
@ConfigurationProperties(prefix = "jwt")
|
||||
@RequiredArgsConstructor
|
||||
|
||||
@@ -17,12 +17,26 @@ import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* JWT 토큰을 생성하는 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 액세스 토큰과 리프레시 토큰을 생성하는 기능을 제공합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtTokenGenerator {
|
||||
|
||||
private final JwtProperties jwtProperties;
|
||||
|
||||
/**
|
||||
* 액세스 토큰을 생성합니다.
|
||||
*
|
||||
* @param authentication 인증 정보
|
||||
* @return 생성된 액세스 토큰
|
||||
*/
|
||||
public String generateAccessToken(Authentication authentication) {
|
||||
return Jwts.builder()
|
||||
.setHeader(createHeader())
|
||||
@@ -33,6 +47,12 @@ public class JwtTokenGenerator {
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 리프레시 토큰을 생성합니다.
|
||||
*
|
||||
* @param authentication 인증 정보
|
||||
* @return 생성된 리프레시 토큰
|
||||
*/
|
||||
public String generateRefreshToken(Authentication authentication) {
|
||||
return Jwts.builder()
|
||||
.setHeader(createHeader())
|
||||
@@ -42,6 +62,11 @@ public class JwtTokenGenerator {
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 헤더를 생성합니다.
|
||||
*
|
||||
* @return JWT 헤더 맵
|
||||
*/
|
||||
private Map<String, Object> createHeader() {
|
||||
Map<String, Object> header = new HashMap<>();
|
||||
header.put("typ", "JWT");
|
||||
@@ -49,6 +74,12 @@ public class JwtTokenGenerator {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 클레임을 생성합니다.
|
||||
*
|
||||
* @param authentication 인증 정보
|
||||
* @return JWT 클레임 맵
|
||||
*/
|
||||
private Map<String, Object> createClaims(Authentication authentication) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put(JwtTokenRule.AUTHORITIES_KEY.getValue(), authentication.getAuthorities().stream()
|
||||
|
||||
@@ -3,15 +3,46 @@ package com.spring.infra.security.jwt;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* JWT 토큰 관련 규칙을 정의하는 열거형입니다.
|
||||
*
|
||||
* <p>이 열거형은 JWT 토큰 처리에 필요한 다양한 상수 값들을 정의합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum JwtTokenRule {
|
||||
|
||||
/**
|
||||
* JWT 토큰 발급 시 사용되는 HTTP 헤더 이름입니다.
|
||||
*/
|
||||
JWT_ISSUE_HEADER("Set-Cookie"),
|
||||
|
||||
/**
|
||||
* JWT 토큰 해석 시 사용되는 HTTP 헤더 이름입니다.
|
||||
*/
|
||||
JWT_RESOLVE_HEADER("Cookie"),
|
||||
|
||||
/**
|
||||
* 액세스 토큰의 접두사입니다.
|
||||
*/
|
||||
ACCESS_PREFIX("access"),
|
||||
|
||||
/**
|
||||
* 리프레시 토큰의 접두사입니다.
|
||||
*/
|
||||
REFRESH_PREFIX("refresh"),
|
||||
|
||||
/**
|
||||
* Bearer 인증 스키마의 접두사입니다.
|
||||
*/
|
||||
BEARER_PREFIX("Bearer "),
|
||||
|
||||
/**
|
||||
* JWT 클레임에서 권한 정보를 나타내는 키입니다.
|
||||
*/
|
||||
AUTHORITIES_KEY("auth");
|
||||
|
||||
private final String value;
|
||||
|
||||
@@ -2,6 +2,10 @@ package com.spring.infra.security.jwt;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -11,10 +15,15 @@ import org.springframework.stereotype.Service;
|
||||
import com.spring.infra.security.service.UserPrincipalService;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* JWT 토큰 관련 서비스를 제공하는 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 JWT 토큰의 생성, 검증, 해석 등 다양한 토큰 관련 기능을 제공합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Service
|
||||
public class JwtTokenService {
|
||||
|
||||
@@ -41,6 +50,13 @@ public class JwtTokenService {
|
||||
this.refreshExpiration = jwtProperties.getRefreshToken().getExpiration();
|
||||
}
|
||||
|
||||
/**
|
||||
* 액세스 토큰을 생성하고 응답 헤더에 설정합니다.
|
||||
*
|
||||
* @param response HTTP 응답 객체
|
||||
* @param authentication 인증 정보
|
||||
* @return 생성된 액세스 토큰
|
||||
*/
|
||||
public String generateAccessToken(HttpServletResponse response, Authentication authentication) {
|
||||
String accessToken = jwtTokenGenerator.generateAccessToken(authentication);
|
||||
ResponseCookie cookie = setTokenToCookie(JwtTokenRule.ACCESS_PREFIX.getValue(), accessToken, accessExpiration / 1000);
|
||||
@@ -48,6 +64,13 @@ public class JwtTokenService {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 리프레시 토큰을 생성하고 응답 헤더에 설정합니다.
|
||||
*
|
||||
* @param response HTTP 응답 객체
|
||||
* @param authentication 인증 정보
|
||||
* @return 생성된 리프레시 토큰
|
||||
*/
|
||||
public String generateRefreshToken(HttpServletResponse response, Authentication authentication) {
|
||||
String refreshToken = jwtTokenGenerator.generateRefreshToken(authentication);
|
||||
ResponseCookie cookie = setTokenToCookie(JwtTokenRule.REFRESH_PREFIX.getValue(), refreshToken, refreshExpiration / 1000);
|
||||
@@ -55,6 +78,14 @@ public class JwtTokenService {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰을 쿠키로 설정합니다.
|
||||
*
|
||||
* @param tokenPrefix 토큰 접두사
|
||||
* @param token 토큰 값
|
||||
* @param maxAgeSeconds 쿠키 유효 시간(초)
|
||||
* @return 생성된 ResponseCookie 객체
|
||||
*/
|
||||
private ResponseCookie setTokenToCookie(String tokenPrefix, String token, long maxAgeSeconds) {
|
||||
return ResponseCookie.from(tokenPrefix, token)
|
||||
.path("/")
|
||||
@@ -65,14 +96,34 @@ public class JwtTokenService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 액세스 토큰의 유효성을 검증합니다.
|
||||
*
|
||||
* @param token 검증할 토큰
|
||||
* @return 토큰의 유효성 여부
|
||||
*/
|
||||
public boolean validateAccessToken(String token) {
|
||||
return jwtTokenUtil.getTokenStatus(token, accessSecretKey) == JwtTokenStatus.AUTHENTICATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 리프레시 토큰의 유효성을 검증합니다.
|
||||
*
|
||||
* @param token 검증할 토큰
|
||||
* @return 토큰의 유효성 여부
|
||||
*/
|
||||
public boolean validateRefreshToken(String token) {
|
||||
return jwtTokenUtil.getTokenStatus(token, refreshSecretKey) == JwtTokenStatus.AUTHENTICATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿠키에서 토큰을 추출합니다.
|
||||
*
|
||||
* @param request HTTP 요청 객체
|
||||
* @param tokenPrefix 토큰 접두사
|
||||
* @return 추출된 토큰
|
||||
* @throws IllegalStateException 토큰이 없을 경우 발생
|
||||
*/
|
||||
public String resolveTokenFromCookie(HttpServletRequest request, JwtTokenRule tokenPrefix) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null) {
|
||||
@@ -81,11 +132,24 @@ public class JwtTokenService {
|
||||
return jwtTokenUtil.resolveTokenFromCookie(cookies, tokenPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰으로부터 인증 정보를 생성합니다.
|
||||
*
|
||||
* @param token JWT 토큰
|
||||
* @return 생성된 Authentication 객체
|
||||
*/
|
||||
public Authentication getAuthentication(String token) {
|
||||
UserDetails principal = userPrincipalService.loadUserByUsername(getUserPk(token, accessSecretKey));
|
||||
return new UsernamePasswordAuthenticationToken(principal, "", principal.getAuthorities());
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰에서 사용자 식별자를 추출합니다.
|
||||
*
|
||||
* @param token JWT 토큰
|
||||
* @param secretKey 비밀 키
|
||||
* @return 추출된 사용자 식별자
|
||||
*/
|
||||
private String getUserPk(String token, Key secretKey) {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
@@ -95,6 +159,11 @@ public class JwtTokenService {
|
||||
.getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 액세스 토큰과 리프레시 토큰 쿠키를 삭제합니다.
|
||||
*
|
||||
* @param response HTTP 응답 객체
|
||||
*/
|
||||
public void deleteCookie(HttpServletResponse response) {
|
||||
Cookie accessCookie = jwtTokenUtil.resetToken(JwtTokenRule.ACCESS_PREFIX);
|
||||
Cookie refreshCookie = jwtTokenUtil.resetToken(JwtTokenRule.REFRESH_PREFIX);
|
||||
|
||||
@@ -3,10 +3,29 @@ package com.spring.infra.security.jwt;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* JWT 토큰의 상태를 나타내는 열거형입니다.
|
||||
*
|
||||
* <p>이 열거형은 JWT 토큰의 유효성 검증 결과를 표현하는 데 사용됩니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum JwtTokenStatus {
|
||||
/**
|
||||
* 토큰이 유효하고 인증된 상태를 나타냅니다.
|
||||
*/
|
||||
AUTHENTICATED,
|
||||
|
||||
/**
|
||||
* 토큰이 만료된 상태를 나타냅니다.
|
||||
*/
|
||||
EXPIRED,
|
||||
|
||||
/**
|
||||
* 토큰이 유효하지 않은 상태를 나타냅니다.
|
||||
*/
|
||||
INVALID
|
||||
}
|
||||
|
||||
@@ -5,19 +5,35 @@ import java.security.Key;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* JWT 토큰 관련 유틸리티 기능을 제공하는 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 JWT 토큰의 상태 확인, 쿠키에서 토큰 추출, 서명 키 생성 등의 기능을 제공합니다.</p>
|
||||
*
|
||||
* @author mindol
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtTokenUtil {
|
||||
|
||||
/**
|
||||
* JWT 토큰의 상태를 확인합니다.
|
||||
*
|
||||
* @param token 검증할 JWT 토큰
|
||||
* @param secretKey 토큰 검증에 사용할 비밀 키
|
||||
* @return 토큰의 상태 (AUTHENTICATED, EXPIRED, INVALID)
|
||||
*/
|
||||
public JwtTokenStatus getTokenStatus(String token, Key secretKey) {
|
||||
try {
|
||||
Jwts.parserBuilder()
|
||||
@@ -34,6 +50,13 @@ public class JwtTokenUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿠키에서 특정 접두사를 가진 토큰을 추출합니다.
|
||||
*
|
||||
* @param cookies 쿠키 배열
|
||||
* @param tokenPrefix 토큰 접두사
|
||||
* @return 추출된 토큰 값 (없으면 빈 문자열)
|
||||
*/
|
||||
public String resolveTokenFromCookie(Cookie[] cookies, JwtTokenRule tokenPrefix) {
|
||||
return Arrays.stream(cookies)
|
||||
.filter(cookie -> cookie.getName().equals(tokenPrefix.getValue()))
|
||||
@@ -42,15 +65,33 @@ public class JwtTokenUtil {
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* 주어진 비밀 키로 서명 키를 생성합니다.
|
||||
*
|
||||
* @param secretKey 비밀 키 문자열
|
||||
* @return 생성된 서명 키
|
||||
*/
|
||||
public Key getSigningKey(String secretKey) {
|
||||
String encodedKey = encodeToBase64(secretKey);
|
||||
return Keys.hmacShaKeyFor(encodedKey.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 문자열을 Base64로 인코딩합니다.
|
||||
*
|
||||
* @param secretKey 인코딩할 문자열
|
||||
* @return Base64로 인코딩된 문자열
|
||||
*/
|
||||
private String encodeToBase64(String secretKey) {
|
||||
return Base64.getEncoder().encodeToString(secretKey.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰을 리셋하는 쿠키를 생성합니다.
|
||||
*
|
||||
* @param tokenPrefix 토큰 접두사
|
||||
* @return 생성된 쿠키 객체
|
||||
*/
|
||||
public Cookie resetToken(JwtTokenRule tokenPrefix) {
|
||||
Cookie cookie = new Cookie(tokenPrefix.getValue(), null);
|
||||
cookie.setMaxAge(0);
|
||||
|
||||
@@ -44,6 +44,8 @@ spring:
|
||||
show-sql: true
|
||||
|
||||
batch:
|
||||
job:
|
||||
enabled: true
|
||||
jdbc:
|
||||
initialize-schema: always
|
||||
|
||||
|
||||
Reference in New Issue
Block a user