내가 생각하는 리액트 쿼리로 인한 큰 장점은 데이터의 캐싱과 동기화를 자동으로 처리하여 네트워크 요청을 최소화하고, 사용자 경험을 향상시키는 것이 아닐까 생각한다.
그래서 이를 측정하기 위해서는 어떻게 해야할까?
1단계 : 기본 상태에서 성능 측정
- 백엔드 요청 로깅 설정 : 캐싱 적용 전의 서버 요청 수를 측정
- 프론트엔드 API 호출 로깅 : 프론트엔드에서 발생하는 API 호출 횟수를 기록
- 서버 성능 지표 측정 : 캐싱 적용 전 서버의 자원 사용량을 측정
2단계 : 리액트 쿼리 캐싱 적용
- 리액트 쿼리 캐싱 설정 : 캐싱을 적용하여 서버 요청 수를 줄입니다
3단계 : 캐싱 적용 후 성능 재측정
- 재측정 및 로그 분석 : 캐싱 적용 후의 서버 요청 수 및 성능을 측정(백엔드, 프론트엔드, 서버 성능 재측정)
4단계 : 데이터 비교 및 분석
- 서버 성능 지표 비교 : 캐싱 전후의 서버 자원 사용량을 비교
이와 같은 과정을 통해 리액트 쿼리의 캐싱 기능이 서버 성능에 미치는 영향을 측정하고자 한다.
1단계 : 기본 상태에서 성능 측정
1-1. 백엔드 요청 로깅 설정
우선 기본 상태에서 성능을 측정하기 위해서 캐시가 적용되지 않은 상태에서
현재 벡엔드로 사용 중인 노드 서버에 로깅 추가하여 청에 대한 기본 로그를 기록한다.
또, AWS CloudWatch Logs에 연동하여 실시간으로 로그를 모니터링한다.
1) Node.js 애플리케이션에 로깅 추가
- Express 애플리케이션에서 morgan을 사용하여 HTTP 요청 로그를 남긴다.
2) AWS CloudWatch Logs에 연동하기
Elastic Beanstalk 콘솔에서 로그 스트리밍 활성화
- Elastic Beanstalk 콘솔에 로그인합니다.
- 애플리케이션을 선택한 다음, 로그를 연동하고 싶은 **환경(Environment)**을 선택합니다.
- 왼쪽 사이드바에서 **Configuration(구성)**을 선택합니다.
- Software 카테고리에서 Modify를 클릭합니다.
- CloudWatch Logs 섹션에서 Stream logs 옵션을 활성화합니다.
- Log Group 이름을 설정합니다. 기본적으로는 /aws/elasticbeanstalk/<환경 이름>/var/log/web.stdout.log와 같은 형식으로 생성됩니다.
- Apply 버튼을 눌러 설정을 저장합니다.
이제 Elastic Beanstalk 환경에서 발생하는 로그가 자동으로 AWS CloudWatch Logs에 전송됩니다.
3) CloudWatch Logs에서 로그 확인
- AWS Management Console에서 CloudWatch 서비스를 엽니다.
- 왼쪽 메뉴에서 Logs를 클릭합니다.
- Log Groups에서 앞서 설정한 Log Group을 선택합니다. 예: /aws/elasticbeanstalk/<환경 이름>/var/log/web.stdout.log
- Log Streams에서 로그 스트림을 선택하면 실시간으로 로그를 확인할 수 있습니다.
4) 로그 필터링 및 분석
- CloudWatch Logs의 필터 기능을 사용하여 특정 조건에 맞는 로그만을 확인할 수 있습니다.
- Metrics Filter를 설정하여 로그를 기반으로 성능 지표를 생성하고 대시보드에 시각화할 수도 있습니다.
이제 NodeJS 로깅을 위해 Express의 미들웨어 morgan 로깅 라이브러리와 CloudWatch Logs 를 통해 HTTP 요청 로그를 남길 수 있습니다.
1-2. 프론트엔드 API 호출 로깅
프론트엔드 API 호출 로깅을 위해 캐시가 없는 상태에서 API 요청을 할 수 있도록 staleTime 과 gcTime 을 0으로 한 다음,
콘솔 로그를 통해 API 호출 함수와 쿼리 훅에서 API 호출 횟수와 쿼리 성공 여부를 판단하는 로그를 작성한다.
import { useQuery } from '@tanstack/react-query';
import api from '../../utils/api';
import { useEffect } from 'react';
let fetchCallCount = 0;
const fetchRecipes = async (searchQuery) => {
fetchCallCount += 1;
console.log(`API 호출 횟수: ${fetchCallCount}, 검색 쿼리:`, searchQuery);
const response = await api.get('/recipe', { params: searchQuery });
return {
recipes: response.data.data,
totalPages: response.data.totalPageNum,
};
};
export const useFetchRecipes = (searchQuery) => {
const queryResult = useQuery({
queryKey: ['recipes', searchQuery],
queryFn: () => fetchRecipes(searchQuery),
staleTime: 0,
gcTime: 0,
keepPreviousData: true,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const { status, data, error } = queryResult;
useEffect(() => {
if (status === "success") {
console.log('쿼리 성공: 캐시된 데이터 사용 여부 확인', data);
} else if (status === "error") {
console.log('쿼리 오류 발생:', error);
}
}, [status, data, error])
return queryResult;
};
그리고 리액트 쿼리 개발 도구(React Query Devtools)를 사용하면 브라우저 내에서 캐시된 데이터의 상태를 시각적으로 확인할 수 있다.
(쿼리 캐시 적용 전)
(...)
export const useFetchRecipes = (searchQuery) => {
const queryResult = useQuery({
queryKey: ['recipes', searchQuery],
queryFn: () => fetchRecipes(searchQuery),
staleTime: 30000,
gcTime: 60000,
keepPreviousData: true,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
(...)
};
(쿼리 캐시 적용 후)
1-3. 기본 상태에서 성능 측정결과
처음에는 애플리케이션에서 쿼리 요청을 통해 기본 상태에서 성능을 평가하고자 했지만, 캐시가 적용되지 않은 상태에서 측정하는 것이라면 간단하게 postman을 이용해서 연속된 api 요청을 해보고자 한다.
이를 위해 postman에 접속하여 확인하고자 하는 api 요청 작성하여 collection runner 를 이용해서 100번의 요청을 보낸다.
요청을 성공적으로 보낸후 CloudWatch > Metrics 에서 해당 요청에 대한 api 요청만 필터해서 결과를 확인해본다.
(로그에서 확인 하고 싶은 정보만 확인하고 싶으면 CloudWatch > 로그 그룹 > 로그 파일 > 지표 필터 편집에서 설정한다)
자신이 확인하고 로그가 기록되어 있는 로그 그룹에 들어가서 패턴 필터링을 통해 확인하고 싶은 로그를 선택하여 필터를 생성합니다.
그리고 CloudWatch > Dashboard > 애플리케이션 에서 지표 결과를 확인해보면 아래와 같은 결과가 나타난다.
CPU 사용량은 1.08%, Network 는 1.32MB 정도 사용된 걸 확인할 수 있었다.
근데 기본적인 CloudWatch 지표로만 성능을 평가하기에는 아쉬운 부분이 있는 것 같다. 그래서 CloudWatch 에이전트 설치하여 메모리 지표를 추가적으로 수집해보고자 한다.
[AWS] - AWS : Elastic Beanstalk에 Cloudwatch Agent 설치(Memory)
위 링크에 연결된 게시물의 과정을 통해 대시보드에 메모리 지표를 추가한다.
(추가적으로 디스크 사용량 대시보드를 추가하였습니다)
이제 서버 성능 측정할 준비가 된것 같습니다. 포스트맨을 통해 기본 상태에서 서버 성능을 측정해봅니다.
기본 상태 성능 측정 결과는 아래와 같습니다. 포스트맨은 여러가지로 api 요청 연습을 하다가 한도(?)가 다되서 결국 수동으로 200회 api 요청을 해서 네트워크 사용량 메모리 사용량 등을 측정하였습니다.
2단계 : 리액트 쿼리 캐싱 적용
이제 캐싱 설정을 통해 API 에 캐시를 적용 합니다.
또한 캐시 설정을 위해 코드 수정에 따라 깃에 코드를 올리고 다시 재배포하는 과정이 불편하기 때문에 .env 파일에 코드를 추가해서 캐시 활성화를 조정하고 네트리파이에 Environment variables 에 값을 추가해서 true / false 만 조정해서 배포한다.
1. .env 파일에 REACT_APP_ENABLE_CACHE 추가
REACT_APP_ENABLE_CACHE=true
2. API 요청 쿼리 수정
(...)
const isCacheEnabled = process.env.REACT_APP_ENABLE_CACHE === 'true';
console.log(isCacheEnabled)
export const useFetchRecipes = (searchQuery) => {
const queryResult = useQuery({
queryKey: ['recipes', searchQuery],
queryFn: () => fetchRecipes(searchQuery),
staleTime: isCacheEnabled ? 5 * 60 * 1000 : 0,
gcTime: isCacheEnabled ? 10 * 60 * 1000 : 0,
refetchOnWindowFocus: isCacheEnabled,
refetchOnReconnect: isCacheEnabled,
});
const { status, data, error } = queryResult;
useEffect(() => {
if (status === "success") {
console.log('쿼리 성공: 캐시된 데이터 사용 여부 확인', data);
} else if (status === "error") {
console.log('쿼리 오류 발생:', error);
}
}, [status, data, error])
return queryResult;
};
isCacheEnabled 에 따라 캐시 유/무 상태를 설정하도록 한다.
이제 isCacheEnabled 를 true 로 변경했으니, 네트리파이에서 그 값을 설정하도록 한다.
네트리파이 > 배포사이트 > site configuration > Environment variables 에서 환경 변수 값을 설정한다.
환경 변수 값을 수정했으면 다시 Deploys 에서 Trigger deploy 에서 Deploy site 버튼을 통해 재배포 합니다.
이제 환경 변수에 따라 캐시 설정이 제대로 적용됐는지 사이트에 들어가 확인해봅니다.
콘솔을 살펴보면 isCacheEnabled 이 true 잘 출력되며 캐시가 적용되어 API 호출이 올라가지 않고 있습니다.
3단계 : 캐싱 적용 후 성능 재측정
이제 기본 상태에서 api 요청을 보냈던 것과 마찬가지로 캐시가 적용된 상태에서 동일하게 api 요청을 보내봅니다.
4단계 : 데이터 비교 및 분석
캐시 적용 전/후 데이터를 비교하면 아래와 같습니다.
1. CPU 사용률
- 캐시 적용 전: CPU 사용률이 1.34%.
- 캐시 적용 후: CPU 사용률이 0.74%
2. 네트워크 사용량 (In/Out)
- 캐시 적용 전: 네트워크 In은 908KB, Out은 859KB
- 캐시 적용 후: 네트워크 In은 128KB, Out은 94.9KB
3. 메모리 사용률
- 캐시 적용 전: 메모리 사용률이 41.6%
- 캐시 적용 후: 메모리 사용률이 41.4%
4. 디스크 사용률
- 캐시 적용 전: 디스크 사용률이 10.5%
- 캐시 적용 후: 디스크 사용률이 9.04%
CPU 사용률은 44.78% 감소, 네트워크 인바운드는 85.90% 감소, 네트워크 아웃바운드는 88.95% 감소, 메모리 사용률은 0.48% 감소, 디스크 사용률은 13.90% 감소하였습니다.
캐시를 적용한 결과, CPU 사용률, 네트워크 트래픽, 메모리 사용률, 디스크 사용률 모두 감소했습니다.
이는 캐시가 서버의 전반적인 성능을 개선하고 리소스 사용을 최적화하는데 효과적인 것을 증명한 것 같습니다.
'프로젝트' 카테고리의 다른 글
프로젝트 : Next 프로젝트에 MongoDB 연결하기 (0) | 2024.10.17 |
---|---|
프로젝트 : Cannot destructure property 'name' of 'item' as it is undefined. (0) | 2024.08.15 |