개선 하기(어노테이션 기반 & 세션 저장소로 DB 사용)

본 포스팅은 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 보고 작성하였음

1. 어노테이션 기반

같은 코드가 계속해서 반복되는 부분 -> 어노테이션 기반으로 변경

  • ex) IndexController에서 세션 값을 가져오는 부분을 메소드 인자로 세션값을 바로 받을 수 있도록 변경

1.1 @LoginUser 어노테이션 생성

@Target(ElementType.PARAMETER) ⓐ
@Retention(RetentionPolicy.RUNTIME) ⓑ
public @interface LoginUser { ⓒ
}
  • ⓐ @Target(ElementType.PARAMETER)
    • 해당 어노테이션이 생성될 수 있는 위치를 지정함
    • PARAMETER로 지정했으니 메소드의 파라미터로 선언된 객체에서만 사용 할 수 있음
      • ElementType.PACKAGE : 패키지 선언
      • ElementType.TYPE : 타입 선언
      • ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언
      • ElementType.CONSTRUCTOR : 생성자 선언
      • ElementType.FIELD : 멤버 변수 선언
      • ElementType.LOCAL_VARIABLE : 지역 변수 선언
      • ElementType.METHOD : 메서드 선언
      • ElementType.PARAMETER : 전달인자 선언
      • ElementType.TYPE_PARAMETER : 전달인자 타입 선언
      • ElementType.TYPE_USE : 타입 선언
  • ⓑ @Retention(RetentionPolicy.RUNTIME)
    • Annotation 이 실제로 적용되고 유지되는 범위를 의미
    • RetentionPolicy.RUNTIME
      • 컴파일 이후에도 JVM 에 의해서 계속 참조가 가능
      • 주로 리플렉션이나 로깅에 많이 사용됨
    • RetentionPolicy.CLASS
      • 컴파일러가 클래스를 참조할 때가지 유효함
    • RetentionPolicy.SOURCE
      • 컴파일 전까지만 유효함
      • 즉, 컴파일 이후에는 사라지게 됨
  • ⓒ @interface
    • 해당 파일을 어노테이션 클래스로 지정(생성)

1.2 LoginUserArgumentResolver

@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");
    }
}
  • ⓐ supportsParameter()
    • 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단함
    • 상기 매서드에서는 파라미터에 @LoginUser 어노테이션이 붙어 있고, 파라미터 클래스 타입이 SessionUser.class인 경우 true를 반환함
  • ⓑ resolveArgument()
    • 파라미터에 전달할 객체를 생성함
    • 상기 소스에서는 세션에서 객체를 가져옴

1.3 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("userName", user.getName());
        }
        return "index";
    }
}
  • ⓐ @LoginUser SessionUser user
    • 기존 SessionUser user = (SessionUser) httpSession.getAttribute("user");로 가져오던 세션 정보 값이 개선 됨
    • 해당 커스텀 어노테션을 사용하면 세션 정보를 가져올 수 있게 됨

2. 세션 저장소로 데이터베이스 사용하기

2.1 세션(Session)?

  • 클라이언트와 웹서버 간 네트워크 연결이 지속 유지되고 있는 상태를 말함
  • 즉, 사용자가 브라우저를 열어 서버에 접속한 뒤 접속을 종료할 때가지의 시점을 이야기함
  • HTTP 프로토콜은 비접속형 프로토콜이므로, 매 접속마다 새로운 네트워크 연결이 이루어는데 세션이 연결 유지를 가능하게 함
  • 정보들이 서버단에 저장되기 때문에 보안 면에서 쿠키보다 우수함

2.2 다중 서버 환경에서 세션관리 방법

(참조 : https://hyuntaeknote.tistory.com/6)

  • Sticky Session (Load Balancer)
  • Session Clustering (TOMCAT 세션)
  • Session Storage
    • Disk Based Database (MySQL, Oracle, MS-SQL 등과 같은 관계형 데이터베이스)
    • In-Memory Database (Redis, Memcached 등)

2.2.1 Sticky Session

  • 고정된 세션
  • Load Balance가 기본적으로 라운드 로빈 방식으로 트래픽을 분산
  • 특정 사용자가 접속을 시도했을 때 처음 접속된 서버로 계속해서 접속되도록 트래픽을 처리하는 방식
  • 장점
    • 사용자는 세션이 유지되는 동안 동일한 서버만을 사용
    • 정합성 이슈에서 자유로움
  • 단점
    • 사용자가 접속해야하는 서버가 정해져 있기 때문에 특정 서버에 트래픽이 집중될 위험이 있음
    • 사용자가 자신의 세션이 없는 다른서버 이용불가
    • 가용성이 떨어짐

2.2.2 Session Clustering(세션 클러스터링)

  • 여러 대의 컴퓨터들이 하나의 시스템 처럼 동작하도록 만드는 것
  • WAS가 2대 이상 설치 형태일 때 동일한 세션으로 관리하는 것을 의미
  • 장점
    • 세션을 복제하여 사용자가 어떤 서버에 접속하더라도 데이터가 세션에 복제됨으로써 정합성 이슈 해결
    • 서버 하나에 장애가 발생하더라도 서비스는 중단되지 않고 운영 가능
  • 단점
    • 모든 서버가 동일한 세션 객체를 가져야하기 때문에 많은 메모리가 필요
    • 세션 저장소에 데이터가 저장될 때마다 모든 서버에 값을 입력해야함
    • 서버 수에 비례하여 네트워크 트래픽이 증가하는 등 성능저하가 발생

2.2.3 Session Storage(세션 저장소)

  • 서버가 아무리 늘어나도 세션 스토리지에 대한 정보만 각각의 서버에 입력해주면 세션을 공유할 수 있게됨
  • 트래픽이 비정상적으로 몰리는 현상을 고려하지 않아도 됨
  • 서버가 하나 장애가 발생하더라도 별도의 세션 저장소가 존재하기 때문에 서비스를 계속해서 제공할 수 있음 (가용성을 확보할 수 있음)
  • 여러대의 서버가 하나의 세션을 사용하기 때문에 데이터 불일치가 발생하지 않음 (정합성 문제 해결)
  • 별도의 세션 복제를 할 필요 없기에 성능적인 문제도 해결이 가능
  • But, 세션 저장소 서버 장애를 방지하기 위해 동일한 세션 저장소 하나를 더 구성하여 복제하는것이 좋음
  1. Disk Based Database
    • 디스크에 저장 및 사용
    • MySQL, Oracle, MS-SQL 등과 같은 관계형 데이터베이스
    • 세션 저장소를 할 수 있는 가장 쉬운방법
    • 많은 설정 필요 없음
    • 처리 속도가 오래걸림, DB I/O가 발생하여 성능상 이슈가 발생할 수 있음
    • 빈번한 Read/Write가 이루어지는 세션 저장소로써 Disk 기반의 데이터베이스는 상대적으로 I/O 속도가 느리기 때문에 적합하지 않음
    • 보통 로그인 요청이 많이 없는 백오피스, 사내 시스템 용도에서 많이 사용

  1. In-Memory Database
    • 메모리에 저장 및 사용
    • Redis, Memcached 등이 있음
    • 세션에 저장하는 데이터들은 영구적으로 저장하는 데이터가 아님
    • In-Memory 데이터베이스는 전원 공급이 중단되면 데이터를 잃어버리지만, 세션 저장소에 저장되는 데이터는 상대적으로 피해가 적기 때문에 In-Memory 데이터베이스 사용이 적합합니다. (일부 데이터베이스는 Replication을 지원하기 때문에 가용성을 확보할 수 있습니다.)
    • 데이터를 메모리에서 Read/Write 할 수 있다는 점에서 빠른 속도로 데이터를 처리할 수 있기 때문에 세션 저장소로써 적합

2.3 세션 저장소로 데이터베이스(Disk Based Database) 설정

  • gradle에 의존성 추가
    • implementation 'org.springframework.session:spring-session-jdbc'
  • application.properties에 추가
    • spring.session.store-type=jdbc

좋은 웹페이지 즐겨찾기