Skip to content

Repository files navigation

맛집 등록/검색 웹 프로젝트

  • 맛집을 등록하고 키워드를 통해 검색할 수 있습니다
  • 필터링을 통하여 맛집을 추천받을 수 있고, 카테고리별로 맛집을 찾아볼 수 있습니다.

프로젝트 목표

  1. 테스트 코드를 통해 개발한 코드의 신뢰성 높이기
  • 이전 프로젝트 진행 시, 개발과정에서 코드의 안정성과 신뢰성에 대한 불편한 경험을 겪었습니다.
  • 코드가 지속적으로 변경되는 상황에서 해당 변화가 다른 부분에 어떤 영향을 미치는지 확인할 수단을 마련할 필요가 있다고 생각하였습니다.
  • 또한 다양한 시나리오에 대한 체계적인 테스트가 필요하다 생각하였습니다.
  • 다양한 시나리오에 대해 테스트 코드를 작성하고 높은 테스트 커버리지(81%)을 달성하였습니다.
  1. 문서화
  • 이전 프로젝트 개발 과정에서 다양한 문제들을 겪게 되었으며, 이러한 문제들을 해결하는 과정에서 발생한 지식과 해결책들이 팀 내에서 공유되지 않고 남겨져 있었습니다.
  • 이로 인해 유사한 문제에 대한 빠른 대응과 지식 공유가 어려웠습니다.
  • 이번 TWOGATHER 프로젝트에서는 이 부분에 대해 신경을 써가며 작업을 하려고 합니다.
  1. 배포 자동화
  • 작은 프로젝트에서는 수동 배포가 크게 불편하지 않을 수 있지만, 나중에 프로젝트 규모가 커질 것을 생각하면 일관성 유지와 시간/비용 절감 측면에서 자동화가 필요하다고 생각하였고, 이를 도입하게 되었습니다.
  1. 서버 모니터링 툴을 통한 버그 탐지
  • 테스트 코드를 이용하여 어느 정도의 시나리오에 대해서는 검증이 되었으나, 트래픽이 몰릴 경우에 대한 대처가 되지 않았다고 생각하였습니다.
  • 서버 모니터링 툴을 통해 어느정도의 트래픽을 겪게 됐을때 동시성 문제 등 다양한 문제들이 일어나는지에 대해 탐색해보고 이를 해결해보는 과정을 겪어보려고 합니다.

기술적 이슈

캐시 도입 과정에서 다양한 주제에 대해 탐색해보고 조회 성능 향상

  • 최적의 캐시 클라이언트 선택을 위해 Lettuce와 Jedis 간의 성능 테스트에 대해 연구하였습니다.
  • 그 결과, Lettuce가 더 나은 응답시간을 보여줬고 이에 따라, 프로젝트에 Lettuce를 적용하기로 결정하였습니다.
  • 그 다음, 캐시 도입에 따른 다양한 문제점과 해결 전략에 대한 연구를 진행하였습니다.
  • 특히 캐시 도입 시 발생하는 데이터 정합성 문제, 캐시 만료 시간 설정과 같은 복잡한 이슈들에 대한 해결책을 모색하고, 수정/삭제 시에 캐시를 비워서 캐시 된 정보와 실제 데이터가 다른 문제를 해결하였습니다.
  • 또한 캐시 적용 시 웹 계층의 상태 정보를 제거하여 무상태 웹 계층을 설계하였습니다. 스케일 아웃 시, 각 서버가 세션 저장소를 독립적으로 가지게 되면 정합성 문제가 발생할 가능성이 있습니다. 이를 해결하기 위해 다양한 방안, 예를 들면 세션 클러스터링, 세션 스토리지 분리 등 다양한 방안에 대해 탐색하며, 가장 효과적인 방법(무상태 웹계층)을 선정하여 적용하였습니다.

쿼리 튜닝을 통한 조회 성능 개선

  • 프로젝트는 '빠르고 안정적인 서비스 제공'이라는 핵심 비즈니스 목표가 있었습니다.
  • 그러나 부하 테스트를 수행할 경우 Search API의 응답 시간이 급격하게 늘어나는 문제가 발생하였습니다.
  • 응답시간은 8초를 넘어가 사용자 경험이 저하되었고, 서비스의 안정성에도 문제가 발생할 위험이 있었습니다.
  • 원인을 파악하기 위해 모니터링 도구인 pinpoint를 활용하였습니다. 그 결과, getConnection()에서 지연이 발생함을 확인하였고, 여러 원인에 대해 가정하고 탐색하였습니다.
  • 잘못된 Connection Pool size 설정 때문에 새로운 요청이 대기 상태가 되어 시스템의 전체적인 처리 능력이 저하될 수 있지만 size 값을 조정해도 같은 문제 상황이 반복되었습니다.
  • 다음으로 서버의 CPU와 메모리 사용량을 모니터링하였습니다. 데이터베이스 서버와 프로젝트 서버 모두 과부하 상태가 아니었기 때문에, 이를 통해 서버 과부하가 getConnection()의 지연 원인은 아니라는 결론을 내렸습니다.
  • 다음으로 쿼리 개선에 대해 시도하였습니다. 기존 쿼리는 단일 요청 당 130ms 정도로 속도가 느리다는 것이 체감되진 않았으나 DB의 약 10,000개의 row를 고려했을 때, 대량의 데이터 처리에 상당한 시간이 소요되었을 것이라고 추측하였습니다. 해결책으로 사용자에게 대략적인 페이지 수를 제공하였고, 검색 버튼 클릭 유무를 백엔드 서버로 전달하여 해당 정보에 따라 count쿼리의 실행 유무를 결정하였습니다. 이로써 불필요한 시점에서의 count쿼리의 수행을 최소화하여 130ms> 30ms로 개선하였습니다.

테스트 코드 리팩토링인수테스트에서 테스트 격리하기

  • 이전 프로젝트에서는 마감에 쫓겨 POSTMAN 같은 API 도구로만 테스트를 수행했습니다. 이 방식의 문제는 다음과 같았습니다.
    • 첫번째로, 코드가 지속적으로 변경되었지만 이러한 변화가 다른 부분에 어떤 영향을 미치는지 확인할 수단이 없었습니다. 특히 규모가 큰 프로젝트에서는 이런 점검이 필수적입니다.
    • 두번째로, POSTMAN만을 사용하면 다양한 사용자 시나리오나 예외 상황에 대한 체계적인 테스트가 어려웠습니다.
  • 이러한 문제점을 극복하기 위해, 코드로 테스트를 작성하기 시작했습니다.
  • 이러한 이유들로 현재 프로젝트에서는 테스트를 꼼꼼히 수행하여 테스트 커버리지 81%를 달성하였고, 테스트 코드를 리팩토링하는 과정을 거쳐 유지보수에 기여하였습니다.
  • 하지만 인수테스트 중 다음과 같은 문제가 발생하였습니다.
    • 인수테스트 시 HTTP 클라이언트와 서버 간의 별도의 포트와 스레드 문제로 @Transactional 어노테이션이 동작하지 않았습니다.
    • 이 문제를 해결하기 위해 TRUNCATE 쿼리를 사용한 클래스를 도입하였습니다.
    • TRUNCATE는 여러 행을 개별적으로 삭제하는 DELETE와 달리 테이블의 모든 행을 한 번에 제거하므로 효율적이었습니다.
  • 이로 인해 테스트 이후의 데이터 정리 시간이 크게 단축되었고, 이는 전체 테스트 수행 시간에도 긍정적인 영향을 주었습니다. TRUNCATE 쿼리를 도입함으로써, 테스트 환경에서의 데이터 정리 과정이 더욱 안정적이고 빠르게 이루어지게 되었습니다. 이는 테스트의 신뢰도를 높이고, 잦은 테스트 수행 시 발생할 수 있는 불필요한 대기 시간을 줄여주었습니다.

동시성 문제 해결

JWT와 RefreshToken 도입기

프로젝트 한계

  • 자바8 문법 일부가 적용이 되지 않은 점
  • 코드 리뷰가 빈약한 점
  • 캐시 적용시 적절한 만료시간을 찾아 설정하지 못한 점

CI / CD

image

Teck stack

image

ERD

image

팀원

Backend 🌟 Backend 🌟
민지 지호
민지 지호

About

맛집 등록 검색 웹 백엔드 프로젝트입니다

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages