Skip to content

Commit

Permalink
Merge pull request #58 from saessagMarket/feat/#57-manage-session-util
Browse files Browse the repository at this point in the history
RequestContextHolder를 이용한 세션 관리
  • Loading branch information
ahyeonkong authored Jan 30, 2025
2 parents a30a292 + 15e2509 commit a237bc0
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 24 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ dependencies {
implementation 'software.amazon.awssdk:core:2.20.0'
implementation 'software.amazon.awssdk:auth:2.20.0'
implementation 'me.paulschwarz:spring-dotenv:3.0.0'
implementation 'org.springframework:spring-web'

}

test {
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/market/saessag/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.market.saessag.global.config;

import com.market.saessag.global.interceptor.SessionInterceptor;
import com.market.saessag.global.util.PathConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private SessionInterceptor sessionInterceptor;
@Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(sessionInterceptor)
.addPathPatterns("/**") // 모든 경로에 인터셉터 적용
.excludePathPatterns(PathConst.EXCLUDED_PATHS); // 제외할 경로 설정
}
}
23 changes: 14 additions & 9 deletions src/main/java/com/market/saessag/global/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ErrorCode {
DUPLICATE_EMAIL(400, "이미 가입된 이메일입니다"),
VERIFICATION_EXPIRED(400, "인증 시간이 만료되었습니다"),
INVALID_VERIFICATION_CODE(400, "잘못된 인증 코드입니다"),
EMAIL_NOT_VERIFIED(400, "이메일 인증이 필요합니다. 먼저 이메일 인증을 완료해주세요."),
USER_NOT_FOUND(404, "존재하지 않는 사용자입니다"),
EMAIL_SEND_FAILED(500, "이메일 발송에 실패했습니다"),
INVALID_EMAIL(400, "유효하지 않은 이메일입니다"),
INVALID_PASSWORD(400, "현재 비밀번호가 일치하지 않습니다");
DUPLICATE_EMAIL(HttpStatus.UNAUTHORIZED, "이미 가입된 이메일입니다"),
VERIFICATION_EXPIRED(HttpStatus.UNAUTHORIZED, "인증 시간이 만료되었습니다"),
INVALID_VERIFICATION_CODE(HttpStatus.UNAUTHORIZED, "잘못된 인증 코드입니다"),
EMAIL_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "이메일 인증이 필요합니다. 먼저 이메일 인증을 완료해주세요."),
USER_NOT_FOUND(HttpStatus.UNAUTHORIZED, "존재하지 않는 사용자입니다"),
EMAIL_SEND_FAILED(HttpStatus.UNAUTHORIZED, "이메일 발송에 실패했습니다"),
INVALID_EMAIL(HttpStatus.UNAUTHORIZED, "유효하지 않은 이메일입니다"),
INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, "현재 비밀번호가 일치하지 않습니다"),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "로그인이 필요한 서비스입니다."),
INVALID_ARGUMENT(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."),
SESSION_EXPIRED(HttpStatus.UNAUTHORIZED, "세션이 만료되었습니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다");

private final int status;
private final HttpStatus status;
private final String message;

}
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
package com.market.saessag.global.exception;

import com.market.saessag.global.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

// 1. 세션 관련 예외를 먼저 처리
@ExceptionHandler(HttpSessionRequiredException.class)
public ResponseEntity<ErrorResponse> handleSessionException(HttpSessionRequiredException e) {
ErrorResponse response = ErrorResponse.of(ErrorCode.UNAUTHORIZED);
return ResponseEntity.status(response.getStatus()).body(response);
}

// 2. IllegalArgumentException 처리
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException e) {
ErrorResponse errorResponse = ErrorResponse.builder()
.message(e.getMessage())
.status(HttpStatus.BAD_REQUEST.value())
.build();

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(errorResponse);
ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_ARGUMENT);
return ResponseEntity.status(response.getStatus()).body(response);
}

// 공통된 예외 처리
// 3. 공통된 예외 처리
@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
ErrorResponse response = ErrorResponse.of(e.getErrorCode());
return ResponseEntity.status(response.getStatus()).body(response);
}

// 기타 서버 예외 처리
// 4. 기타 서버 예외 처리
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
ErrorResponse response = ErrorResponse.builder()
.message("서버 내부 오류가 발생했습니다")
.status(500)
.build();
ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR);
return ResponseEntity.status(response.getStatus()).body(response);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.market.saessag.global.interceptor;

import com.market.saessag.global.exception.CustomException;
import com.market.saessag.global.exception.ErrorCode;
import com.market.saessag.global.util.PathConst;
import com.market.saessag.global.util.SessionUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class SessionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestUrl = request.getRequestURI();

// PathConst의 허용된 URL 목록 사용
for (String url : PathConst.EXCLUDED_PATHS) {
if (requestUrl.startsWith(url)) {
return true;
}
}

// 그 외의 URL은 세션 체크 후 에러 처리
if (SessionUtil.getData("user") == null) {
throw new CustomException(ErrorCode.UNAUTHORIZED);
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import com.market.saessag.global.exception.ErrorCode;
import lombok.*;
import org.springframework.http.HttpStatus;

@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class ErrorResponse {
private String message;
private int status;
private HttpStatus status;

public static ErrorResponse of(ErrorCode errorCode) {
return ErrorResponse.builder()
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/market/saessag/global/util/PathConst.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.market.saessag.global.util;

// 제외할 경로 공통 상수 클래스
public class PathConst {
public static final String[] EXCLUDED_PATHS = {
"/api/sign-up",
"/api/sign-in",
"/error"
};

private PathConst() {} // 인스턴스화 방지
}
36 changes: 36 additions & 0 deletions src/main/java/com/market/saessag/global/util/SessionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.market.saessag.global.util;

import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Component
public class SessionUtil {
// 세션 가져오기
public static HttpSession getSession() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attributes.getRequest().getSession();
}

// 세션에 데이터 저장
public static void setData(String key, Object value) {
getSession().setAttribute(key, value);
}

// 세션에서 데이터 가져오기
public static Object getData(String key) {
return getSession().getAttribute(key);
}

// 특정 세션 삭제
public static void removeData(String key) {
getSession().removeAttribute(key);
}

// 모든 세션 삭제
public static void clear() {
getSession().invalidate();
}
}

0 comments on commit a237bc0

Please sign in to comment.