[ EnjoyDelivery ] 이슈 3. 다중 서버에서 인증 정보는 어디에 저장할까 : Session Storage

Dayeon myeong·2021년 12월 4일
0

Enjoy Delivery

목록 보기
3/4
post-custom-banner

이 글은 MindDiary 이슈 10. 다중 서버에서 인증 정보는 어디에 저장할까 : 토큰 인증 방식글과 이어집니다.

해당 프로젝트에서는 사용자 인증 시 세션 인증 방식을 사용합니다.

다중 서버의 상황에서는 서버별로 세션이 존재하기 때문에 만약 인증 정보가 있는 서버가 아닌 다른 서버로 요청이 간다면 문제가 발생합니다. 즉, 데이터 정합성 이슈가 발생합니다. 문제를 해결하기 위해 sticky session, session clustering, session storage 방식으로 문제를 해결합니다.

Sticky Session

Sticky Session 방법은 세션이 sticky 하다. 고정되었다라고 생각하면 됩니다. 서버가 3개가 있는 경우 클라이언트의 세션이 1번 서버에서 사용되었다면 이 후에도 이 사용자의 request는 1번 서버로만 보내집니다. 이렇게 고정된 서버로 보내지기 위해서는 Load Balancer가 필요합니다. 로드밸런서는 먼저 클라이언트의 요청을 받으면 쿠키에 저장된 서버 정보를 통해 지정된 서버로 요청을 전달합니다.

이 방식은 동일한 서버만 계속 사용하기 때문에 데이터 정합성 이슈에서 자유로울 수 있습니다. 하지만 고정된 세션이란 것은 특정 서버에 트래픽 과부하가 일어날 수 있습니다. 또한 자신이 사용하는 세션에 장애가 발생되면 해당 세션에 저장된 여러 사용자의 세션 정보는 사라지게 됩니다. 즉, 가용성이 떨어집니다. 그렇기 때문에 Sticky Session은 사용하지 않았습니다.

Session Clustering

Session Clustering 방법은 세션 여러대를 하나의 시스템처럼 동작하는 것입니다.
대표적으로 Tomcat은 all-to-all 세션 복제 방식이 있습니다. all-to-all 세션 복제란 하나의 세션에서 변경이 일어나면 다른 모든 세션에서 복제하는 방식입니다.

이 방식은 여러 대의 세션에서 모두 같은 내용이 저장되기 때문에 데이터 정합성 문제는 일어나지 않습니다. 하지만 중복된 데이터들이 저장된다는 점은 많은 메모리가 필요하며 모든 세션이 같은 내용을 복제해야 되니 네트워크 트래픽이 늘어납니다. 따라서 메모리나 네트워크 트래픽과 같은 성능 문제로 인해 사용하지 않았습니다.

Session Storagy

Session Storagy 방법은 기존 서버가 갖고 있는 세션 저장소를 사용하는 것이 아니라, 외부에 별도의 세션저장소를 두고 사용하는 방식입니다.

이 방식은 여러 대의 서버가 모두 같은 세션 저장소를 사용하기 때문에 데이터 정합성 문제는 일어나지 않습니다. 하지만 모두 같은 세션 저장소를 사용한다는 것은 트래픽 과부하가 발생할 수 있습니다. 세션 저장소 자체에 문제가 발생하면 수많은 클라이언트의 세션 정보가 사라질 수도 있습니다.

Session Storagy 방식을 사용한 이유

3가지 모두 데이터 정합성 이슈는 없지만, 트래픽 부하 , 장애 발생과 같은 문제가 있습니다.

하지만 Session Storagy는 서버 간 네트워크 요청이 늘어나 네트워크 트래픽 문제가 발생하지 않아 Session Clustering의 단점을 해결할 수 있습니다. 또한 서버와 세션 저장소가 분리되어 있기 때문에 서버의 영향이 세션까지 미치지 않습니다.

또한 장애가 발생하여 세션들이 다 사라지는 점에서는 생각해봐야할 점이 있습니다.
세션에 저장되는 인증정보는 영구적으로 저장되어야하는 데이터가 아닙니다. 단지 로그인을 유지하기 위해서만 필요한 것입니다. 만약 세션 데이터가 소멸되도 사용자는 다시 로그인을 하면 됩니다.

가장 좋은 점은 세션이 하나이기 때문에 개발자 입장에서 관리하기 편하다는 점입니다..

따라서 Session Storagy 방식을 사용하기로 했습니다.

Redis 사용 이유

Session Storagy로 Redis를 사용했습니다.

Mysql과 같은 디스크 기반의 DB는 보통 디스크 읽고 쓰는 작업에서 랜덤 I/O가 일어난다고 합니다. 랜덤 I/O란 하드 디스크 드라이브의 플래터(원판)을 돌려서 읽어야 할 데이터가 저장된 위치로 디스크 헤더를 이동시킨 다음, 데이터를 읽는 것입니다. 만약 많은 양의 데이터를 읽어야 한다면 디스크 헤더도 많이 이동하고 랜덤 I/O도 많이 일어납니다. 시스템 콜이 늘어나고 속도도 Redis보다 느립니다. 인덱스를 사용해서 꼭 필요한 특정 사용자의 인증정보만 읽도록 한다면 랜덤 I/O를 줄일 순 있을 지 모릅니다.

하지만 세션에 저장될 정보는 영구적으로 저장되어야하는 데이터가 아닙니다. 인증 정보가 다 영구 저장되어야 한다면 메모리 문제가 발생합니다. 게다가 세션에 저장된 인증 정보가 손실되어도 사용자는 재로그인을 하면 되기 때문에 Redis를 사용했습니다.

또한 기존에 장바구니 기능을 Redis 캐시를 사용했기 때문에 설정만 추가하면 된다는 점이 편리하다는 점이 있기도 합니다...

코드

//build.gradle에 dependencies
	    implementation 'org.springframework.session:spring-session-data-redis'

 @EnableRedisHttpSession
...
 @SpringBootApplication
 public class EnjoyDeliveryApplication {
 ...

스프링에서는 Redis를 세션 저장소로 쓰도록 하기 위한 dependency가 존재하여 다루기 쉽습니다.


@Service
@RequiredArgsConstructor
public class SessionLoginService implements LoginService {

  private static final String USER = "user";
  private final HttpSession session;

//로그인 시 "user" 속성에 인증 정보 객체를 저장합니다.
  @Override
  public void loginUser(UserInfo userInfo) {
    session.setAttribute(USER, userInfo);
  }
//인증 정보 객체를 가져옵니다.
  @Override
  public UserInfo getCurrentUserInfo() {
    return (UserInfo) session.getAttribute(USER);
  }
}

로그인시 세션 쿠키가 없다면 새로운 세션을 만듭니다. 이 때 이 세션에 key-value 형식으로 ("user", 인증 정보 userInfo) 를 저장합니다. 그리고 클라이언트에게 해당 세션의 세션 ID를 전달합니다. 클라이언트는 이후 요청할 때마다 세션 ID가 담긴 세션 쿠키를 전달합니다. 만약 인증정보가 필요하면 세션 ID와 매핑되는 기존의 세션에서 인증정보를 가져옵니다.

위와 같은 방식은 이전 사용했던 JWT 토큰보다는 코드 량도 줄고 사용이 간편했습니다. 또한 다중 서버환경에서 데이터 정합성 이슈를 해결 할 수 있고, 서버와의 분리로 서버 장애에 영향을 받지 않게 되었습니다.

Session Storage 의 단점과 해결방법

  • 네트워크 트래픽 몰릴 수 있음
    Replication으로 문제를 해결 할 수 있다.
    쓰기 연산 master 읽기 연산 storage를 늘려서 읽기용 서버를 늘려 부하를 분살 할 수 있다.

참고 문헌

Sopt 자료

우아한 테크톡

Real Mysql

[#1] 서버가 여러대면 로그인 정보는 어디에 저장할까? - Sticky Session, Session Clustering, Redis Session Storage

다중 서버 환경에서 Session은 어떻게 공유하고 관리할까? - 2편(Sticky Session, Session Clustering, Session Storage 분리

HttpSession은 "언제" 만들어질까?

profile
부족함을 당당히 마주하는 용기
post-custom-banner

0개의 댓글