내가 백엔드로 참여하고 있는 MJS 프로젝트가 thingo로 명칭을 바꾸게 되면서,
나는 새로운 기능 개발 보다는 성능 개선 파트를 집중적으로 담당하며 진행하기로 하였다.
이에 따라 성능 개선을 위하여 알아두어야 할 몇가지를 정리하고자 한다.
1. 문제로 볼 기준, 2. 문제 후보 카테고리 를 정리한 후
1. 한 기능에서 어떤 문제가 나타나고 있는지, 2. 개선 방향성 잡기, 3. 개선 진행 후 결과의 형태로 나타낼 예정이다.
1. 문제로 볼 기준
성능 문제는 단순히 “느린 것 같다.”, “서버가 힘들어 보인다”, “코드가 비효율적인 것 같다” 같이 감각적인 판단으로 해서는 안된다.
Google SRE는 사용자에게 제공되는 시스템을 바라볼 때 최소 latency, traffic, errors, saturation 네 가지 신호를 체크해야한다고 말했다.
또한 AWS Well-Architected의 Performance Efficiency관점에서도 성능은 “더 빠르게 보이느냐” 뿐 아니라 요구사항을 만족하는 수준으로 자원을 효율적으로 사용하느냐의 문제라고 말했다.
즉 성능 문제라는 것은 단순 체감이 아닌 응답시간, 처리량, 에러율, 자원사용량, 병목 위치를 기준으로 잡는 것이 중요하다.
1-1. 성능 문제의 1차 정의
: 사용자 요청 또는 내부 작업이 기대한 시간 안에 처리되지 않거나, 처리 과정에서 과도한 자원 사용·에러·병목을 유발하여 서비스 품질을 떨어뜨리는 상태
- 사용자 시점에서의 “느림” 뿐 만 아니라 서버 내부의 “비효율” 또한 포함한다.
- API 요청 뿐 만 아니라 크롤링, 배치, 동기화 같은 내부 작업도 성능 범위에 포함시킬 수 있다.
1-2. 띵고 프로젝트에서 사용할 수 있는 구체적 판단 기준
(1) 응답시간이 과도하게 길다
웹 성능에서 가장 직접적으로 체감되는 기준은 응답시간이다.
일반적인 사용자 경험 기준에서 반응은 대략 50~200ms 수준이 바람직하다.
1초를 넘어가면 지연이 체감되기 시작한다고 말한다.
따라서 thingo에서 주요 API나 핵심 화면 로딩이 해당 범위를 반복적으로 크게 넘긴다면 성능 문제로 체크해 볼 수 있다.
한 번 느리게 나왔다 (X), 지속적으로 체감 지연이 발생한다. (O)
(2) 요청량이 늘어날 때 처리 효율이 급격히 떨어진다.
SRE에서 latency와 함께 traffic을 같이 보는 이유는, 평소에는 괜찮던 시스템이 요청량이 많아질 때 갑자기 느려지는 경우가 많기 때문이다.
따라서 평상시 속도만이 아닌 부하가 걸렸을 떄 얼마나 안정적으로 처리되는로 판단해야한다.
한 번 호출했을 때 빠르냐 (X), 지속적인 요청이 많을 때에도 유지되느냐(O)
(3) 에러율 상승이 성능 저하와 함께 나타난다.
성능 문제를 단순 속도의 문제로만 보면 안되는 이유는, 성능 저하가 종종 에러의 증가와 같이 오기 때문이다.
Google SRE는 error를 핵심 지표 중 하나로 보고, 사용자 입장에서는 응답 속도 보다 실패한 응답에 대해 더 직접적인 품질저하라고 느낀다.
따라서 HTTP 상태코드 기준으로도 4xx/5xx는 요청 실패 또는 서버 문제를 의미하기 때문에 느려짐과 함께 이러한 응답이 많아진다면 성능 관점의 장애가 있다고 판단할 수 있다.
(4) CPU, 메모리, DB커넥션, I/O 등 자원 포화가 발생한다.
SRE의 네 번째 신호인 saturation은 시스템 자원이 어느 정도 차있는지, 즉 병목이 어디서 생기는지 파악하는 데에 중요하다.
사용자는 느리다고 느끼지만 실제 원인은 CPU 포화일 수 있고, 혹은 DB 커넥션 고갈, 디스크 I/O 병목일 수도 있다.
따라서 응답이 늦다는 것에만 머무르기 보다는 왜 늦어지는가를 자원 포화 기준으로 확인해야 한다.
해당 기주은 특히 EC2와 같은 제한된 환경이나 단일 서버 구조에서 중요하다.
예를 들어 크롤링, 배치, API 요청 처리가 같은 자원을 공유하면 특정 시간대에 서로 영향을 줄 가능성이 커진다.
(5) 시스템 내부에서 병목 지점을 설명할 수 있다.
Controller - Service - Repository - DB - 외부 API - 직렬화/역직렬화 중 어디서 시간이 가장 많이 소요되는지를 설명할 수 있을 때 제대로 정의 된다.
OpenTelemetry는 observability를 “내부를 직접 보지 않고도 왜 이런 일이 일어나는지 질문할 수 있게 하는 능력”이라고 말한다,.
즉 성능 문제 정의는 측정 가능성과 원인 추적 가능성이 포함되어야 한다.
따라서 우리의 thingo 프로젝트의 성능 문제는 아래처럼 정의하는 것이 더 좋다.
- 검색 API가 느리다 (X)
- 검색 API 전체 1.8초 중 DB 조회가 1.2초를 차지해 병목으로 추정된다. (O)
- 목록 조회가 느리다 (X)
- 목록 조회 시 연관 엔티티 접근 때문에 추가 쿼리가 반복 실행된다 (O)
1-3. 문제 기준을 위와 같이 잡은 이유
위 기준을 종합한다면 thingo 프로젝트에서 성능 문제는 아래의 네 질문으로 요약가능하다.
- 사용자가 느리다고 체감하는가
- 요청이 늘면 더 나빠지는가
- 에러나 타임아웃이 함께 증가하는가
- 어떤 자원이나 처리 단계가 병목인지 설명 가능한가
해당 네 가지는 latency, traffic, errors, saturation이라는 검증된 운영 지표와 연결되므로, 성능개선의 출발 기준으로 쓰기에 적절하다.
출처
https://sre.google/sre-book/monitoring-distributed-systems/
https://www.postgresql.org/docs/current/indexes-intro.html?
https://www.postgresql.org/docs/current/using-explain.html
https://docs.hibernate.org/orm/5.0/userguide/en-US/html/ch09.html?