본문 바로가기
개발 이야기/오늘의 학습

로그인 기능 구현하기

by 리태 2024. 2. 19.
해당 글은 학습한 내용을 정리하기 위해 작성한 것으로 오류나 내용상 부족한 점이 있을 수 있습니다.

 

 

사이드 프로젝트를 진행하면서 항상 필요했던 기능 중 하나가 바로 로그인 기능이었습니다. 기본적인 로그인과 깃허브, 구글 등을 통한 Oauth 기능을 적용해보았지만 클라이언트를 통해 백엔드로 넘어간 데이터가 어떻게 관리되고 구현되는지 알아보고자 학습하게 되었습니다.

 

 

 

기본적인 로그인 처리 방식

 

이미지 1. 로그인 처리 방식

 

로그인 처리 과정은 크게 인증(Authentication)인가(Authorization) 두 단계로 구분됩니다. 인증은 사용자가 입력한 로그인 정보와 DB에 저장된 사용자 정보가 일치하는지 여부를 확인하는 과정이고, 인가는 사용자가 요청한 요청에 대해 권한을 가지고 있는지 여부를 확인하는 과정을 말합니다.

 

 

인증(Authentication)

사용자가 브라우저에서 아이디와 패스워드를 입력하고 로그인을 요청하면 백엔드에서는 전달받은 사용자 입력 정보를 DB에 저장된 사용자 정보와 비교합니다. DB에 존재하는 사용자일 경우 백엔드의 메모리 세션에 로그인 정보를 저장하고 세션 ID를 브라우저에 전달합니다. 브라우저는 백엔드로부터 받은 세션 ID를 브라우저의 저장소(localStorage, sesstionStorage, cookie 등)에 저장합니다.

 

인가(Authorization)

사용자의 정보를 가져오는 fetchUser라는 API를 예시로 인가의 과정 살펴보도록 하겠습니다. 사용자가 백엔드에 요청을 보낼 때 브라우저에 저장된 세션 ID를 포함하여 요청을 보냅니다. 백엔드에서는 요청에 포함된 세션 ID와 메모리 세션에 저장된 세션 ID를 비교하고 일치하는 경우 DB에 사용자 정보를 요청합니다. DB에서 제공한 사용자 정보는 백엔드를 거쳐 사용자(브라우저)가 전달받게 됩니다.

 

 

 

기본적인 로그인 방식의 문제와 해결

백엔드 메모리 세션의 메모리 부족

기본적인 로그인 방식은 사용자의 로그인 정보를 백엔드의 메모리 세션에 저장합니다. 사용자가 증가하게 되면 메모리 세션에 저장할 데이터의 양의 증가하게 되고 메모리 세션의 메모리 부족 문제가 발생하게 됩니다. 메모리 부족을 해결하는 방법으로는 스케일 업(scale-up)과 스케일 아웃(scale-out)이 있습니다.

 

스케일 업(scale-up)은 백엔드 서버의 물리적인 성능 향상(메모리 업그레이드 등)을 통해 메모리 부족 문제를 해결하는 방법입니다. 상대적으로 쉽고 낮은 관리 비용으로 문제를 해결할 수 있지만 성능 향상에 한계가 있고 업드레이드가 진행되는 동안 서버가 중단되는 문제점이 있습니다. 또한 각 서버가 처리하는 양이 증가하기 때문에 서버에 문제가 발생했을 경우 보다 큰 타격을 입을 수 있습니다.

 

스케일 아웃(scale-out)은 동일한 백엔드 서버의 양을 늘려 메모리 부족 문제를 해결하는 방법입니다. 필요에 따라 점층적으로 확장할 수 있고 확장 비용이 상대적으로 낮다는 장점이 있습니다. 반면에 관리 비용이 높고 데이터의 정합성을 유지하기 어려워 관련 이슈가 발생할 수 있는 문제가 있습니다.

 

 

세션 테이블과 병목 현상

스케일 아웃의 데이터 정합성 문제를 해결하기 위해 등장한 것이 세션 테이블입니다. 기존의 메모리 세션의 경우 서버에서 로그인 여부와 같은 상태(stateful)를 저장하기 때문에 요청하는 서버에 따라 로그인 여부가 다르게 반환되는 문제가 발생하게 됩니다. 이를 해결하기 위해 해당 데이터를 DB의 세션 테이블에 저장을 하고 백엔드 서버의 메모리 세션을 stateless 상태로 만들게 됩니다.

 

세션 테이블을 통해 스케일 아웃의 데이터 정합성 문제를 해결하였지만 DB의 병목 현상(bottle-neck)이라는 새로운 문제가 발생하게 됩니다. 여러 개의 서버가 하나의 DB에 접근을 요청하면서 속도가 느려지는 문제가 생긴 것입니다. 백엔드 서버와 마찬가지로 DB의 개수를 늘릴수도 있지만 데이터베이스의 경우 방대한 양의 데이터를 저장하기 때문에 비효율적이며 동기화 이슈(각 DB가 일치하지 않는 문제)가 발생할 수 있습니다.

 

따라서 보다 좋은 대안은 샤딩(Sharding)을 통한 데이터 분산입니다. 샤딩이란 데이터를 분산하여 저장하는 방식을 말합니다. 파티셔닝 방법으로는 데이터를 열을 기준으로 분산시키는 수직(vertical) 파티셔닝과 행을 기준으로 분산시키는 수평(horizontal) 파티셔닝이 있습니다.

 

 

 

최근 로그인 처리 방식 트랜드

Redis를 활용한 로그인 처리

Redis(Remote Dictionary Server)는 메모리에 키값과 데이터를 저장할 수 있는 인 메모리 데이터 구조입니다. 인가 과정에서 필요한 데이터를 DB의 세션 테이블에 저장할 경우 disk i/o로 인해 속도 저하가 발생할 수 있는데 속도가 빠른 메모리에 저장하는 Redis를 활용하는 방법입니다.

 

JWT(Json Web Token)을 활용한 로그인 처리

JWT는 최근 가장 많이 사용되는 로그인 구현 방식입니다. 'DB에 세션 정보를 저장하지 말자'라는 아이디어에서 시작되었습니다. 로그인 정보가 담긴 객체를 암호화(encoding)하여 세션 ID처럼 사용합니다. 백엔드에서 암호화된 accessToken을 브라우저에게 전달하면 브라우저는 세션 ID와 마찬가지로 토큰을 브라우저의 저장소에 저장합니다. 백엔드로부터 인가가 필요한 API를 요청할 때 저장된 토큰을 함께 전달합니다. 백엔드에서는 브라우저가 보낸 토큰을 복호화(encoding)하여 로그인 정보가 일치하는지 여부를 확인합니다.

 

하지만 중복 로그인, 토큰 해킹 등의 문제를 해결하기 위해 결국 JWT 방식도 Redis를 필요로 하게 됩니다.