프로젝트를 뛰다 보면 빠짐없이 찾아오는 순간이 있음. "어, 내 로컬에선 문제없었는데...?" 1년 넘게 잊고 지냈던 성능 이슈가 CJ IFRS(연결회계솔루션) 프로젝트를 하면서 제대로 터졌음.
1. 배경 설명: 1초 vs 60초의 격차
프로젝트 투입 초기, 프레임워크 소개 페이지와 솔루션 페이지의 기능은 비슷했음. 하지만 로딩 속도는 60초 가까이 차이가 났음. 화면이 멈춘 듯한 그 정적... 실사용자였다면 벌써 이탈하고도 남았을 시간이었음.
데이터 차이가 범인이라는 건 직감했음. 프레임워크 개발 당시엔 공통코드 100개, 다국어 500개가 전부였는데, 실제 프로젝트에선 공통코드 4,000건에 다국어 12,000건이 쏟아졌음. 하지만 의문이 들었음. "고작 수만 건 데이터에 1분이라니? 이게 말이 되나?"
2. 문제 원인: 범인은 WAS가 아니었음
서버 로그를 샅샅이 뒤졌음. 쿼리 응답 속도는 아주 쌩쌩했음. 서버는 죄가 없었음. 문제는 프론트엔드, 그중에서도 데이터를 처리하는 과정에 있었음. 고도화 과정에서 다 없앴다고 생각했던 '그놈', reduce가 데이터 가공 로직 구석에서 똬리를 틀고 있었음.
JavaScript // 매번 배열 전체 순회 + 중복 체크 + 객체 생성 콤보 const processedData = rawData.reduce((prev, next) => { // ... 대환장 파티의 코드들 return prev; }, {}); 데이터가 쌓일수록 이놈이 브라우저의 메인 스레드를 혹사시키고 있었던 거임.
왜 Reduce를 지양해야 하는가?
O(n²) 복잡도: 중첩된 순회와 탐색은 데이터가 10배 늘면 연산은 100배 늘어남.
메모리 오버헤드: 매 루프마다 생성되는 새로운 객체와 참조 복사는 브라우저 가비지 컬렉터의 짐이 됨.
유지보수 지옥: 로직이 길어질수록 가독성은 나락 가고, 버그는 증식함.
🛠 3. 해결책: 만만한게 Lodash
고민할 것도 없이 바로 리팩토링에 들어갔음. reduce 로직을 걷어내고, 성능이 검증된 Lodash 유틸리티를 도입했음.
// 수정 후: 덩어리별로 나누고, 깔끔하게 중복을 뺌
const groupedData = _.groupBy(rawData, 'common_type');
const processedData = _.mapValues(groupedData, (items) => {
const uniqueItems = _.uniqBy(items, (item) => `${item.key}-${item.value}`);
return [allEntry, ...uniqueItems];
});왜 Lodash인가?
성능 최적화 : 내부적으로 구현된 알고리즘이 브라우저 최적화에 맞춰져 있어, 우리가 직접 짠 로직보다 훨씬 효율적으로 메모리를 관리함.
가독성: groupBy, uniqBy 등 직관적인 메서드 이름만으로 코드의 의도가 명확해짐. 협업하는 동료들이 "이 코드가 왜 이렇게 돌아가는지" 고민할 시간을 줄여줌.
4. 결과: 60초에서 1초로
결과는 드라마틱했음. 60초 가까이 걸리던 로딩 속도가 단 몇 초로 줄어들었음.