코딩하는 고릴라

[FE] 데코레이터로 API 캐싱 로직 분리하기 본문

Project/Pennypal

[FE] 데코레이터로 API 캐싱 로직 분리하기

코릴라입니다 2024. 7. 24. 00:23
반응형

기존에 Map 객체를 활용해 API 응답 데이터 캐싱 로직을 구현했었습니다.

const cacheData = API_CACCHE_DATA.get('http://localhost:8080/api/team');

if (!cacheData || cacheData.exp < new Date().getTime()){
	axios.get('http://localhost:8080/api/team')
    .then((res) => {
    	// 캐시 데이터 설정 {data, exp}
    	API_CACHE_DATA.set('http://localhost:8080/api/team', {
        	data: res.data,
            exp: new Date().getTime + 1000 * 60 * 10,
        }) 
        // 응답 데이터 활용
        console.log(res.data);
    }.catch((err) => console.log(err));
} else {
	// 캐시 데이터 활용
	console.log(cacheData.data);
}

해당 로직을 처음 구현했을 당시에는 썩 괜찮은 코드라고 생각했었습니다.
하지만 위 코드는 api 호출에 대한 응답값의 캐싱이 필요한 부분이 여러 군데라면 위 코드 전체를 복사해 api 호출 함수만 변경시켜 사용해야 합니다. 이는 캐싱 관련 로직에 변경이 발생했을 때, 가져다 쓴 모든 곳에서 로직을 변경해줘야 하는 불편함을 초래할 수 있습니다.

- 전체 캐싱 로직 변경 수요가 발생하면, 로직1, 로직2, 로직3 내의 캐싱 로직을 모두 일일이 수정해줘야 한다.
 
자바스크립트에 대해 계속해서 학습한 결과, 데코레이터를 적용시켜 로직의 분리와 보다 범용성 있는 형태로 리팩토링 할 수 있음을 알게 되었습니다.

// 데이터를 불러 올 fetch 함수를 인수로 받는 래퍼 함수
function cacheWrapper(func) {
	const cacheMap = new Map(); // 캐시 데이터를 저장할 map

	return async function (url) { // 래퍼 함수를 호출했을 때 반환할 중첩 함수
		let cacheData = cacheMap.get(url); // 캐시 데이터

		if (cacheData && cacheData.exp > new Date().getTime()) { // 캐시 데이터가 존재하며, 만료기간이 지나지 않았다면 캐시 데이터 반환
			return cacheData;
		}

		// 캐시에 데이터가 없거나 만료기간이 지났다면 인수로 받은 fetch함수인 func를 호출, 반환값을 캐시에 저장 후 반환
		let res = await func(url);
		cacheMap.set(url, { data: res.data, exp: new Date().getTime() + 1000 * 60 * 10 });
		return res.data;
	}
}

////////

// 외부에서 위 래퍼 함수를 활용해 사용하는 방식

// 1. cacheWrapper 함수에 인수로 fetch 함수를 넣어 전달하면, cacheWrapper의 렉시컬 환경이 생성되고 내부의 중첩 함수가 반환된다.
let cachedFetch = cacheWrapper(fetchFunction);

let result = cachedFatch('http://localhost:8080/api/team'); // cacheWrapper 함수 내 중첩함수에 인수로 url을 전달해 함수를 호출한다.
// 래퍼 함수 내부의 로직에 따라, 캐시 데이터를 체크한 후 알맞은 값을 반환시킨다.

// 다른 함수에 대해 위 캐싱 로직 활용의 필요성이 있다면, 래퍼 함수에 인수로 전달할 함수만 변경하여 호출해주면 된다.
let cachedFetch2 = cacheWrapper(fetchFunction2);
let result2 = cachedFetch('other_url');

 
캐싱을 활용할 컴포넌트에서 let cachedFetch = cacheWrapper(fetchFunction)를 한 번 호출하면, 캐싱을 위한 Map 객체를 활용할 수 있게 된다. 그리고 그 결과로 url을 인수로 받아 api 요청을 진행하는 내부 중첩 함수를 반환받는다.
그 후 cachedFetch(url)을 호출하면, 해당 url에 대한 캐시 체크 로직을 거친 후 캐시값이나 api 요청 결괏값을 반환해 준다.
 
이는 여러 컴포넌트에서 서로 다른 함수에 대한 캐시 작업이 필요할 때, cachedWrapper(func)에서 인수로 전달해 줄 함수들만 달리하여 호출하면 손쉽게 캐싱 로직을 활용할 수 있다. 이를 통해 api 호출 결과 데이터를 불러오는 본 함수의 로직과 캐싱 로직을 분리할 수 있으며, 캐싱 로직 수정 시 위 래퍼함수를 가져다 쓴 모든 곳에서 수정사항이 반영되는 범용성도 확보할 수 있게 된다.
 
자바스크립트에 대해 학습하면 할수록, 기존 프로젝트에서의 개선점이 점점 눈에 보이는 것 같다.

반응형