이 글은 '스프링부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱(jojoldu)'을 공부하며 작성한 글로 생략된 내용은 책을 구매해서 확인하는 것을 권장합니다.
참고 소스코드 깃허브 https://github.com/jojoldu/freelec-springboot2-webservice
http://www.yes24.com/Product/Goods/83849117
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - YES24
가장 빠르고 쉽게 웹 서비스의 모든 과정을 경험한다. 경험이 실력이 되는 순간!이 책은 제목 그대로 스프링 부트와 AWS로 웹 서비스를 구현한다. JPA와 JUnit 테스트, 그레이들, 머스테치, 스프링
www.yes24.com
책의 5장에 해당하는 내용 중 중복 코드 최소화를 위해 어노테이션 기반으로 개선하는 것과 세션 저장소 3가지와 jdbc 등록 에 대해 정리하고자 한다.
어노테이션 기반으로 개선하는 이유
같은 코드가 여러 곳에서 반복된다면 수정이 필요할 때, 작성된 부분을 다 찾아서 바꿔주어야 한다. 이렇게 되면 수정이 반영되지 않은 코드가 생길 수도 있어, 유지보수성이 떨어진다.
- 같은 코드 반복을 최소화 하기 위해서 사용
앞서 진행한 과정에서 개선할 수 있는 부분은 IndexController에서 세션 값을 가져오는 부분이다. 이는 다른 컨트롤러와 메소드에서 세션 값이 필요하면 그때마다 가져오는 코드를 추가해야 한다.
-> 중복 코드의 작성을 방지하기 위해서 메소드 인자로 세션 값을 받을 수 있도록 변경할 필요가 있다.
어노테이션 기반으로 개선(메소드 인자로 세션값을 받도록)
@LoginUser 생성 (config/auth/)
//이 어노테이션이 생성될 수 있는 위치 결정 - 메소드의 파라미터로 선언된 객체에서만 사용할 수 있도록 함
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}
@Target: 어노테이션이 생성될 수 있는 위치 결정 (import문을 추가 할 때, java.lang.annotation로 선택해서 추가)
@interface: 이 파일을 어노테이션 클래스로 지정한다는 의미
LoginUserArgumentResolver 클래스 생성 (config/auth/)
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
private final HttpSession httpSession;
@Override
//supportsParameter() - 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단
public boolean supportsParameter(MethodParameter parameter){
//파라미터에 @LoginUser 어노테이션이 붙어있고 파라미터 클래스 타입이 SessionUser.class라면 true 리턴
boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
return isLoginUserAnnotation && isUserClass;
}
@Override
//resolveArgument() - 파라미터에 전달될 객체 생성
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory inderFactory) throws Exception{
//세션에서 객체를 가져옴
return httpSession.getAttribute("user");
}
}
supportsParameter(): 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단
resolveArgument(): 파라미터에 전달될 객체 생성
위까지 작성했다면 @LoginUser를 사용하기 위한 환경이 구성되었다.
WebConfig 클래스 생성 (config/)
LoginUserArgumentResolver가 스프링에서 인식될 수 있도록 WebMvcConfigurer에 추가
@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers){
argumentResolvers.add(loginUserArgumentResolver);
}
}
* HandlerMethodArgumentResolver는 WebMvcConfigurer addArgumentResolvers()를 통해서 추가해야 한다.
3. IndexController의 코드 @LoginUser를 사용해서 개선
IndexController 수정
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
@GetMapping("/")
public String index(Model model, @LoginUser SessionUser user){
model.addAttribute("posts",postsService.findAllDesc());
if(user != null){
model.addAttribute("name",user.getName());
}
return "index";
}
...
}
원래 코드에서 세션 정보 값을 가져오는 코드 부분을 삭제하고 @LoginUser SessionUser를 인자로 받아 세션 정보 값을 가져온다. 이렇게 하면 어느 컨트롤러에서나 이제 @LoginUser만 사용하면 세션 정보를 가져올 수 있다.
재실행후 로그인하면 정상적으로 작동한다.
세션저장소로 데이터베이스 사용
지금 이 프로젝트는 내장 톰캣 메모리에 세션이 저장되기 때문에, 재 실행할 때마다 로그인이 풀린다.
기본적으로 세션은 실행되는 WAS(Web Application Server)의 메모리에 저장되고 호출되는데, 메모리에 저장되다 보니 애플리케이션을 실행할 때마다 항상 초기화된다.
-> 배포할 때마다 톰캣이 재시작되는 문제 존재
-> 2대이상의 서버에서 서비스시 톰캣마다 세션 동기화를 해주어야 하는 문제
그래서 실무에서는 보통 세션저장소 3가지 중 하나를 선택한다.
세션 저장소 3가지
- 톰캣세션 사용
- 기본적으로 선택되는 방식(별다른 설정x)
- 톰캣(WAS)에 저장되기 때문에, 2대이상의 WAS가 구동된다면 톰캣들간의 세션 공유를 위한 추가 설정 필요
- 세션저장소로 데이터베이스 사용 ex)MySQL 등
- 여러 WAS간의 공용 세션을 사용할 수 있는 가장 쉬운 방법
- 많은 설정 필요없지만 로그인 요청마다 DB IO가 발생 -> 성능상 이슈가 발생할 수 있다.
- 로그인 요청이 많지 않은 백오피스나 사내 시스템 용도로 사용한다.
- Redis, Memcahed와 같은 메모리 DB를 세션 저장소로 사용
- B2C 서비스에서 주로 사용
- 실제 서비스 사용하기 위해서는 Embedded Redis와 같은 방식이 아닌 외부 메모리 서버 필요
이 프로젝트에서는 사용자가 많은 서비스가 아니기 때문에, 설정이 간단하고 비용이 절감되는 데이터베이스를 사용하는 방법을 선택한다. 특히 3번째 방법인 레디스와 같은 메모리 DB를 사용할 경우, AWS에 서비스 배포시 레디스와 같은 서비스(엘라스틱 캐시)에 별도로 사용료 지불이 필요하다.
spring-session-jdbc 등록
build.gradle에 의존성 추가
implementation('org.springframework.session:spring-session-jdbc')
application.properties 코드 추가
세션저장소를 jdbc로 지정
spring.session.store-type=jdbc
애플리케이션을 다시 실행하고 로그인 한 뒤, h2-console에 들어가보면
세션을 위한 테이블 SPRING_SESSION, SPRING_SESSION_ATTRIBUTES가 생성된 것을 확인할 수 있다. 등록된 세션은 방금한 구글로그인으로 생성된 세션이다.
H2 기반으로 스프링이 재실행되면서 H2도 재시작 되기 때문에, 지금도 재시작하면 세션이 풀린다.
이후 AWS로 배포하게 되면 AWS의 데이터베이스 서비스인 RDS(Relational Database Service)를 사용하게 되므로 그때부터는 세션이 풀리지 않는다.
'스프링부트와 AWS로 혼자 구현하는 웹서비스' 카테고리의 다른 글
스프링 부트 기존 테스트에 시큐리티 적용하기 (feat. 책과 달리 테스트 4개 실패하는 이유) (0) | 2023.03.08 |
---|---|
스프링 시큐리티와 OAuth2.0으로 로그인 구현하기 - 네이버 로그인 (0) | 2023.03.07 |
스프링 시큐리티와 OAuth2.0으로 로그인 구현하기 - 구글 로그인 (0) | 2023.03.06 |
스프링 부트 게시글 등록 및 전체 조회 목록, 수정, 삭제 화면 구현 (0) | 2023.03.01 |
스프링 부트 서버 템플릿 엔진과 클라이언트 템플릿 엔진 차이 (feat. 머스테치 사용법) (2) | 2023.03.01 |
댓글