작은 이커머스 서비스를 만든다고 가정하고 시작했다. 기능은 단순했다. 로그인, 상품 조회, 상품 주문, 회원 정보 저장, 주문 정보 저장. 이 정도면 그냥 서버 한 대에 다 넣어도 되지 않을까 싶었다.
가장 쉬운 방법부터 시도해봤다. 서버 한 대에 웹 서버, 애플리케이션, DB를 전부 설치하는 것. 개발 초기에는 이게 제일 편하다. 배포도 한번만 하면 되고 서버 관리도 하나만 신경 쓰면 된다.

그런데 이 구성으로 사용자가 늘어난다고 가정해보면, 문제는 거기서부터 시작된다. 서버가 눈에 띄게 느려지는데, 정작 원인을 못 찾겠는 상황이 되어버린다. 웹 요청 처리가 느린 건지, 주문 로직이 느린 건지, DB 조회가 느린 건지 구분할 방법이 없다. CPU와 메모리 지표를 봐도 세 가지가 뒤섞여서 하나로 튀는 것처럼 보였다. 더 답답했던 건, 뭐 하나 고치겠다고 재시작하면 나머지 두 개까지 같이 멈춰버린다는 점이었다.
→ 여기서 그럼 그냥 더 좋은 서버로 바꾸면 되지 않나 하는 생각이 들었다. 그런데 조금만 더 파보니 이건 임시방편일 뿐이었다. 진짜 필요한 건 역할별로 서버를 나누는 것이었다. 웹 처리, 비즈니스 로직 처리, 데이터 저장은 애초에 리소스를 쓰는 패턴이 다르다. 웹은 I/O 위주고, 애플리케이션은 CPU 연산 위주고, DB는 디스크와 메모리 위주다. 이걸 한 서버에 몰아넣으니 어디가 병목인지 구분이 안됐던 거고 장애가 나면 셋 다 같이 죽는 구조였던 거다. 이게 흔히 말하는 3-Tier Architecture(Presentation - Application - Data)의 시작점이라는 걸 여기서 이해했다.
서버를 세 대로 나눴다. Web, Application, DB. 이제 원인 분리는 됐다. 그런데 사용자가 100명에서 10,000명으로 늘어나니 이번엔 Application 서버만 CPU 100%를 찍었다. 처음엔 단순하게 생각해서 더 큰 서버로 교체했다. 잠깐은 버텼는데 결국 또 한계가 왔고, 무엇보다 교체할 때마다 서비스를 내려야 한다는 게 계속 걸렸다.
여기서 Scale-up(수직 확장)과 Scale-out(수평 확장)의 차이를 찾아봤다. Scale-up은 서버 한 대의 사양을 올리는 거라 간단하지만 물리적 한계가 있고 교체할 때 다운타임이 생긴다. 반면 Scale-out은 서버를 여러 대로 늘리는 방식이라 이론상 한계가 거의 없고 한 대가 죽어도 나머지가 버텨준다. 다만 여러 대를 하나처럼 다루기 위한 장치가 추가로 필요하다는 게 숙제로 남았다. (= 로드밸런서)
Application 서버를 두 대로 늘렸다. 그런데 사용자는 여전히 서버 한 대의 주소로만 접속하고 있었다. 두 번째 서버는 사실상 놀고 있는 셈이었다. 당연한 결과였는데 서버를 늘리는 것과 트래픽을 나누는 건 완전히 다른 문제라는 것을 알게되었다.

→ 여기서 필요한 게 로드밸런서였다.
앞단에 분산 장치를 두고 나니 다음 문제가 등장했다. 로그인한 사용자가 페이지를 넘길 때마다 로그아웃되는 현상이었다. 원인을 찾아보니 단순했다. 요청이 App 1로 갔다가 다음 요청이 App 2로 가면, App 2 입장에서는 이 사용자가 로그인했다는 사실 자체를 모른다. 세션 정보가 각 서버 메모리 안에만 있었기 때문이다.
→ 이건 세션을 서버 밖으로 빼야 한다는 걸 알려주는 문제였다. 서버가 이 사용자를 기억하는 상태를 자기 안에 들고 있으면 서버를 늘릴수록 이런 문제가 계속 생긴다. 그래서 Redis 같은 외부 세션 스토어에 세션을 저장하고 어떤 App 서버가 요청을 받든 그 외부 저장소를 조회해서 로그인 상태를 확인하는 구조로 가야 한다는 걸 정리했다. 이렇게 서버 자체가 상태를 갖지 않게 만드는 걸 Stateless 설계라고 부른다는 것을 알게되었다.