본문 바로가기

Project/DelFood13

[DelFood] Ngrinder로 진행한 성능 테스트 개요 Delfood의 0.1버전을 배포하였습니다. 제가 만든 프로젝트가 실제로 트래픽을 받았을 때 어느정도의 퍼포먼스를 보여주는지 측정해보기로 했습니다. 사용한 부하 발생기는 Ngrinder입니다. 정석 대로라면 직접 Tomcat에 설치하고 각종 환경을 세팅해야 하지만 보다 간편하게 만들어져 있는 Docker 이미지를 받아 사용하였습니다. Ngrinder의 부하 발생 구조 Ngrinder는 크게 Controller, Agent 두 가지로 구성되어 있습니다. 실제 트래픽은 Agent에서 발생시키고 그 Agent를 Controller에서 관리합니다. 그림으로 보면 다음과 같이 볼 수 있겠네요. 스크립트 작성 과정 Ngrinder에서 부하를 발생시키기 위해서는 Groovy Script 작성이 필요합니다. 회원.. 2020. 2. 13.
[Delfood] CI/CD 서버 구축과 첫 배포 CI/CD의 필요성 프로젝트가 거의 완성이 되어가며 배포와 테스트 자동화의 필요성을 느꼈습니다. 이전에 했던 프로젝트는 직접 jar파일을 빌드한 후, FTP를 사용하여 서버에 올린 후 java -jar로 직접 실행시켰던 기억이 납니다. 자잘한 변경사항이 생긴다면 또다시 빌드 - 전송 - 직접 실행.. 을 반복해야 했죠. 서버에 올라간 후 문제가 없는지 직접 url을 기반으로 테스트까지 해야 했습니다. 했던 테스트를 또 반복하는게 상당히 귀찮은 일이였습니다. 최근 CI/CD의 개념을 배우며 위와 같은 귀찮은 문제를 쉽게 해결할 수 있다는 것을 알게 되었습니다. Github에 commit을 push하면 자동으로 소스코드를 통합하여주고, 배포까지 할 수 있는 편리한 툴이였죠. 바로 Jenkins였습니다. 이러.. 2020. 2. 11.
[이슈 #10] 푸시 메세지를 비동기로 처리하여 성능 개선하기 개요 DelFood에 드디어 푸시 메세지 전송 기능이 추가되었습니다. Firebase Cloud Messaging(이하 FCM)을 기반으로 앱, 웹으로 사용자에게 푸시 메세지 전송 기능을 제작하였습니다. 여러 사용자에게 순차적으로 푸시 메세지를 전송할 수도 있고, 한 명의 사용자에게 푸시 메세지를 전송할 수도 있습니다. 원한다면 하나의 topic을 따르는 사용자들에게도 일괄적으로 전송할 수 있습니다. 당장 구현을 할 때는 몰랐지만 구현을 다 마치고 곰곰히 코드를 보며 생각해 보니 의문점이 떠올랐습니다. '푸시메세지를 보낼 사용자가 엄청 많아지면 어떻게 되는거지?' 라는 생각이 들었습니다. 변경 전 로직 한 사용자에게 푸시 메세지를 전송하는 로직은 다음과 같습니다. 1. 사용자가 가지고 있는 토큰들을 조회.. 2019. 12. 31.
[이슈 #9] Mybatis <collection> 태그 N+1 문제 없이 사용하기 개요 DelFood 주문 기능에 있는 '가격 계산' 로직에서 N+1 쿼리 문제가 발생하는 것을 확인하였습니다. 주문 전 가격을 계산하는 기능이었는데 for문을 돌며 select query를 날리고, 또 mybatis의 collection 태그를 잘못 사용하여 꽤 느린 로직이었습니다. 변경 전 요청과 결과 값 [ { "menuId": "1", "count": "1", "options": [ { "optionId": "1" }, { "optionId": "9" } ] }, { "menuId": "4", "count": "2", "options": [ { "optionId": "4" } ] } ] { "menus": [ { "id": 1, "name": "바사칸치킨", "price": 3000, "option.. 2019. 12. 18.
[이슈 #8] 1:N:M 관계 INSERT 시 N + M번의 쿼리 발생을 리팩토링하기 개요 DelFood의 주문 로직을 작성하던 도중 발생하는 1 : N : M의 쿼리 이슈를 해결해 보려고 합니다. 주문 진행 로직 고객이 메뉴를 선택합니다. 해당 메뉴에 대한 옵션을 선택합니다. 여러 메뉴를 선택한 고객이 주문을 진행합니다 이 과정에서 주문 - 주문 메뉴 - 주문 메뉴 옵션은 1 : N : M의 관계를 가집니다. 이 관계를 다중 for문을 돌며 쿼리를 호출하게 된다면 많은 DB요청이 발생하게 됩니다. 처음 만든 다중 for문을 단 3번의 insert문을 호출하는 것으로 변경해보겠습니다. 개선 방향 1. 데이터베이스 스키마상 문제가 있는 테이블을 수정할 것입니다. 관계가 있는 테이블 입력 시 (주문메뉴 - 주문메뉴 옵션) 해당 부모 데이터의 PK가 필요합니다. 하지만 변경 전 DB 테이블은 .. 2019. 12. 18.
[이슈 #7] 서버 부하를 줄이기 위한 캐싱 적용 부하 증가 고려 저번 이슈에서 주소 데이터 조회 속도를 인덱스를 적용하여 개선하였습니다. 하지만 주소 조회를 계속해서 진행할수록 DB성능이 점점 떨어지는 것이 느껴졌고 이를 개선하기 위하여 주소 캐시를 적용하기로 하였습니다. 인덱스 적용 포스팅 https://deveric.tistory.com/68 [이슈 #6] 주소 데이터의 빠른 조회를 위해 인덱스 설정하기 얼마 전 주소데이터를 DB에서 관리하도록 변경하였는데, 데이터가 100,000,00건정도 되어서 검색이 정말 느렸습니다. 인덱스를 걸지 않은 상태로 조회를 하면 검색에만 10초가 넘게 걸리는 무시무시한 상황이었습.. deveric.tistory.com 캐싱 전략별 특징 Local Cache와 Global Cache중 어떤 전략을 사용할지 생각해보았.. 2019. 11. 21.
[이슈 #6] 주소 데이터의 빠른 조회를 위해 인덱스 설정하기 얼마 전 주소데이터를 DB에서 관리하도록 변경하였는데, 데이터가 100,000,00건정도 되어서 검색이 정말 느렸습니다. 인덱스를 걸지 않은 상태로 조회를 하면 검색에만 10초가 넘게 걸리는 무시무시한 상황이었습니다. 다행히도 주소 DB는 DML문이 거의 진행되지 않습니다. 가끔 공공데이터에서 업데이트 정보가 올라온다면 그 부분만 수정해주면 되는 것이죠. 조회는 많고 수정 삽입 삭제가 거의 없는 테이블이니 인덱스를 잡기에 부담은 없을 것 같습니다. 우선 해당 주소 DB를 구축하는 과정은 이전 포스팅에 적혀있습니다. 필요하시다면 참고해주세요. https://deveric.tistory.com/66?category=350852 [이슈 #4] 주소를 외래키로 관리하도록 변경(공공데이터 사용) 기존 방식의 문제.. 2019. 11. 1.
[이슈 #5] 반복되는 로그인 체크 로직을 AOP로 리팩토링하기 로그인 체크가 필요한 상황 회원의 정보가 필요하거나, 해당 회원의 접근권한을 확인해야 할 때가 있습니다. 마이페이지, 내 정보 수정 등이 대표적인 예 입니다. DelFood 화면 프로토타입의 마이페이지는 다음과 같습니다. 해당 화면을 출력하려면 일단 회원 로그인이 먼저 진행되어야 합니다. 비회원의 경우에는 내 정보를 볼 수 없겠죠. 변경 전 프로젝트 코드 AOP를 적용하지 않은 상태에서는 다음과 같이 코드를 작성하여 권한을 확인하였습니다. 1. HttpSession에서 Member id 정보를 가져온다. 2. 가져온 id 정보가 null이면 401 status를 반환한다. 3. id가 null이 아니면 핵심 로직을 수행한 후 반환한다. Session 체크용 공통 모듈 SessionUtil Code pub.. 2019. 11. 1.
[이슈 #4] 주소를 외래키로 관리하도록 변경(공공데이터 사용) 기존 방식의 문제점 기존에 주소를 입력하는 방식은 '주소', '상세주소' 컬럼을 사용하여 사용자의 주소를 직접 입력받는 식이였습니다. 당연히 이 주소들은 공통적인 포맷으로 관리하기도 어렵고, 주소 체계를 변경한다면 모든 테이블의 주소 체계를 전부 변경해야 했습니다. 간단하게, 변경 전 회원 테이블은 다음과 같이 생겼습니다. MEMBER Table Schema 위 방식과 같이 주소를 직접 입력하도록 한다면, 주소를 이용한 좌표 검색, 입력한 주소 별 정렬 등 주소 관리가 어려울 것이라 생각됩니다. 또한 도로명주소와 지번 주소의 관리도 어렵게 되어 테이블의 변경이 필요하다고 판단하였습니다. 1. 주소 정보를 공공데이터에서 가져오기 '공공데이터포털'에서는 도로명주소, 지번주소, 좌표 등의 데이터를 가지고 있는.. 2019. 10. 27.