-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REFACTOR] AOP를 통한 쿠키 관리 로직 분리 #456
Changes from all commits
2a0b67a
e5ade1c
1eeb886
0f647dc
6e1c8fb
8406d6e
09fbd57
f5a5c62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package ddangkong.aop.cookie; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.CLASS) | ||
public @interface DeleteCookie { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package ddangkong.aop.cookie; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.CLASS) | ||
public @interface IssueCookie { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package ddangkong.aop.cookie; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.PARAMETER) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface MemberId { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package ddangkong.aop.cookie; | ||
|
||
import ddangkong.facade.room.dto.RoomJoinResponse; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.aspectj.lang.annotation.After; | ||
import org.aspectj.lang.annotation.AfterReturning; | ||
import org.aspectj.lang.annotation.Aspect; | ||
import org.aspectj.lang.annotation.Pointcut; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.ResponseCookie; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.context.request.RequestContextHolder; | ||
import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
||
@Slf4j | ||
@Aspect | ||
@Component | ||
public class RoomMemberCookieAspect { | ||
|
||
private final RoomMemberCookieEncryptor roomMemberCookieEncryptor; | ||
|
||
public RoomMemberCookieAspect(RoomMemberCookieEncryptor roomMemberCookieEncryptor) { | ||
this.roomMemberCookieEncryptor = roomMemberCookieEncryptor; | ||
} | ||
|
||
@Pointcut("@annotation(ddangkong.aop.cookie.IssueCookie)") | ||
public void issueCookie() { | ||
} | ||
|
||
@Pointcut("@annotation(ddangkong.aop.cookie.DeleteCookie)") | ||
public void deleteCookie() { | ||
} | ||
|
||
@AfterReturning(value = "issueCookie()", returning = "roomJoinResponse") | ||
public void handleIssueCookie(RoomJoinResponse roomJoinResponse) { | ||
HttpServletRequest request = getHttpServletRequest(); | ||
HttpServletResponse response = getHttpServletResponse(); | ||
String origin = request.getHeader(HttpHeaders.ORIGIN); | ||
|
||
ResponseCookie encodedCookie = roomMemberCookieEncryptor.getEncodedCookie(roomJoinResponse.member().memberId(), origin); | ||
response.addHeader(HttpHeaders.SET_COOKIE, encodedCookie.toString()); | ||
} | ||
|
||
@After("deleteCookie()") | ||
public void handleDeleteCookie() { | ||
HttpServletRequest request = getHttpServletRequest(); | ||
HttpServletResponse response = getHttpServletResponse(); | ||
String origin = request.getHeader(HttpHeaders.ORIGIN); | ||
|
||
ResponseCookie deleteCookie = roomMemberCookieEncryptor.deleteCookie(origin); | ||
response.addHeader(HttpHeaders.SET_COOKIE, deleteCookie.toString()); | ||
} | ||
|
||
private HttpServletRequest getHttpServletRequest() { | ||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); | ||
return servletRequestAttributes.getRequest(); | ||
} | ||
|
||
private HttpServletResponse getHttpServletResponse() { | ||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); | ||
return servletRequestAttributes.getResponse(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package ddangkong.config; | ||
|
||
import ddangkong.resolver.RoomMemberArgumentResolver; | ||
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.WebMvcConfigurer; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
public class CookieConfig implements WebMvcConfigurer { | ||
|
||
private final RoomMemberArgumentResolver roomMemberArgumentResolver; | ||
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(roomMemberArgumentResolver); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package ddangkong.resolver; | ||
|
||
import ddangkong.aop.cookie.MemberId; | ||
import ddangkong.aop.cookie.RoomMemberCookieEncryptor; | ||
import ddangkong.exception.room.NotFoundCookieException; | ||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import java.util.Arrays; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.core.MethodParameter; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.bind.support.WebDataBinderFactory; | ||
import org.springframework.web.context.request.NativeWebRequest; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.method.support.ModelAndViewContainer; | ||
|
||
@Component | ||
public class RoomMemberArgumentResolver implements HandlerMethodArgumentResolver { | ||
|
||
private final RoomMemberCookieEncryptor roomMemberCookieEncryptor; | ||
|
||
private final String cookieKey; | ||
|
||
public RoomMemberArgumentResolver(RoomMemberCookieEncryptor roomMemberCookieEncryptor, @Value("${cookie.rejoin-key}") String cookieKey) { | ||
this.roomMemberCookieEncryptor = roomMemberCookieEncryptor; | ||
this.cookieKey = cookieKey; | ||
} | ||
|
||
@Override | ||
public boolean supportsParameter(MethodParameter parameter) { | ||
boolean isLongType = parameter.getParameterType().equals(Long.class); | ||
boolean hasParameterAnnotation = parameter.hasParameterAnnotation(MemberId.class); | ||
return isLongType && hasParameterAnnotation; | ||
} | ||
|
||
@Override | ||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, | ||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { | ||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); | ||
Cookie[] cookies = request.getCookies(); | ||
if (cookies == null) { | ||
throw new NotFoundCookieException(); | ||
} | ||
Cookie cookie = Arrays.stream(request.getCookies()) | ||
.filter(curCookie -> curCookie.getName().equals(cookieKey)) | ||
.findAny() | ||
.orElseThrow(NotFoundCookieException::new); | ||
|
||
String cookieValue = cookie.getValue(); | ||
return roomMemberCookieEncryptor.getDecodedCookieValue(cookieValue); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
import ddangkong.aop.cookie.EncryptionUtils; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 아래서 사용되는 import 문 맞을까요? 확인 부탁드릴께요~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 어떤 의도에 질문인지 잘 이해가 안됐어요. 좀 더 자세히 설명이 가능할까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅇ? 이 때 안쓰는 import로 봤던 것 같은데... 아니었나? |
||
import ddangkong.controller.BaseControllerTest; | ||
import ddangkong.exception.room.InvalidCookieException; | ||
import org.junit.jupiter.api.Nested; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import ddangkong.aop.cookie.RoomMemberCookieEncryptor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
import ddangkong.controller.BaseControllerTest; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,13 @@ | |
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import ddangkong.aop.cookie.EncryptionUtils; | ||
import ddangkong.aop.cookie.RoomMemberCookieAspect; | ||
import ddangkong.aop.cookie.RoomMemberCookieEncryptor; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.restdocs.RestDocumentationContextProvider; | ||
import org.springframework.restdocs.RestDocumentationExtension; | ||
|
@@ -18,6 +22,7 @@ | |
import org.springframework.web.context.WebApplicationContext; | ||
|
||
@ExtendWith(RestDocumentationExtension.class) | ||
@Import(value = {RoomMemberCookieEncryptor.class, EncryptionUtils.class, RoomMemberCookieAspect.class}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BaseDocumentTest에 위치시킨 이유: |
||
public abstract class BaseDocumentationTest { | ||
|
||
@Autowired | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleIssueCookie
는 정상 작동을 완료했을 때 작동하는데,handleDeleteCookie
는 정장 작동 했을 뿐만 아니라 예외가 던져졌을 때도 작동하는 이유가 있나요?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리턴값이 필요했기 때문에 AfterReturning를 사용했었는데, After가 '정상 작동' + '예외 발생 시'를 포함하는줄은 몰랐네요! 커찬 덕분에 학습하게 되었어요!
직접 테스트해보니까 위 같은 프로세스로 진행되더라고요!
쿠키 삭제 로직은 정상 작동을 하든, 예외가 발생하든 삭제되어야 하는 로직흐름이 맞을 것 같아요! 방을 나가고 더 이상 사용하지 못하도록 해야하기 때문이죠! 그렇기에 After로 두는게 맞을 것 같습니다!