Blog

[Spring-LDW] 소셜 로그인 부분 어노테이션 기반으로 리팩토링

Category
Author
PinOnMain
1 more property
스프링 부트와 AWS로 혼자 구현하는 웹 서비스  : 인텔리제이, JPA, JUnit 테스트, 그레이들, 소셜 로그인, AWS 인프라로 무중단 배포까지 이동욱 저
Table of Content

개선이 필요한 나쁜 코드?

일반적으로 클린코드를 지향한다면 어떤 것들을 살펴봐야 할까? 교재에서는 예제에 대해서 대표적으로 다음과 같은 내용이 나타났다.
가장 대표적인 것은 같은 코드가 반복되는 부분이라고 생각한다.
이것 외에 내가 생각하는 부분은 클래스 및 변수, 메소드 등 개발자가 직접 명명 할 수 있는 것들의 의미가 그대로 사용되어야 한다고 생각한다. 이 부분은 여러 선배 개발자분들, 멘토님들을 통해서 하나의 예시로 gubun 이라는 알수없는 변수명의 사진을 자주 보았다. 코드 자체로 의미가 전달이 될 정도로 category 와 같이 영어 명사로, 메소드는 간략한 동사로 작성하는 것이 좋다는 생각이 들었다. 따라서 교재의 어노테이션 진행 추가와 더불어 SessionUser user로 되어있던 변수명을 SessionUser sessionUser로 변수 명을 좀더 명확하게 추가했다. 이러한 코드 정리를 통해서 유지 + 보수성을 향상 시키는 목적을 항상 생각해야겠다.
IndexContoller에서 세션값을 가져오는 부분의 개선이 필요하다.
SessionUser user = (SessionUser) httpSession.getAttribute("user");
교재에서는 위 부분의 중복을 개선하고자 한다고 한다. 나는 user라는 변수명도 그대로 sessionUser가 되면 좋겠다고 추가적으로 생각했다. 물론 user로 알아볼 수 있는 단계이지만 무슨 유저인지 그대로 그 아래 코드를 별도로 분리해서 보면 개선할 필요도 있다고 생각했다.

@LoginUser 어노테이션 생성하기

LoginUser.java 인터페이스 생성하기

어노테이션 인터페이스를 직접 생성하여 중복된 코드를 모듈화 시키는 방법을 사용하고자 한다.
package com.citefred.ldwspring.config.auth; 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 LoginUser { }
Java
복사

@Target(ElementType.PARAMETER)

어노테이션이 생성될 위치 지정(파라미터)
메소드의 파라미티로 선언된 객체에서 사용 할 수 있도록 지정됨
이 외에도 클래스 선언문에 쓸 수 있는 타입 등이 있음

@interface

이 파일을 어노테이션 클래스로 지정
@LoginUser 를 통해서 이 어노테이션을 사용 할 수 있음

LoginUserArgumentResolver.java

HandlerMethodArgumentResolver 인터페이스를 구현한 클래스를 작성. 조건에 맞는 경우 메소드가 있으면 이 구현체 클래스가 지정한 값으로 해당 메소드의 파라미터로 넘기는 용도
package com.citefred.ldwspring.config.auth; import com.citefred.ldwspring.config.auth.dto.SessionUser; import lombok.RequiredArgsConstructor; 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; import javax.servlet.http.HttpSession; @RequiredArgsConstructor @Component public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver { private final HttpSession httpSession; @Override public boolean supportsParameter(MethodParameter parameter) { boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null; boolean isUserClass = SessionUser.class.equals(parameter.getParameterType()); return isLoginUserAnnotation && isUserClass; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return httpSession.getAttribute("user"); } }
Java
복사

supportsParameter(MethodParameter parameter)

컨트롤러 메소드의 특정 파라미터를 지원하는지 판단하는 메소드
여기서는 파라미터에 @LoginUser 어노테이션이 작성되어있고 파라미터 클래스 타입이 SessionUser.class인 경우 true를 반환

resolveArgument( … )

파라미터에 전달할 객체 생성
여기선 세션에서 객체를 가져옴
위 코드들을 통해 @LoginUser 어노테이션이 사용하기 위한 환경은 구성 됨

생성한 어노테이션을 사용 할 수 있도록 설정

LoginUserArgumentResolver가 스프링에서 인식 될 수 있도록 WebMvcConfigurer에 추가해야 한다.

WebConfig.java

HandlerMethodArgumentResolver는 항상 WebMvcConfigureradd.ArgumentResolvers() 메소드를 통해 추가해야 한다.
package com.citefred.ldwspring.config; import com.citefred.ldwspring.config.auth.LoginUserArgumentResolver; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @RequiredArgsConstructor @Configuration public class WebConfig implements WebMvcConfigurer { private final LoginUserArgumentResolver loginUserArgumentResolver; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(loginUserArgumentResolver); } }
Java
복사

생성한 어노테이션을 사용해보기

IndexController.java

IndexController의 코드에서 반복되는 코드를 모두 @LoginUser를 사용하여 앞서 지적되었던 중복 문제를 개선해보기
package com.citefred.ldwspring.web; import com.citefred.ldwspring.config.auth.LoginUser; import com.citefred.ldwspring.config.auth.dto.SessionUser; import com.citefred.ldwspring.service.PostsService; import com.citefred.ldwspring.web.dto.PostsResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @RequiredArgsConstructor @Controller public class IndexController { private final PostsService postsService; @GetMapping("/") public String index(Model model, @LoginUser SessionUser sessionUser){ model.addAttribute("posts", postsService.findAllDesc()); if (sessionUser != null) { model.addAttribute("userName", sessionUser.getName()); } return "index"; } ... }
Java
복사

@LoginUser SessionUser sessionUser

기존 SessionUser user = (SessionUser) httpSession.getAttribute("user"); 처럼 메소드마다 객체를 생성하여 가져오던 로그인 유저 세션 정보 값을 이제 @LoginUser 어노테이션으로 가져 올 수 있음
추가적인 로그인 유저 정보가 필요한 메소드를 작성한다면 동일한 방법으로 어노테이션으로 유저 정보를 가져 올 수 있음

어노테이션 적용 로그인 작동 테스트

코드 리팩토링 이후 소셜 로그인이 정상적으로 작동하는지 테스트
Search
 | Main Page | Category |  Tags | About Me | Contact | Portfolio
Context
Table of Content

Heading 1

Context

Heading 2

Context

Heading 3

Context

Toggle H1

Toggle H2

Toggle H3

Search
 | Main Page | Category |  Tags | About Me | Contact | Portfolio