개발놀이터

온라인 쇼핑몰 ver.3 (2) : 기술 선택 이유 본문

사이드 프로젝트/온라인 쇼핑몰 ver.3

온라인 쇼핑몰 ver.3 (2) : 기술 선택 이유

마늘냄새폴폴 2023. 6. 16. 20:25

프로젝트를 구현할 때 어떤 기술을 어떻게 구현했는지도 물론 중요하겠지만 왜 그 기술을 선택했는지에 대한 고찰이 조금 부족했다는 생각이 들었습니다. 

 

물론 사전에 조사는 다 했지만 직접 써보고 단점을 겪어본 것이 아니기 때문에 추상적일 뿐이었습니다. 

 

이번 기회에 정확히 어떤 기술이 어떤 장점과 단점이 있고 내 프로젝트에 어떤 이유때문에 이 기술을 선택하게 되었는지에 대한 정리를 해봤습니다. 

 

 

기술 선택 이유

제 프로젝트에는 다음과 같은 기술이 선택되었습니다. 

 

  1. 캐싱에 Redis
  2. 검색엔진에 Elasticsearch
  3. 인증에 JWT
  4. 동시성 문제 해결에 비관적 락
  5. 배포에 docker
  6. 자동화에 jenkins

 

1. 캐싱에 Redis

스프링에서 캐싱을 사용하기 위해서 다양한 방법이 존재합니다. 

 

  1. Redis
  2. Memcached
  3. EHcache

 

제 프로젝트를 처음 기획할 때 이 세가지 유형의 캐시 중 Redis를 선택했습니다. 

 

그 이유는 Memcached와 EHcache의 단점 때문입니다. 

 

Memcached

Memcached는 String 타입의 자료구조만 지원합니다. 또한 자체적인 레플리케이션을 지원하지 않기 때문에 확장성에 매우 불리합니다. 제가 추후에 레플리케이션에 대해서 적용해볼 예정이지만 Memcached는 위와 같은 이유 때문에 제 프로젝트에 부적합하다고 생각해서 제외했습니다. 

 

EHcache

스프링에서 자체적으로 제공하고 있는 캐시중 하나입니다. EHcache는 인스턴스가 여러개 떠있는 MSA에서 불리한 모습을 보여줍니다. 세션 클러스터링을 하듯 데이터 싱크를 맞춰줘야 한다는 문제가 있습니다. 때문에 인스턴스가 여러개인 경우에는 기술의 복잡도가 올라가게 되어있습니다. 때문에 EHcache를 제외했습니다. 

 

 

2. 검색엔진에 Elasticsearch

사실 검색 엔진에는 선택의 여부가 없었습니다. Elasticsearch가 독보적이었기 때문입니다. 

 

아직 추가하진 않았지만 추후에 추가할 예정인 ELK스택을 이용해 로그 관리를 할 예정이기 때문에 검색엔진에 Elasticsearch를 사용하는 것은 제 프로젝트에 당연하다고 생각했습니다. 

 

3. 인증에 JWT

인증 레이어를 스프링 시큐리티만 단독으로 사용하다 JWT와 섞어서 사용했습니다. 

 

제 꿈 중 하나인 제 프로젝트를 MSA로 바꾸는 작업을 위한 준비과정으로써 사용했습니다. MSA가 되면 각각의 도메인들이 격리된 인스턴스로 존재하게 되는데 각각의 도메인끼리 통신을 할 때 인증 방식으로 stateless한 JWT가 적절하다는 판단하에 인증을 JWT로 바꾸게 되었습니다. 

 

4. 동시성 문제 해결에 비관적 락

우선 낙관적 락과 비관적 락을 얘기하기 전에 synchronized 키워드와 serializable 격리수준으로의 격상을 얘기하고 넘어가겠습니다. 

 

synchronized 키워드

syncrhonized 키워드도 물론 동시성 문제를 해결하는데 탁월한 모습을 보여주지만 JPA를 사용하고 있는 제 입장에서는 별로 매력적으로 느껴지지 않았습니다. 

 

기본적으로 JPA는 각각의 스레드에 트랜잭션이 부여되어 데이터베이스 격리수준에 따라 트랜잭션이 고립성을 가지고 있습니다. 때문에 웬만한 일이 아니고서는 동시성 문제가 발생하지 않습니다. 

 

때문에 syncrhonized 키워드로 모든 결제 로직에 락을 거는 것은 자원의 낭비라고 생각했습니다. 

 

serializable 격리수준으로 격상

serializable 격리수준으로 데이터베이스 격리수준을 격상하는 것 또한 동시성 문제를 해결하는 좋은 방법이 될 수 있습니다. 

 

하지만 serializable 격리 수준에서는 Deadlock (이하 데드락) 현상이 발생했습니다. synchronized 키워드로 동시성 문제를 해결했으면 걱정하지 않아도 될 데드락문제 때문에 선택하지 않게 되었습니다. 

 

데드락 문제를 해결하기 위해 데이터 조회 결과 값을 Collections.sort() 를 이용해 정렬해주면 데드락 문제가 해결되었습니다만, sort() 메서드는 정렬하는데 시간복잡도가 O(nlogn)이 걸리기 때문에 serializable 격리수준으로 생기는 성능 문제와 정렬에 필요한 시간복잡도를 고려하면 성능적인 이슈가 상당할 것으로 예상되었습니다. 

 

낙관적 락과 비관적 락

이제 낙관적 락과 비관적 락에 대해서인데, 기본적으로 낙관적 락과 비관적 락의 차이점을 잘 알고 있습니다. 

 

낙관적 락은 커밋시에 충돌이 일어날 경우 예외가 발생하여 그 예외를 처리해줌으로써 동시성 문제를 해결할 수 있습니다. 때문에 충돌이 빈번하지 않은 경우에 사용하면 좋습니다. 

 

비관적 락은 동시성 문제가 발생한다고 가정하고 데이터 조회에 락을 거는 것이기 때문에 성능상 낙관적 락에 비해 손해를 보지만 컨셉 자체가 이용자 수가 1000만명을 가정하고 있기 때문에 충돌이 빈번할 것으로 예상되어 비관적 락을 선택했습니다. 

 

5. 배포에 Docker

도커를 경험해보고 무조건 배포엔 도커를 사용해야겠다고 생각했습니다. 

 

제가 느낀 도커의 장점은 다음과 같습니다. 

 

  1. 버전 관리가 너무 쉽다 : MySQL의 버전을 올리고 싶으면 이미지를 새로운 MySQL의 이미지를 받아오면 됩니다. 이전에는 우분투에 깔려있던 MySQL을 지우기 위해서 귀찮은 짓을 해야했지만 이젠 이미지만 없애고 다음 버전의 이미지만 풀링해오면 됩니다. 
  2. 추상화로 배포의 단조로움 : 컨테이너로 추상화한다는 개념 자체가 신기했는데, OS위에 컨테이너를 띄우고 그 컨테이너안에서 추가적으로 관리가 가능하다는 점이 매력적이었습니다. 
  3. 블루 그린 배포 : 블루 그린 배포에 도커가 제격이었습니다. 작동하지 않는 애클리케이션 서버를 미리 띄워두고 기존 포트를 삭제하면서 새로 띄운 포트를 장착시켜주기만 하면 되는 구조는 정말 편했습니다. 도커 컴포즈를 이용해 정말 쉽게 스위칭이 됐습니다. 

 

사실 도커는 경쟁상대가 없기 때문에 제 프로젝트 선택에서 당연한 선택이 되었습니다. 

 

6. 자동화에 jenkins

CI 자동화에 jenkins와 github action이 있다고 들었는데 많은 개발자들에게서 각각의 장단점에 대해서 알 수 있었습니다. 

 

jenkins는 유지보수가 힘들고 github action은 잡 스케쥴링에 이슈가 있다. 

 

저는 CI 자동화에 잡 스케쥴링 이슈는 치명적이라고 (개인적으로) 생각합니다. 또한, jenkins가 넓은 커뮤니티를 가지고 있어 트러블 슈팅하는 과정이나 어떻게 사용해야 하는지에 대한 레퍼런스를 많이 참고할 수 있었습니다. 

 

 

이렇게 제 프로젝트인 온라인 쇼핑몰 ver.2에서 어떤 기술을 사용했고 왜 사용했는지에 대해서 기술했습니다.