AWS

각 서비스에 맞는 아키텍처는 무엇일까? - 옷 쇼핑몰 웹앱의 아키텍처를 개선하면서 설계해보자

S_N_Y 2024. 3. 31. 10:47

세 가지의 웹앱 서비스를 예시로 아키텍처 설계 시리즈를 적으려고 한다.

설계에 관심이 있으면 서비스 종류가 무엇이 있는지 먼저 조금 파악을 한 뒤에 이 글을 읽으면 좋을 것이다!

 

#0 들어가기 앞서

여러 가지 서비스들을 정리해봤는데 이것들이 실제로 어떻게 연결되고 관리되는지도 조금 다뤄보는게 좋을 것 같아 올려본다..! 아키텍처에 관한 부분은 꼭 정답이라고 할 수는 없지만 여러 가지 사례를 통해 어떤 서비스들이 어떤 식으로 동작하는지에 대해 정리해본다면 많은 분들께도 도움이 될 것 같아 정리해보려고 한다.

+) ⚠️ 가장 기초적인 아키텍처 설계 예시이기 때문에 확장성을 고려해서 초반부터 서버리스 세팅을 할 수 있지만 일단 기초 자료로써 이 글을 다루려고 하니 실제 아키텍처 설계와 똑같이는 할 수 없다는 것을 먼저 언급하고 시작하겠다!

 

#1 사례 2️⃣) 옷 쇼핑몰 웹앱

<조건>

- 사람들이 온라인으로 옷을 쇼핑할 수 있고 장바구니로 옷을 담아 살 수 있는 웹앱

- 동시에 수백 명의 사용자가 있고 이 모든 사용자들이 웹사이트를 구경한다.

- 확장성과 수평 확작성을 고려하고 싶고 웹을 최대한 stateless하게 유지하고 싶다.

(사용자가 웹 사이트를 둘러볼 때 넣어놨던 장바구니를 잃어버리지 않도록..! - 장바구니가 유지되면서 확장될 수 있게)

- 주소나 사용자 정보를 효율적으로 보관하고 어디에서나 접근할 수 있는 데이터베이스에 저장할 필요가 있어보인다.

 

✳️ 초기

1) ✴️ 극초기 - 이전 글에서 다룬 아키텍처 그대로 써보기

이전 사례에서 다뤘던 아키텍처와 같은 종류의 아키텍처가 기본적으로 설계되어있다고 치자.

사용자가 있고 Route53이랑 다중AZ ELB가 있고 오토 스케일링 그룹과 세 개의 AZ가 기본적으로 있다. 

애플리케이션이 ELB에 접근하고 ELB는 "음 1번 인스턴스와 대화하세요~"라고 하면

장바구니를 생성하는데 다음 요청은 같은 인스턴스가 아니라 다른 인스턴스(2나 3)로 간다.

그러니까 장바구니가 사라져버리는데 사용자는 장바구니에 뭔가를 넣으면 다른 인스턴스로 리디렉션이 되어서 계속 사라지는 요상한 애플리케이션이 완성이 되는 구조인 것이다.😨🌩️

...

계속 인스턴스를 바꿔버려서 장바구니가 자꾸 초기화되는 문제는 어떻게 고칠까?🥶

고착도, 즉 세션이 밀접성있게 바꿀 수 있는데

이는 ELB의 기능 중 하나인 🌟ELB Stickiness를 활성화🌟시킨다!

 

2) ✴️초기 1 - 기존 아키텍처에 ELB Stickiness 적용

이제는 사용자가 첫 번째 인스턴스에 접속해서 뭔가를 장바구니에 추가한다.

그리고 이 기능의 특징인 고착도 덕분에 두 번째 요청도 동일한 인스턴스로 가게 된다..!

(사실 모든 요청이 이 기능 덕분에 동일한 인스턴스로 가게 할 것)

근데 잘 작동하긴 하지만 만약 EC2 인스턴스가 어떤 이유로든 종료가 되면 장바구니를 잃어버리게 된다.

=> 장바구니를 곧바로 잃어버리지 않는 그 부분에 대해선 개선되지만 그렇게 좋은 아키텍처는 되지 못 한다.

 

3) ✴️ 초기 2 - 쿠키 사용하기

그러면 어떻게 더 개선할까?🤷‍♂️

사용자 쿠키라는 방식이 있는데 장바구니 내용을 저장하는 대신에

사용자쪽에서 장바구니 내용을 저장하도록 하는 것이다.😀

쿠키에 저장한다는건 로드 밸런서에 접속할 때마다

"내 장바구니에는 이런 것들이 있다~"라고 말하게 하는 것이라고 생각하면 편하다.

첫 번째 서버랑 다른 서버에 접속해도 사용자가 직접 EC2 인스턴스로 장바구니 내역을 보내주기 때문에 각각의 서버가 장바구니의 내용을 알 수 있다..!

=> 이제 각각의 EC2가 이전에 있었던 일을 알 필요가 없는 stateless(무상태)를 달성했다.

...

그런데 HTTP요청이 점점 무거워진다🤦‍♀️☄️

=> 왜냐하면 웹 쿠키를 통해 장바구니 내용을 보낼 때 장바구니에 뭔가를 추가할수록 점점 더 많은 데이터가 담겨서 보내지기 때문이다..

그리고 이 뿐만이 아니라 쿠키 형태로 보내지는 것 자체가 어떤 해커에 의해 쉽게 변경될 수 있어 사용자의 장바구니가 갑자기 수정될 수 있기 때문에 어느 정도의 보안 위험 또한 존재한다.

...

그래서 이런 종류의 아키텍처에서는 EC2 인스턴스가 반드시 사용자 쿠키의 내용을 검증해야한다! 

그리고 전체의 쿠키의 크기는 4KB까지만 가능하니까 매우 작은 정보밖에 저장 안 되는데 다른 방법은 없을까?

...

=> 사용자의 세션 ID만 보내보자 ⬇️⬇️

 

✳️ 중반

4) ✴️ 중반 - 초기2보다 더 괜찮게 세션 ID(Server Session)로 보내보기 (개선사항 적용)

 

백그라운드에 ElasticCache 클러스터가 존재하게 한다!

세션 ID를 보낼 때에 EC2 인스턴스에게 "이 물건을 장바구니에 추가할거야~"라고 말하면 EC2 인스터스는 장바구니 내용을 ElasticCache에 추가하고 이 장바구니 내용을 불러올 수 있는 ID가 바로 세션ID가 된다..!

그래서 사용자가 세션 ID와 함께 두 번째 요청을 보내면 다른 EC2 인스턴스로 가게 되고 그 EC2 인스턴스는 세션 ID를 사용하여 ElasticCache로부터 장바구니 내용을 찾아서 세션 데이터를 불러올 수 있는 것이다.

 

그리고 ElasticCache의 또 다른 장점!

1천분의 1초 이하의 성능을 가지고 있다는 점 => 이 모든 과정이 엄청 빠르게 진행된다.

+) 아직 여기에선 다루지 않을 것이지만 DynamoDB라는 또 다른 방식의 세션 데이터를 저장할 수 있는 서비스가 있는데 알아두고 있으면 좋다🙂

 

아무튼 이렇게 하면 ElasticCache가 정보의 출저가 되면서 해커들도 ElasticCache 내부를 수정할 수 없기 때문에 훨씬 안전해졌고 실제로 많이 사용된다.

 

✳️ 후반 (추가 요구사항 적용까지)

➕) 여기서 추가 요구사항 : 데이터베이스에 오래 저장하고플 때 (RDS 사용해보기)

- 사용자의 데이터를 데이터베이스에 저장하려고 한다 (사용자 주소 등을 저장하고 싶을 때)

=> 따라서 다시 한 번 EC2 인스턴스와 통신을 할텐데 이번에는 RDS 인스턴스와 통신할 것이다.

(RDS는 장기적인 저장을 위한 것이라 좋다)

RDS와 직접 통신함으로써 주소, 이름 등의 사용자 데이터를 저장하거나 불러올 수 있다. 그리고 각각의 인스턴스가 RDS와 통신할 수 있고 일종의 다중AZ stateless 솔루션을 효과적으로 얻을 수 있다!

...

생각해보니 쇼핑몰을 사용하는 사용자들은 상품을 사는 시간보다

대부분 웹 사이트를 둘러보는데 시간을 쏟는다..!📰

그렇다면 '읽기'를 어떻게 확장할까?

...

➕) 추추가 개선사항 :  쓰기를 수행하는 RDS 마스터 사용하기

쓰기를 수행하는 RDS 마스터를 사용할 수 있다.

또한, 복제가 일어나는 RDS 읽기 전용 복제본(RDS Read Replicas)을 사용할 수도 있다.

즉 뭔가를 읽을 때 읽기 전용 복제본으로부터 읽어오는데 RDS에서는 5개의 읽기 전용 복제본으로부터 읽는다.

=> 이 말은 즉슨 RDS 데이터베이스의 '읽기'를 확장할 수 있게 만드는 것이다. 

...

혹은 또 다른 패턴으로 '캐시를 사용'하는 '쓰기 모드'가 있다!

 

) 추추추가 개선사항 :  캐시 사용해서 쓰기 모드

1사용자가 EC2 인스턴스와 통신하는 방식으로 작동한다. 

2캐시를 살펴보고 "이런 정보를 가지고 있니?"라고 물어볼텐데

3가지고 있지 않으면 RDS로부터 읽어 들여서 ElasticCache에 집어넣는다

=> 4즉 이 정보가 캐싱이 되는 것!

 

다른 EC2도 같은 방식으로 작동하는데

5단, 이번에는 ElasticCache와 통신할 때 정보를 얻게 되고 캐시 히트가 된다.

=> 캐싱이 됐기 때문에 즉시 응답을 받는다

=> 이 패턴을 통해 RDS상의 트래픽을 줄여서 CPU사용을 줄이고 동시에 성능을 향상시킬 수 있는 것이다!

그런데 이제 캐시를 유지보수해야하는데 꽤 어려운 일이고 애플리케이션 쪽에서 이루어져야 한다는 점이 있다.

-----------------------------------------------------------------------

 

만약 재해로 인해 피해를 받지 않으려면 어떻게 할까?🌋

) 추추추추가 개선사항 : 재해복구를 위한 다중AZ, 대기복제본 만들기

사용자가 Route53과 통신을 하는데 이제 우리는 다중AZ ELB가 있는 상태라고 가정하자.

여기서 Route53으로 인해 이미 가용성이 높다.

그래서 뭔가 더 할 필요는 없고 1️⃣로드 밸런서는 다중 AZ로 만들어볼 것이다.

 (오토 스케일링 그룹도 다중 AZ, RDS 역시 다중 AZ)

또 다른 방법으로는 2️⃣재해가 발생할 경우 인계받을 수 있는 대기 복제본이 있다. 레디스를 사용한다면 ElasticCache도 다중 AZ를 가지고 있다.

=> 이렇게 해서 전반적으로 다중 AZ를 가진 어플리케이션이 되어서 AWS 가용 영역이 다운되는 것에 대비할 수 있게 되었다!

...

 

그리고 Security Group(보안 그룹)에 대해서는 매우 안전해야 한다.

) 추추추추추가 개선사항 : 서로 참조하는 보안 그룹을 위해 철저한 보안 추가하기

ELB쪽 어디에서나 HTTP, HTTPS 트래픽을 열 수 있다.

EC2 인스턴스 측면에서는 로드 밸런서로부터 오는 트래픽만 제한하고

ElasticCache 측면에서는 EC2 Security Group으로부터 오는 트래픽만 제안하면 강해진다. (RDS도 마찬가지)