Skip to content

Showpot 기술 선택 이유

JunsuPark edited this page Jan 12, 2025 · 4 revisions

내부 서비스

1. 주 데이터베이스

결정 : Postgresql

비교군 : MySQL VS Postgresql

공통점

  • 구조화된 쿼리 언어(SQL)를 인터페이스로 사용하여 데이터를 읽고 편집 가능
  • 오픈 소스이며 강력한 개발자 커뮤니티 지원 제공
  • 두 제품 모두 데이터 백업, 복제 및 액세스 제어 기능이 내장되어 있음

Postgresql의 차별점

  • 인덱스
    • MySQL은 계층적으로 인덱싱된 데이터를 저장하는 B-트리 및 R-트리 인덱싱을 지원 PostgreSQL 인덱스 유형에는 트리, 표현식 인덱스, 부분 인덱스 및 해시 인덱스가 포함, 크기를 확장할 때 데이터베이스 성능 요구 사항을 세밀하게 조정할 수 있는 더 많은 옵션이 있음
  • 데이터 유형
    • MySQL은 순수 관계형 데이터베이스, 반면 PostgreSQL은 객체 관계형 데이터베이스. 즉, PostgreSQL에서는 데이터를 속성을 가진 객체로 저장할 수 있음. 객체는 상위-하위 관계 및 상속과 같은 패러다임을 지원

선정 이유 : 선택 당시 솔직히 둘 중 어느 데이터베이스를 사용해도 크게 상관이 없을 것이라고 예상함. 그러나 앞으로 데이터베이스를 변경하지는 않을 것을 고정으로, 요구 사항에 맞게 성능을 점진적으로 향상할 가능성을 높게 여겨 MySQL보다 상대적으로 옵션과 기능(인덱스 & 데이터 유형 등)이 다양한 Postgresql을 선택하기로 결정함.

2. Spring 서버 분리

결정 : Core Server와 Alarm Server로 분리한다.

서비스 요구사항

Showpot 애플리케이션은 사용자가 관심있는 아티스트, 장르를 구독하면, 어드민이 관련된 공연 정보가 등록 시 알림을 받음. 또한, 공연을 티켓팅하기 위한 알림 시간을 선택할 수 있음. 이후 서비스 자체적으로 공지 사항 등 알림을 보내는 요구사항이 생길 수 있음.

선정 이유 : 단일 서버의 한계

사용자가 받을 알림 내역이 상당히 존재함으로, 사용자 트래픽이 증가함에 따라 단일 서버에서 알림 이외의 인증/인가, 어드민, Showpot 도메인 CRUD 등까지 수행하기에는 과부하가 올 것으로 판단. 따라서 인증/인가, 어드민, Showpot 핵심 도메인(아티스트, 장르, 공연, 유저) CRUD 기능은 Core 서버에서 수행하고 알림에 대한 기능은 알림 서버에서 수행하여 부하를 분산하도록 함.

3. 내부 아키텍처

결정 : Layered 아키텍처와 멀티모듈을 이용한 의존성 관리

고려사항

showpot 요구사항으로는 인증/인가, 어드민의 데이터 관리, Showpot 도메인(아티스트, 장르, 공연, 유저 등등) CRUD, 알림 등 기능이 많음. 추후 기능 추가 시 단일 서버에서 패키지로만 나누는 것은 복잡성을 높일 것으로 예상. (하나의 gradle에서 모든 의존성을 관리하므로)

선정 이유 : 멀티 모듈을 도입하여 프로젝트의 복잡성을 줄이고 효율적인 협업을 진행

멀티모듈 도입 목표

  1. 코드의 재사용성을 높이고, 각 모듈의 독립적인 개발 및 테스트를 수행
  2. 팀원이 독립적으로 모듈을 개발하고 관리할 수 있으므로, 전체 프로젝트의 개발 속도와 품질을 향상
  3. 각 모듈을 독립적으로 관리함으로써, 특정 모듈의 변경이나 업데이트가 전체 프로젝트에 미치는 영향을 최소화
  4. 각 모듈의 역할에 따른 의존성을 관리하므로 변경에도 용이할 것
  5. 즉 프로젝트의 복잡성을 줄이게 되고 효율적인 협업이 가능할 것이다.

적용 구조 - ShowPot Server의 멀티 모듈 적용기 참고

  • yapp_backend 모듈

    • 프로젝트를 잡는 가장 큰 틀
  • app 모듈

    • 애플리케이션을 실행하는 가장 큰 단위
  • common 모듈

    • 순수 JAVA로 존재하는 모듈
  • api 모듈

    • 클라이언트로부터 요청을 받아서 응답해주는 모듈

    • service 계층은 표현 영역과 도메인 영역을 연결해주는 창구 역할

  • domain 모듈

    • showpot 서비스의 도메인에 관한 모듈

    • usecase 계층은 domain 중심이 되는 로직이 담김

    • ORM, 데이터베이스 의존성도 포함(향후에도 변경하지 않을 것을 가정하였기 때문)

  • infrastructure 모듈

    • 외부 의존성들을 가지는 모듈

4. 서버간 비동기 요청 처리

결정: Redis Pub/Sub을 적용

고려사항

Core 서버와 Alarm 서버가 분리된 후 서버간 통신 수단이 필요함

비교군 : Http Request VS Message Queue

Http Request : 동기 방식으로 응답을 받을 때까지 다른 작업을 못함으로 비효율적, Core 서버와 Alarm 서버를 분리한 이점을 좀 더 살리지 못함 Message Queue : 외부 message queue를 이용하면, 비동기 요청 방식으로 Core 서버와 Alarm 서버를 유연하게 결합하게 됨

  • Message queue 스택(Rabbitmq, Kafka 등)은 다양하지만, 그 중 Redis Pub/Sub을 사용.

선정 이유 : 초기 유저 수 100명을 기준으로 비용 최적화

기존에 이미 Redis를 인증 토큰 저장소로 도입, In memory cache로 사용 가능성도 있었음. AWS에서는 프리티어로 ElasticCache로 Redis를 사용할 수 있어 추가 비용이 발생하지 않음. Message queue를 도입하기 위한 다른 기술을 선정하면 클라우드 비용이 추가로 발생. 그러나, Redis Pub/Sub의 문제는 메시지 유실이 발생할 수 있으며, 이를 대응할 수 있는 수단이 없다는 것. 초기 유저 수 100명을 목표로 메시지 유실이 발생할 지는 확인되지 않았으며, 따라서 유실이 발생하게 됨을 확인하게 되면 대응할 수 있는 RabbitMQ로 마이그레이션을 하기로 결정. Redis Pub/Sub 유실은 Prometheus/Grafana를 통한 모니터링으로 확인.

5. 알림을 보내기 위한 스케줄러

결정 : Spring Quartz를 이용

요구사항

사용자는 공연 티켓팅 알림을 5분전, 10분전, 30분전, 1시간 전 으로 예약을 할 수 있으며, 해당 시간에 알림을 받을 수 있음.

비교군 : @Scheduled VS Spring Quartz Job Scheduler

  • @Scheduled

    • 알림 시간을 데이터베이스에 저장하고 @Scheduled로 주기적으로 확인을 하여 알림 요청을 보내도록 할 수 있음.
  • Spring Quartz

    • Job과 Trigger을 등록하여 애플리케이션의 메모리 상에서 알림 시간을 기억하여, 해당 시간마다 job을 실행 할 수 있음

선정 이유 : 데이터베이스 DISK I/O 감소

@Schedule 방식은 주기적으로 데이터베이스에 조회 쿼리를 발생시킴. 현재 데이터베이스는 RDS를 사용하며, core와 alarm 서버의 스키마만 나누었지 단일 데이터베이스임. 따라서 주기적으로 조회 쿼리를 요청하면 성능 문제가 발생할 수 있음. 따라서 Spring Quartz를 사용하여 애플리케에션 메모리 상에서 알림 작업을 기억하도록 함. 혹여나, 애플리케이션이 중단 될 경우, 메모리 상에 저장된 알림 작업을 잃어버리므로, 애플리케이션 실행 시 데이터베이스에 저장된 기록을 통해 복구하도록 함. 애플리케이션의 메모리 사용량이 너무 높으면 문제가 발생할 수 있으므로, 현재는 모니터링으로 확인하며 진행할 예정.

6. 알림 전송 방식

결정 : FCM을 이용한 알림 전송

비교군 : Polling, WebSocket, SSE, FCM

Polling, WebSocket, SSE : 실시간성을 고려했을 때 적용할만함. showpot 서비스의 알림은 공연 티켓팅 알림, 구독한 아티스트, 장르 관련 공연이 게시되었을 때 알림을 받음. 1년에 내한 공연은 그렇게 많지 않음. Polling처럼 주기적으로 확인하기에도 오버헤드가 너무 큼. WebSocket으로 클라이언트와 서버가 지속적으로 연결을 이어갈 필요는 없음. SSE 방식도 클라이언트(앱)가 실행 중이어야만 연결이 유지되므로, 앱이 종료되거나 백그라운드 상태로 전환되면 작동하지 않음. 실시간 스트림 방식이라 네트워크가 끊기면 데이터가 유실될 가능성이 큼.

선정 이유 : Polling, WebSocket, SSE의 경우 알림을 받기 위해 클라이언트와 서버간의 지속적인 연결을 유지하기 위한 오버헤드가 발생함. FCM을 도입하여, 알림 메시지를 클라이언트로 전송해주는 외부 기술을 도입하여, showpot 요구사항에 적합한 알림 전송 기술로서 선택.

7. 아티스트 검색 및 생성 방식

결정 : Spotify API 연동

문제사항

기존에는 어드민이 아티스트를 직접 어드민 페이지에서 생성을 하여, 사용자는 데이터베이스에 저장된 아티스트를 검색할 수 있었음. 그러나 내한 공연 서비스에서 어드민이 잘 모르는 아티스트가 발생할 수 있으며, 내한하는 모든 아티스트를 주기적으로 어드민이 등록하기에는 비용이 많이 들었음.

선정이유 : Spotify API를 연동하여 유저가 아티스트를 검색하여 어드민으로부터 한정된 아티스트 생성 한계를 깰 수 있었음.

사용자는 아티스트를 검색하여 아티스트를 구독할 수 있음. 이때, 데이터베이스에 저장되어 있지 않은 아티스트라면, 구독과 동시에 아티스트를 데이터베이스에 생성하도록 함.


클라우드 서비스

8. AWS 클라우드 인스턴스

결정 : AWS EC2 t2.micro 프리티어를 선택

비교군 : AWS ECS, AWS EC2

  • AWS ECS : AWS ECS-fargate를 이용한 롤링 업데이트 무중단 배포 방식은 운영 환경을 쉽게 관리할 수 있음
  • AWS EC2 : 프리티어를 사용해 많은 클라우드 비용을 줄일 수 있음

선정 이유 : 사이드 프로젝트 처음에 가장 최소화하기로 고려한 것은 비용임. 초기 유저수도 100명으로 잡음.

무중단 배포가 운영 서버의 다운 타임을 최소화하여, 사용자의 경험이나 오류 등을 최소화할 수 있으나, 출시를 하기 전인 상태에서 필요성을 크게 느끼진 못함. EC2 프리티어로 운영 서버를 배포하므로 매월 약 2만원 정도로 지출이 됨. Locust로 초기 목표 가상 유저 100을 대상으로 Load 테스트 한 결과, 서버가 터지지 않고 원활히 실행됨을 확인.

9. AWS 클라우드 데이터베이스

결정 : AWS RDS를 선택

비교군 : AWS RDS VS AWS Aurora

  • AWS Aurora

    • 컴퓨팅 리소스에서 분리되고 데이터베이스 인스턴스당 최대 128TiB까지 자동 스케일 업되는 내결함성을 갖춘 자가 복구 분산 스토리지 시스템을 제공
    • 지연 시간이 짧은 읽기 전용 복제본 최대 15개, 특정 시점으로의 복구, Amazon Simple Storage Service(S3)로의 지속적 백업
    • 3개 가용 영역(AZ)에 걸친 복제를 통해 뛰어난 성능과 가용성을 제공
  • AWS RDS

    • 비용 효율적이고 크기 조정 가능한 하드웨어 용량을 갖춘 확장 가능한 MySQL 및 PostgreSQL 배포를 몇 분 만에 배포 가능
    • 기존 데이터베이스와 관련된 코드, 애플리케이션 및 도구를 재사용
    • 컴퓨팅, 메모리 및 스토리지 용량 사용률과 같은 중요한 운영 지표 보기

선정 이유 : 사이드 프로젝트에서 가장 중요하게 관리해야 하는 것 중 하나는 비용임. RDS는 프리티어로 무료로 사용할 수 있지만 Aurora는 지원을 하지 않음. Aurora는 고성능, 가용성 및 신뢰성을 보장하는 완전 관리형 서비스이며 Postgresql에서는 3배 높은 초당 SELECT 수와 초당 UPDATE 수를 제공함. 아무리 성능이 좋고, 완전 관리형 서비스여도, 데이터양이 어느정도 될지 모르는 상황에서 비용이 발생하는 클라우드 데이터베이스는 오버 엔지니어링으로 판단. 따라서 AWS RDS로 선택하고 성능적인 면에서는 데이터베이스 자체에서 쿼리 성능을 개선하는 것 부터 시작.

10. AWS 클라우드 Load Balancer

결정 : ALB + ACM을 이용한 HTTPS 적용 및 라우팅

비교군 : Nginx VS AWS ALB

  • Nginx : 서버에서 Https 복호화를 해야하므로 서버에 부하가 생김.
  • ALB + ACM : 복호화 역할을 대신하므로 서버의 부하를 줄일 수 있음.

현재 테스트용 서버와 운영 서버로 나눠진 상태에서 인스턴스도 각각 나눠져 있음.

선정 이유 : ALB + ACM으로 HTTPS 요청에 대한 서버의 복호화 작업을 없앨 수 있음. 로드 밸런싱의 주요 목적은 Scale Out이지만, 테스트용 서버와 운영 서버를 나눈 시점에서 비용을 최소화 하기 위해, 하나의 ALB를 이용하여 적절한 인스턴스로 라우팅을 하도록 함.