Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dc15b75
[REFACTOR] OAuth 및 Cookie 관련 코드 리팩토링 refactor 리팩토링 (#93)
leegwichan Feb 11, 2025
279bf95
[FEAT] 단건 조회에 토론 형식을 같이 반환하도록 수정 (#98)
coli-geonwoo Feb 13, 2025
e13cd49
[FEAT] 의회식 토론 EXPORT Api 구현 (#99)
coli-geonwoo Feb 18, 2025
067b71f
[FEAT] 시간총량제 테이블 Entity 구현 (#102)
leegwichan Feb 21, 2025
84a0d8c
[REFACTOR] 스프링 시작 시 발생하는 예외 설정 (#103)
leegwichan Feb 21, 2025
ca21785
[FEAT] 시간총량제 API 구현 (#106)
unifolio0 Mar 3, 2025
c360901
[CHORE] DB 형상 관리 도구 도입 (#107)
leegwichan Mar 3, 2025
8562642
[CHORE] 애플리케이션 에러로깅 도입 (#109)
coli-geonwoo Mar 5, 2025
8f0b6f5
[FEAT] 멤버의 테이블 조회 시, 테이블 순서 구현 (#110)
leegwichan Mar 7, 2025
785224e
[FIX] time auditing이 되지 않는 문제 개선 (#113)
coli-geonwoo Mar 9, 2025
f5ba8a7
[REFACTOR] 멤버 테이블 조회에서 소요시간 대신 주제 반환 (#114)
coli-geonwoo Mar 10, 2025
286bf85
feat : 의회식 토론 진행 API 구현
leegwichan Mar 10, 2025
916dd92
fix : 시간 총량제 테이블 수정 API Http Method 변경
leegwichan Mar 10, 2025
94d4f1a
feat : 시간 총량제 토론 진행 API 구현
leegwichan Mar 10, 2025
191bec8
refactor : TimeBoxes 의 타입 파라미터 명시
leegwichan Mar 10, 2025
68742b5
refactor : 사용하지 않는 인자 제거, 필요 없는 Wrapper 타입 제거
leegwichan Mar 10, 2025
cdf5775
test: 테스트 메서드 네이밍 수정
leegwichan Mar 10, 2025
961d367
refactor: 컨트롤러 메서드 네이밍 수정
leegwichan Mar 10, 2025
c00aed0
test: Service Test 시 Thread.sleep() 제거
leegwichan Mar 10, 2025
5e5bb3c
[FEAT] 사용자 지정 테이블 Entity 구현 (#118)
unifolio0 Mar 11, 2025
4e19c6f
[FEAT] Debate API 구현 (#119)
leegwichan Mar 11, 2025
ead31d6
[REFACTOR] agenda nullable하게 변경 (#123)
unifolio0 Mar 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ configurations {
compileOnly {
extendsFrom annotationProcessor
}

configureEach {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
}

repositories {
Expand All @@ -32,11 +36,25 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.5'

// Excel Export
implementation 'org.apache.poi:poi-ooxml:5.2.3'
implementation 'org.apache.poi:poi:5.2.3'

// DB schema manager
implementation 'org.flywaydb:flyway-mysql'

// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

Expand All @@ -47,14 +65,9 @@ dependencies {
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2'
testImplementation 'com.epages:restdocs-api-spec-restassured:0.18.2'

// Excel Export
implementation 'org.apache.poi:poi-ooxml:5.2.3'
implementation 'org.apache.poi:poi:5.2.3'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.5'
// Logging
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml"
}

bootJar {
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/debatetimer/DebateTimerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ public class DebateTimerApplication {
public static void main(String[] args) {
SpringApplication.run(DebateTimerApplication.class, args);
}

}
47 changes: 47 additions & 0 deletions src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.debatetimer.aop.logging;

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.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class ClientLoggingAspect extends LoggingAspect {

private static final String CLIENT_REQUEST_TIME_KEY = "clientRequestTime";

@Pointcut("@within(com.debatetimer.aop.logging.LoggingClient)")
public void loggingClients() {
}

@Around("loggingClients()")
public Object loggingControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
setMdc(CLIENT_REQUEST_TIME_KEY, System.currentTimeMillis());
logClientRequest(proceedingJoinPoint);

Object responseBody = proceedingJoinPoint.proceed();

logClientResponse(proceedingJoinPoint);
removeMdc(CLIENT_REQUEST_TIME_KEY);
return responseBody;
}

private void logClientRequest(ProceedingJoinPoint joinPoint) {
String clientName = joinPoint.getSignature().getDeclaringType().getSimpleName();
String methodName = joinPoint.getSignature().getName();
log.info("Client Request Logging - Client Name: {} | MethodName: {}", clientName, methodName);
}

private void logClientResponse(ProceedingJoinPoint joinPoint) {
String clientName = joinPoint.getSignature().getDeclaringType().getSimpleName();
String methodName = joinPoint.getSignature().getName();
long latency = getLatency(CLIENT_REQUEST_TIME_KEY);
log.info("Client Response Logging - Client Name: {} | MethodName: {} | Latency: {}ms",
clientName, methodName, latency);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.debatetimer.aop.logging;


import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
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.CodeSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Slf4j
@Aspect
@Component
public class ControllerLoggingAspect extends LoggingAspect {

private static final String REQUEST_ID_KEY = "requestId";
private static final String START_TIME_KEY = "startTime";

@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
public void allController() {
}

@Around("allController()")
public Object loggingControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
setMdc(REQUEST_ID_KEY, UUID.randomUUID().toString());
setMdc(START_TIME_KEY, System.currentTimeMillis());
logControllerRequest(proceedingJoinPoint);

Object responseBody = proceedingJoinPoint.proceed();

logControllerResponse(responseBody);
removeMdc(START_TIME_KEY);
return responseBody;
}

private void logControllerRequest(ProceedingJoinPoint proceedingJoinPoint) {
HttpServletRequest request = getHttpServletRequest();
String requestParameters = getRequestParameters(proceedingJoinPoint);
String uri = request.getRequestURI();
String httpMethod = request.getMethod();
log.info("Request Logging: {} {} parameters - {}", httpMethod, uri, requestParameters);
}

private void logControllerResponse(Object responseBody) {
HttpServletRequest request = getHttpServletRequest();
String uri = request.getRequestURI();
String httpMethod = request.getMethod();
long latency = getLatency(START_TIME_KEY);
log.info("Response Logging: {} {} Body: {} latency - {}ms", httpMethod, uri, responseBody, latency);
}

private HttpServletRequest getHttpServletRequest() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return requestAttributes.getRequest();
}

private String getRequestParameters(JoinPoint joinPoint) {
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
String[] parameterNames = codeSignature.getParameterNames();
Object[] args = joinPoint.getArgs();
Map<String, Object> params = new HashMap<>();
for (int i = 0; i < parameterNames.length; i++) {
params.put(parameterNames[i], args[i]);
}
return params.toString();
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/debatetimer/aop/logging/LoggingAspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.debatetimer.aop.logging;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

@Slf4j
public abstract class LoggingAspect {

protected final void setMdc(String key, Object value) {
MDC.put(key, String.valueOf(value));
}

protected final void removeMdc(String key) {
MDC.remove(key);
}

protected final long getLatency(String startTimeKey) {
long startTime = Long.parseLong(MDC.get(startTimeKey));
return System.currentTimeMillis() - startTime;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/debatetimer/aop/logging/LoggingClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.debatetimer.aop.logging;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggingClient {
}
2 changes: 2 additions & 0 deletions src/main/java/com/debatetimer/client/OAuthClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.debatetimer.client;

import com.debatetimer.aop.logging.LoggingClient;
import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.dto.member.MemberInfo;
import com.debatetimer.dto.member.OAuthToken;
Expand All @@ -9,6 +10,7 @@
import org.springframework.web.client.RestClient;

@Component
@LoggingClient
@EnableConfigurationProperties(OAuthProperties.class)
public class OAuthClient {

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/debatetimer/client/OAuthProperties.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.debatetimer.client;

import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.exception.custom.DTInitializationException;
import com.debatetimer.exception.errorcode.InitializationErrorCode;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import lombok.Getter;
Expand All @@ -20,11 +22,21 @@ public OAuthProperties(
String clientId,
String clientSecret,
String grantType) {
validate(clientId);
validate(clientSecret);
validate(grantType);

this.clientId = clientId;
this.clientSecret = clientSecret;
this.grantType = grantType;
}

private void validate(String element) {
if (element == null || element.isBlank()) {
throw new DTInitializationException(InitializationErrorCode.OAUTH_PROPERTIES_EMPTY);
}
}

public MultiValueMap<String, String> createTokenRequestBody(MemberCreateRequest request) {
String code = request.code();
String decodedVerificationCode = URLDecoder.decode(code, StandardCharsets.UTF_8);
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/debatetimer/config/CorsConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.debatetimer.config;

import com.debatetimer.exception.custom.DTInitializationException;
import com.debatetimer.exception.errorcode.InitializationErrorCode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
Expand All @@ -13,9 +15,21 @@ public class CorsConfig implements WebMvcConfigurer {
private final String[] corsOrigin;

public CorsConfig(@Value("${cors.origin}") String[] corsOrigin) {
validate(corsOrigin);
this.corsOrigin = corsOrigin;
}

private void validate(String[] corsOrigin) {
if (corsOrigin == null || corsOrigin.length == 0) {
throw new DTInitializationException(InitializationErrorCode.CORS_ORIGIN_EMPTY);
}
for (String origin : corsOrigin) {
if (origin == null || origin.isBlank()) {
throw new DTInitializationException(InitializationErrorCode.CORS_ORIGIN_STRING_BLANK);
}
}
}

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
Expand All @@ -30,7 +44,7 @@ public void addCorsMappings(CorsRegistry registry) {
)
.allowCredentials(true)
.allowedHeaders("*")
.exposedHeaders(HttpHeaders.AUTHORIZATION);
.exposedHeaders(HttpHeaders.AUTHORIZATION, HttpHeaders.CONTENT_DISPOSITION);
}
}

10 changes: 10 additions & 0 deletions src/main/java/com/debatetimer/config/JpaAuditingConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.debatetimer.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {

}
7 changes: 7 additions & 0 deletions src/main/java/com/debatetimer/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.debatetimer.config;

import com.debatetimer.controller.tool.export.ExcelExportInterceptor;
import com.debatetimer.controller.tool.jwt.AuthManager;
import com.debatetimer.service.auth.AuthService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
Expand All @@ -19,4 +21,9 @@ public class WebConfig implements WebMvcConfigurer {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthMemberArgumentResolver(authManager, authService));
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ExcelExportInterceptor());
}
}
Loading
Loading