배포 전 로컬에서 혼자 사용하고 있는데 비동기 로직 처리가 너무 느리다...
스크랩 속도가 느린 것도 있지만 로직이 불필요하게 중첩되어 있다고 생각했다.
그래서 로직을 분리했고 결과적으론 렌더링까지 4~5초 정도 걸리던 로직이 약 3초 정도로 줄었다.
리팩토링 전에 슥슥 적었던 부분인데 요약하면
접속 후 스크래핑 ~ json 저장 까지 비동기 로직이 함수 하나에서 돌아가고 있었고,
한 곳의 업체(데이터)당 스크래핑 > 필터링 > json 저장을 3번 반복 했던 것이다.
고민하던 중 솔루션#1이 떠올라서 진행하였다.
아래는 기존의 클라이언트 코드이다
async function initializeMenuAndRender() {
RESTAURANT_DATA.forEach(({ name, ig_name }) => createRestaurantElement(name, ig_name));
startLoading();
if (isNotLatest() && isUpdateTime() && isWeekday()) {
const data = RESTAURANT_DATA.filter(({ ig_name }) => lunchData[ig_name].lastUpdate !== getCurrentDate());
for (const { ig_name, ig_owner_id, filter } of data) {
await updateMenuData(ig_name, ig_owner_id, filter);
}
const updatedData = await getUpdatedData();
renderMenuImages(updatedData);
} else {
renderMenuImages(lunchData);
}
endLoading();
}
async function updateMenuData(ig_name, ig_owner_id, filterFunc) {
try {
const feedData = await useFetch(GET_FEED_DATA_URL, 'GET', { ig_name, ig_owner_id });
const { url, id } = filterFunc(feedData);
await useFetch(SET_MENU_DATA_URL, 'POST', { ig_name, url, id, lastUpdate: getCurrentDate() });
} catch (error) {
handleClientError(error, 'updateMenuData Function');
}
}
문제는 저 updateMenuData() 함수였는데, 피드 데이터를 가져온 후에 가져온 데이터를 필터링해서 필요한 정보를 뽑아서 local static json 내용을 업데이트 할 수 있도록 서버로 보내 fs-module을 사용해 json을 수정한다.
왜 이렇게 했나 싶다..
아래는 개선한 코드이다.
async function initializeMenuAndRender() {
try {
const notUpdatedData = RESTAURANT_DATA.filter(({ ig_name }) => lunchData[ig_name].lastUpdate !== getCurrentDate());
const isNotLatest = notUpdatedData.length > 0;
RESTAURANT_DATA.forEach(({ name, ig_name }) => createRestaurantElement(name, ig_name));
startLoading();
if (isNotLatest && isUpdateTime() && isWeekday()) {
const data = notUpdatedData.map(({ ig_name, ig_owner_id, filter }) => updateMenuData(ig_name, ig_owner_id, filter));
const menuData = await Promise.all(data);
await useFetch(SET_MENU_DATA_URL, 'POST', menuData);
const updatedData = await getUpdatedData();
renderMenuImages(updatedData);
} else {
renderMenuImages(lunchData);
}
endLoading();
} catch (error) {
handleClientError(error, 'initializeMenuAndRender Function');
}
}
async function updateMenuData(ig_name, ig_owner_id, filterFunc) {
try {
const feedData = await useFetch(GET_FEED_DATA_URL, 'GET', { ig_name, ig_owner_id });
const { url, id } = filterFunc(feedData);
return { ig_name, url, id, lastUpdate: getCurrentDate() };
} catch (error) {
handleClientError(error, `updateMenuData Function for ${ig_name}`);
throw error;
}
}
로직을 분리하면서 다른 것도 조금씩 수정했다.
아래는 기존의 서버 코드이다
app.get('/getFeedData', async (req, res) => {
try {
const { ig_owner_id } = req.headers;
const scrapeData = await getScrapeData(`https://www.instagram.com/graphql/query/?query_hash=e769aa130647d2354c40ea6a439bfc08&variables={"id":"${ig_owner_id}", "first":12 }`);
if (scrapeData.status !== 'ok') {
throw new Error(scrapeData.message);
}
res.json(scrapeData);
} catch (error) {
handleServerError(res, error, '피드 데이터를 가져오는 중 오류가 발생했습니다.');
}
});
app.post('/setMenuData', async (req, res) => {
try {
const { ig_name, url, id, lastUpdate } = req.body;
const data = await readFile(LUNCH_MENU_DATA, 'utf8');
const jsonData = JSON.parse(data);
const isNotSameId = jsonData[ig_name].id !== String(id);
const isNotSameLastUpdate = jsonData[ig_name].lastUpdate !== lastUpdate;
// id 혹은 lastUpdate가 다를 경우에만 json 파일 내용 수정
if (isNotSameId || isNotSameLastUpdate) {
// id가 다를 경우에만 lastUpdate 수정
jsonData[ig_name].lastUpdate = isNotSameId ? lastUpdate : jsonData[ig_name].lastUpdate;
jsonData[ig_name].id = id;
jsonData[ig_name].url = url;
jsonData[ig_name].base64 = await getImageBase64(url);
await writeFile(LUNCH_MENU_DATA, JSON.stringify(jsonData, null, 4), 'utf8');
console.log('JSON 파일 내용이 성공적으로 수정되었습니다.');
} else {
console.log('JSON 파일은 이미 최신 상태입니다.');
}
res.sendStatus(200);
} catch (error) {
handleServerError(res, error, 'JSON 데이터를 수정하는 중 오류가 발생했습니다.');
}
});
위 두 로직이 함수 1회당 한번씩 실행이 됐었다..
이전 포스팅 중에서 설명했던 적이 있으니 설명은 생략하겠다.
app.get('/getFeedData', async (req, res) => {
try {
const { ig_owner_id } = req.headers;
const scrapeData = await getScrapeData(`https://www.instagram.com/graphql/query/?query_hash=e769aa130647d2354c40ea6a439bfc08&variables={"id":"${ig_owner_id}", "first":12 }`);
if (scrapeData.status !== 'ok') {
throw new Error(scrapeData.message);
}
res.json(scrapeData);
} catch (error) {
handleServerError(res, error, '피드 데이터를 가져오는 중 오류가 발생했습니다.');
}
});
app.post('/setMenuData', async (req, res) => {
try {
const data = await readFile(LUNCH_MENU_DATA, 'utf8');
const jsonData = JSON.parse(data);
for (const { ig_name, url, id, lastUpdate } of req.body) {
const isNotSameId = jsonData[ig_name].id !== String(id);
if (isNotSameId) {
jsonData[ig_name].id = id;
jsonData[ig_name].url = url;
jsonData[ig_name].lastUpdate = lastUpdate;
jsonData[ig_name].base64 = await getImageBase64(url);
}
}
await writeFile(LUNCH_MENU_DATA, JSON.stringify(jsonData, null, 4), 'utf8');
res.sendStatus(200);
console.log('JSON 업데이트 완료.');
} catch (error) {
handleServerError(res, error, 'JSON 데이터를 수정하는 중 오류가 발생했습니다.');
}
});
/getFeedData는 그대로이고 /setMenuData 해당 로직이 수정되었다.
별거 아닌 수정이지만 실제로 데이터를 가져와서 렌더링하는 속도가 소폭 상승하였고,
데이터를 가져오고 렌더링 전까지 클라이언트에 스켈레톤UI를 추가하여 체감상 더욱 더 빨라진 느낌이 든다.
더 수정할 부분도 있고, 에러 처리도 개선해야 하지만 우선 이 정도면 배포하여 회사 사람들한테 보여줄 수 있을 거 같다.
다음 포스팅은 nas에 서버 세팅하는 포스팅이 될지, 후기로 완을 할지는 모르겠다.