[Spring] 필터 구성 api 의 성능 이슈

HeeJun·2024년 7월 15일

오늘 기록 할 내용은 업무 중 성능 이슈가 발견되어 원인 파악과, 해결한 방법이다.

성능 이슈가 발견 된 api 는 사내 데이터 분석가와 수집 스크립트 개발자들이 매일 사용하는 데일리 신규 상품을 확인하는 페이지의 필터를 구성하는 api 였다.

결론부터 말하자면

기존 응답속도 : 10sec / 데이터 size : 50MB 에서 응답속도 : 65ms / 데이터 size : 20KB 로 개선되었다.

총 5개의 필터가 존재하는데 유독 그중 하나의 필터만 api 응답이 느려 front 에서 rendering 이 늦어지는 현상이 있었다.
해당 필터는 국내의 모든 보험사 이름과, 내부 db 의 seq 값을 갖는 필터였는데, 자주 사용하는 필터인 만큼 항상 불편함을 느끼고 있었고, 개선이 필요하다고 판단해 원인 파악을 진행했다.

[STEP 1] Browser 개발자 도구 활용하기

일단 Browser 에서 rendering 이 늦어지고 있으니, Front rendering 로직의 문제 혹은 필요한 데이터를 제공하는 api 의 응답이 늦어는 것이 원인이라고 생각했다.
Front 의 필터 구성 로직을 먼저 확인한 결과 단순히 api 의 응답 데이터를 기반으로 element 를 추가하는 단순한 로직이였다. 크게 rendering 속도에 영향을 줄 만한 문제점은 파악되지 않았다.
다음으로는 api 의 응답 속도를 체크했다. 일단 보험사에 관한 데이터는 table 이 분리되어 관리되고 있었고, 총 row 수가 60 을 넘지 않고 필터 구성에 필요한 데이터는 보험사 이름과, db seq 값 뿐인데 응답 속도는 거의 10 sec 가 나오고 있었다.

[STEP 2] 개발자 도구 network tab 활용하기

응답속도 체크는 했고, 다음으로 data volume 을 확인했다. 확인한 결과 필요하다고 판단한 데이터량에 비해 실제로 api 호출을 통해 응답되는 데이터의 volume 이 50MB 이상으로 거대했다.
과도하게 큰 data volume 이 원인이라고 판단했다.

[STEP 3] service layer 와 query 확인하기

필터 구성에 필요한 데이터 제공을 위한 api 의 service layer 를 확인한 결과 단순히 mybatis mapper 를 호출하고 있어 원인은 query 에 있을 것이라고 판단했다.
query 를 확인한 결과 필터 구성에 필요한 보험사 데이터만을 조회하는 query 를 작성해 사용한 것이 아니라 보험사와 그와 연관 된 모든 보험 상품 더 나아가 보험 상품과 연관 된 모든 데이터를 join 해서 만들어진 full data 를 그대로 반환하고 있었다.

원인 파악을 마친 후 두 가지의 해결 방안을 생각했다.

1. query 는 그대로 사용하고 service layer 에서 data 를 필터링 한 후 응답 데이터를 반환한다.
2. service layer 는 그대로 두고 query 를 필요한 데이터만 조회 할 수 있도록 수정한다.
여기서 두 가지의 방식을 가지고 많은 고민을 했다. 각각 장단점이 명확하기 때문이다.

[Solv 1] Service Layer 에서 데이터 정제

장점 : Query 문이 단순해지고, 로직이 service layer 에서 관리되기 때문에 유지보수에 용이하다. 추후 추가적인 정제가 필요하다면 로직 추가 작업이 편리하다.
단점 : Full Data 의 Volume 이 크면, DB 에 부하가 발생 할 수 있다. 또한 대량의 데이터를 Server 의 메모리에 load 해야하기 때문에 자원 낭비가 발생 할 수 있다.

[Solv 2] 데이터 조회 시 정제

장점 : 필요한 데이터만 선택적으로 조회 할 수 있어 성능 향상, 메모리 낭비가 줄어들고 데이터 전송 속도에서도 이득을 취할 수 있다.
단점 : Query 가 복잡해질 가능성이 있고, 추후 요구사항의 변동이 발생할 경우 유지보수가 어려울 수 있다.
본인은 많은 고민 끝에 두 번째 방법을 선택하기로 했다.
일단 기본적으로 Full Data 의 volume 이 너무 크기도 했고, 단순 필터 구현을 위한 데이터를 조회하는 것이기 때문에, 데이터 정제 로직이 필요하지 않고 하나의 Table 내에서 필요한 Column 만 조회하면 되기에 추후 유지보수에도 어려움이 없을 것이라고 판단했다.
또한 개인적으로 해당 Query 로 조회되는 데이터 중 90% 이상은 필요없는 데이터기 때문에 새로운 Query 를 작성하고 로직의 의도를 명확히 하는 것이 중요하다고 판단했다.
JPA 라면 Lazy Loading 을 사용하고 Company Entity 에서 필요한 데이터만 추출해 DTO 든 VO 든 변환 해 응답 데이터로 사용했을테니 이런 고민은 하지 않았을지 모른다. 아직 Mybatis 를 사용하는 시스템이라 데이터 정제에 대한 책임을 어디에 부여할지 고민 할 수 있는 좋은 기회가 되었다.

결론적으로 10 sec 의 응답속도와 응답 데이터의 size 가 50MB 가 넘는 api 는 65 ms 의 응답 속도와 응답 데이터의 size 는 20KB 가 안되는 api 로 개선되었다.

profile
내가 작성한 코드 한 줄로 누군가를 편하게

0개의 댓글