백엔드 개발 포트폴리오 | 출사 관련 커뮤니티

내일배움캠프 4기 웹 개발 과정 Node.js 트랙 수료생 최종 프로젝트 '찰칵'을 소개합니다.
Aug 16, 2023
백엔드 개발 포트폴리오 | 출사 관련 커뮤니티
📸 사진 공유 플랫폼, 찰칵!
여기저기 흩어져 있는 출사 관련 정보 및 커뮤니티를 한 곳에서! `찰칵(Chalkak)`은 출사 관련 정보를 공유하는 플랫폼이자 커뮤니티 입니다. 나만이 알고 있는 매력적인 포토스팟을 테마별 콜렉션으로 공유하여 누구나 쉽게 사진 명소를 찾을 수 있도록 돕습니다. 찍고, 찍어주고, 대화하고, 함께해요. 출사 모임을 직접 만들거나 참여하여 우리의 사진과 삶을 함께 나눕니다.
 
 

🛠️서비스 아키텍처

 
notion image
 

💪주요기능

포토스팟 콜렉션

  • 카카오 맵을 이용해 원하는 좌표 내 포토스팟 저장 기능
  • 포토스팟 저장 시 최대 5장의 사진 저장 기능
    • notion image
      notion image
 
  • 콜렉션에 있는 포토스팟 리스트를 확인하고 해당 포토스팟을 클릭하면 해당 좌표로 이동
    • notion image
       
  • 마음에 드는 콜렉션에 나의 반응을 표현할 수 있는 좋아요 기능
    • notion image
       
  • 포토스팟에 등록된 사진을 모아보고 비슷한 사진을 추천하는 기능
    • notion image

같이 찍어요 & 채팅

  • 같이 사진을 찍으러 가는 인원을 모을 수 있는 모임 기능
    • notion image
       
  • 모임이 모집 마감되면 모임에 참여한 인원끼리 실시간 채팅 기능
    • notion image
      notion image
       

로그인

  • 회원가입을 통한 이메일 로그인 및 네이버와 카카오 계정을 이용한 소셜 로그인 기능
    • notion image
 

🔧기술적 의사결정

📌 [동시성 처리] Bull Queue

도입배경
  • 모임 참여 기능에서 동시 요청으로 인해 정해진 신청 인원을 초과하는 상황을 고려하여 동시성 제어가 필요했다.
 
논의과정
Bull Queue
RabbitMQ
Kafka
간단 설명
Node.js용 메시지 큐
AMQP 프로토콜을 기반으로 하는 오픈 소스 메시지 브로커
분산 스트리밍 플랫폼
성능
Redis 기반 높은 성능 / 순서가 엄격히 보장 X
고성능
높은 처리량 / 낮은 지연 시간
사용성
가볍고 편리
많은 클라이언트 라이브러리
초기 설정 및 복잡한 운영
안정성
안정성 이슈 (Redis 및 처리량 관련)
안정적 (AMQP 프로토콜 사용)
높은 안정성
효율성
작은 규모의 메시지큐에 적합
작은 메시지 크기에 적합 / 처리량이 적은 경우 비효율
대용량 데이터 처리 특화
확장성
높은 확장성, 분산 처리 지원
높은 확장성, 분산 처리 지원
높은 확장성, 분산 처리 지원
보안성
안전한 데이터 전송 및 저장
안전한 데이터 전송 및 저장
안전한 데이터 전송 및 저장
사용 사례
대규모 분산 시스템, 실시간 데이터 처리, 백그라운드 작업 처리
대규모 분산 시스템, 메시지 큐, 이벤트 기반 애플리케이션
대규모 분산 시스템, 이벤트 스트리밍 처리, 로그 수집 및 분석
 
BullQueue
  • 장점
    • Node.js 환경에서 운영된다. Node.js 개발자들이 쉽게 사용 가능하며 높은 처리량과 낮은 지연 시간을 제공한다.
    • Redis 기반이다.
  • 단점
    • 다른 메시지 큐와 비교했을 때 기능이 제한적이며, 대규모 분산 시스템에서 사용성이 제한적이다.
    •  
RabbitMQ
  • 장점
    • AMQP 프로토콜을 기반으로 하여 안정성과 다양한 기능을 제공하며 다양한 언어를 지원하고 대규모 분산 시스템에서도 안정적으로 동작한다.
  • 단점
    • Broker 자체의 성능이 중요한 경우에는 다른 시스템과 비교해 처리량이 낮을 수 있다.
    •  
Kafka
  • 장점
    • 높은 처리량과 낮은 지연 시간을 제공하며 분산 스트리밍 처리에 적합하다. 그리고 데이터를 병렬로 처리하기 때문에 처리량이 높다.
  • 단점
    • 주로 대용량 처리를 위해 사용된다. 상대적으로 복잡하고 운영 관리가 어려울 수 있다.
    •  
 
최종결정
  • Redis 기반의 빠른 처리 속도와 쉬운 사용성으로 작은 규모의 메시지 큐에 적합한 Bull Queue를 선택하여 동시성 제어를 위한 작업 대기열 방식으로 적용하기로 결정하였다.
 

📌 [이미지 스토리지] AWS S3

도입배경
  • 사진을 많이 저장하면 스토리지 공간을 확장해야 한다. 그렇기 때문에 스토리지 공간이 필요한 만큼 동적으로 할당하여 효율적으로 사진을 관리할 수 있는 클라우드 기반의 객체 스토리지 서비스가 필요했다.
 
기술비교
AWS S3
Google Cloud Storage
Microsoft Azure Blob Storage
비용
저렴한 가격 대비 높은 성능
저렴한 가격 대비 높은 성능
비교적 높은 가격
기능성
다양한 객체 타입 지원
안정적이고 고성능
관리 작업을 간소화하는 기능 제공
보안성
256-bit AES 암호화 및 HTTPS를 통한 데이터 전송 보안
256-bit AES 암호화 및 HTTPS를 통한 데이터 전송 보안
암호화, 로그 기록 및 액세스 제어 등 다양한 보안 기능 제공
데이터 복제
지리적으로 분산 된 데이터 복제 기능 제공
지리적으로 분산 된 데이터 복제 기능 제공
데이터 복제를 위해 LRS, GRS 및 RA-GRS 등 다양한 옵션 제공
사용 편의성
AWS Management Console, CLI, SDK 등 다양한 도구를 사용한 구성 및 관리 가능
RESTful API 및 CLI를 사용하여 쉽게 구성 및 관리 가능
Azure Portal, PowerShell, CLI 등 다양한 도구를 사용하여 구성 및 관리 가능
 
AWS S3
  • 장점
    • 저렴한 비용으로 매우 빠르고 안정적인 전송 속도와 데이터 신뢰성을 제공한다.
    • 다양한 객체 타입을 지원하여 많은 종류의 데이터를 저장할 수 있다.
    • 지리적으로 분산 된 데이터 복제 기능을 제공하여 데이터 손실을 방지한다.
    • AWS Management Console, CLI, SDK를 사용하여 구성 및 관리 가능하다.
  • 단점
    • 트래픽이 너무 많을 경우 비용이 높을 수 있다.
    •  
Google Cloud Storage
  • 장점
    • 매우 빠르고 안정적인 전송 속도와 데이터 신뢰성을 제공한다.
    • 저렴한 가격에 높은 성능을 제공한다.
    • 지리적으로 분산 된 데이터 복제 기능을 제공하여 데이터 손실을 방지한다.
    • RESTful API와 CLI를 사용하여 쉽게 구성 및 관리할 수 있다.
  • 단점
    • AWS S3와 비교해 객체 타입 지원 범위가 제한적이다.
    •  
Microsoft Azure Blob Storage
  • 장점
    • 관리 작업을 간소화하는 기능을 제공해서 관리 작업을 자동화할 수 있다.
    • 암호화, 로그 기록 및 액세스 제어 등 다양한 보안 기능을 제공한다.
    • Azure Portal, PowerShell, CLI를 사용하여 구성 및 관리가 가능하다.
  • 단점
    • 비교적으로 높은 가격으로 서비스를 제공한다.
 
 
최종결정
  • 저렴한 비용과 AWS에서 RDS, EC2 등 AWS 서비스를 같이 사용하기 때문에 비용 관리가 용이하고, 접근하기 쉬운 API와 SDK를 제공한다. 그리고 자세하고 이해하기 쉽게 정리가 잘 되어있는 문서를 제공하기 때문에 결정하였다.
 

📌 [DB 모델링] STI (Single Table Inheritance)

도입배경
  • 이메일 및 소셜 로그인 회원의 DB에 저장되는 엔터티의 일부 칼럼이 다르므로, 싱글 및 멀티 테이블 상속 전략이 필요하다.
 
기술비교
Single Table Inheritance
Multi Table Inheritance
테이블 개수
1
2개 이상
전체 조회
간편
복잡
userId FK
userId만 필요
userId 및 provider 테이블
추가 칼럼
하위 엔티티 구분 칼럼 필요
필요 없음(서로 구분 됨)
유효성 검사
복잡
간단
 
Single Table Inheritance
  • 장점
    • 하나의 테이블에 모든 모델을 관리하기 때문에 간결성을 높이고 유지보수를 간단하게 만들어 준다.
    • 조인이 필요 없으므로 조회 쿼리를 단순화 할 수 있기 때문에 조회 성능이 빠르다.
    • 새로운 모델을 추가하더라도 별도의 테이블을 생성할 필요가 없어 유연하다.
  • 단점
    • 자식 엔티티가 매핑한 칼럼은 모두 null을 허용해야 한다.
    • 단일 테이블에 모든 것을 저장하므로 테이블의 크기가 커질 수 있다.
    • 상황에 따라 조회 성능이 오히려 느려질 수 있다.
    •  
Multi Table Inheritance
  • 장점
    • 상속된 열이 필요하지 않아서 중복 데이터가 감소한다.
    • 새로운 모델을 추가하더라도 모델에 대한 테이블을 생성할 수 있다. 각 고유한 데이터를 저장하기 위한 공간을 제공하기 때문에 테이블 간의 관계를 간소화 할 수 있어 유연하다.
    • 테이블 간의 관계를 구성할 수 있어 데이터베이스의 정규화 수준을 높일 수 있다.
  • 단점
    • 복잡한 조인과 서브쿼리가 필요하다. 조회 쿼리가 복잡하기 때문에 조회 성능이 저하 될 수 있다.
    • 각 모델에 대한 별도의 테이블이 있어서 유지보수가 어려울 수 있다.
    • 테이블 간의 관계가 복잡해질 수 있다.
    • 데이터베이스 디자인이 보다 어려워진다.
 
최종결정
  • Multi Table Inheritance를 사용하면 조인이 복잡해지고 조회 성능이 느려진다. 유저는 여러 관계의 중심이 된다는 점에서 유저를 가리키는 외래키가 복합키가 되어 복잡성이 증가한다. 따라서 싱글 테이블 전략을 선택해서 조인을 단순화시켜 조회 성능을 올리고 연관성과 복잡성을 줄이는 것으로 결정했다.
 

📌 [CI/CD] GitHub Actions

도입배경
  • 직접 테스트 코드를 실행하고 수동으로 배포를 하는 번거로움이 있다. 이 과정을 자동화하여 개발 외에 소요되는 시간을 단축하고자 도입했다.
    •  
기술비교
GitHub Actions
Jenkins
Travis CI
설치 및 설정
GitHub 저장소 내 워크플로우 파일 작성으로 간단히 설정 가능
별도의 서버와 플러그인 설치 필요
GitHub 저장소와 연동하여 간단히 설정 가능
비용
무료 티어 범위 초과 시 유료
서버 유지 비용 발생
공개 저장소는 무료 비공개 저장소는 유료
트리거 설정
매우 유연
매우 유연
유연
지속적 통합
지원
지원
지원
지속적 배포
지원
지원
지원
테스트 병렬화
지원
지원
지원
사용 언어
YAML
Java
Ruby
 
GitHub Actions
  • 장점
    • 매우 유연한 트리거 설정을 제공한다.
    • 무료로 제공한다.
    • GitHub와 연동이 원활하다.
  • 단점
    • 특정 플러그인 및 확장이 부족하다.
    • 커스터마이징의 폭이 좁다.
    • 상대적으로 새로운 기술이기 때문에 자료가 부족하고 커뮤니티 작다.
    •  
Jenkins
  • 장점
    • 커스터마이징의 폭이 넓다.
    • 다양한 플러그인과 인터페이스를 지원한다.
    • 매우 넓은 커뮤니티를 가지고있어 자료 찾기가 용이하다.
  • 단점
    • 설정이 쉽지 않다.
    • 보안 및 안정성 이슈가 발생할 수 있다.
    • 많은 자원을 소비하고 느릴 수 있다.
    •  
Travis CI
  • 장점
    • 쉽게 설정할 수 있다.
    • 커스텀 빌드 환경을 지원한다.
  • 단점
    • 일반적인 커스터마이징이 가능하다.
    • 높은 비용이 발생할 수 있다.
    •  
 
최종결정
  • 무료로 이용할 수 있으면서 GitHub와의 연동이 매우 원활하고 접근성이 좋은 GitHub Action을 이용해 CI/CD를 도입할 수 있도록 결정했다.
 

🛠️트러블슈팅

유저 블락 전략

유저 권한의 검증 문제
🐐
발생 문제 Passport를 사용한 AuthGuard를 통과한 이후 DB를 조회해 유저의 블락 여부를 확인하는 과정에서 DB 조회 횟수가 늘어나면서 성능 저하와 리소스 낭비 발생
 
  • 해결과정
    • 매 API 요청마다 DB 조회를 하지 않기 위해 CacheManager로 DB에서 가져온 블락 여부에 대한 데이터를 5분 간 Cache (Redis)에 저장한다. 이를 통해 DB 조회 횟수를 줄여 리소스를 효율적으로 활용하고, 성능을 개선할 수 있었다.
       
  • 결과
    • 외부 DB와의 통신에 따른 시간 소요와 DB 및 Redis와의 속도 차이로 인한 개선
      • 1회당 블락 유저 조회 소요 평균 시간
        • 25.54ms ⇒ 1.7 ms (93.34% 단축)
        • notion image
       
    • 기존
      • DB에서 UserEntity의 isBlock 값을 가져와서 검증
        notion image
       
    • 변경
      • Cache (Redis)에 저장해 둔 isBlock 값을 가져와서 검증
        notion image
 
Redis 부하 문제
🐐
발생 문제 API를 요청한 모든 유저의 블락 여부를 Redis에 캐싱하다 보니 Redis의 부하가 심해짐
 
  • 해결과정
    • 블락된 유저의 정보를 Cache에 저장하고, 액세스 토큰의 만료 시간을 1시간으로 지정했다. 이를 통해 최근 1시간 동안 블락된 유저 숫자만큼만 Cache에 올라가게 되어, Cache 성능이 개선되었다. 또한, 액세스 토큰이 만료되면 로그아웃 처리를 하고, 로그인하지 못하도록 막아 보안성을 높였다.
  • 수치 비교
    • 기존
      • 유저수에 비례해 증가하던 기존 메모리 사용구조
        • notion image
         
    • 변경
      • 최대 100개 수준을 넘지 않는 메모리 사용구조
      •  
 

이미지 리사이징

고화질 이미지의 처리 문제
🐐
발생 문제 이미지의 크기가 커질수록 이미지 업로드 및 웹 페이지 로딩 속도가 느려짐
 
  • 해결과정
    • sharp를 이용해 큰 사이즈의 사진을 찰칵 웹 페이지에 적합한 크기로 리사이징해 사이즈를 대폭 줄였다.
 
  • 결과
    • 업로드 속도 ⇒ 약 297ms에서 134ms으로 54.21% 개선
      • 리사이징 전
        • notion image
         
      • 리사이징 후
        • notion image
       
    • 사진 SIZE ⇒ 160,324를 72,626으로 54.7% 감소
      • 리사이징 전
      • notion image
      • 리사이징 후
      notion image
 

즉각적인 Bull Queue 작업결과 응답

요청 결과가 잘못되는 문제
🐐
발생 문제 모임 참여 신청 기능에서 Bull Queue로 구현한 후 아파치 Jmeter로 테스트를 했는데, 대기열에 Job을 넣은 이후 그 결과를 기다리지 않고 모든 요청에 대해 바로 성공 값을 반환하는 이슈가 발생 (성공/실패 여부와 상관없이 항상 성공 결과 리턴)
 
  • 해결과정
    • 작업이 완료될 때까지 잠시 기다린 후 응답을 반환하기 위해 메서드 간에 Event-Emitter를 사용했다.
 
  • 결과
    • [해결 전] Bull Queue만 사용
      • 15, 16번 유저만 참여에 성공
      • 참여에 실패한 14, 17, 18번 유저도 201 성공 결과를 반환 받고 있음
      notion image
      notion image
       
    • [해결 후] Event-Emitter를 통해 특정 이벤트를 기다리게 함
      • 14, 15번 유저만 참여에 성공
      • 참여에 실패한 16, 17, 18번 유저는 403 실패 결과를 반환 받음 (정원 초과)
      notion image
      notion image
 
Share article
Subscribe to our newsletter
RSSPowered by inblog