오늘은 아침부터 종일 개발면접 공부를 하면서 그동안 배워왔던 지식들을 깊게 이해하는 부분들도 생기고
그땐 아무리 이해하려고 해도 이해하기 어려웠던 부분들을 예전보다 쉽게 이해하는 것 같은 부분들도 생겨
하루종일 모니터를 봐서 토끼눈이 됏지만 마음만은 뿌듯했던 것 같다
특히 뿌듯하게 느껴졌던건, 드디어 점점 '급급한 코드'가 아니라 한번 더 생각하게 되는 코드를 짜보려고 하게 됐다는거
공부를 하면서도 틈틈이 리팩토링을 적용해볼만한 부분들을 찾고 있는데, 오늘은 내가 짰던 함수 중에 리액트퀼에서 onchange이벤트가 발생할때마다 함수가 리랜더링이 일어나는 부분들을 발견했고 공부한 '시간복잡도'를 낮출 수 있는 부분을 찾아냈다
보면 이렇게 onchange가 발생할때마다 add함수가 랜더가 되고 있었다.
리액트퀼의 모듈을 설정해줄때, 했던 방식처럼 useMemo와 useCallback이 떠올랐고 둘 중 어떤게 적합할지를 생각해봤다.
useMemo는 특정 '값'이 바뀔 때 함수를 실행하고, 값이 바뀌지 않는 경우엔 이전에 계산된 값을 재사용하는데
한마디로 useMemo는 동일한 입력값에 대해 항상 동일한 결과값을 반환하는 경우에 useMemo를 사용하는게 적합한데, module같은 경우에는 무언가 계산을 해서 그때마다 바뀌는 값을 반환하는 함수도 아니라 적합했었다.
but, 지금 최적화하고 싶은 addImgTag 함수에는 매번 결과값이 다른 Date.now() 값이 있었다. => useMemo는 적합하지 않다고 판단
useCallback은?
useCallback은 '함수'를 기억해두었다가, 이전에 생성한 함수를 재사용하는 것인데 의존성 배열안의 넘들이 변경되었을때만 함수를 새로 그리도록 만드는 것이다.
오, 너로 정해따 ㅇ_<
유즈콜백을 사용해서 콜백함수 안에 addImgTag 함수를 넣었고, 의존성 배열에 addImgTag 함수가 참조하고 있는 quillRef, setImgLoading, storage를 추가해줬다.
이 변수들이 변경될때 함수가 다시 생성되도록 설정을 시켜 최적화를 진행하였다.
[기존 코드]
const addImgTag = async () => {
let randomID = Date.now();
const imgRef = ref(storage, `photoTitle${randomID}`);
const imgDataUrl = localStorage.getItem("imgDataUrl");
let downloadUrl;
if (imgDataUrl) {
setImgLoading("loading");
const response = await uploadString(imgRef, imgDataUrl, "data_url");
setImgLoading("completed");
downloadUrl = await getDownloadURL(response.ref);
const range: any = quillRef.current.getEditorSelection();
quillRef.current
.getEditor()
.insertEmbed(range.index, "image", downloadUrl);
quillRef.current.getEditor().setSelection(range.index + 1);
(document.body.querySelector(":scope > input") as HTMLElement).remove();
}
};
[ 리팩토링한 코드 ]
const addImgTag = useCallback(async () => {
let randomID = Date.now();
const imgRef = ref(storage, `photoTitle${randomID}`);
const imgDataUrl = localStorage.getItem("imgDataUrl");
let downloadUrl;
if (imgDataUrl) {
setImgLoading("loading");
const response = await uploadString(imgRef, imgDataUrl, "data_url");
setImgLoading("completed");
downloadUrl = await getDownloadURL(response.ref);
const range: any = quillRef.current.getEditorSelection();
quillRef.current.getEditor().insertEmbed(range.index, "image", downloadUrl);
quillRef.current.getEditor().setSelection(range.index + 1);
(document.body.querySelector(":scope > input") as HTMLElement).remove();
}
}, [quillRef, setImgLoading, storage]);
두번째로 리팩토링한 부분
[처음 개선했던 코드] : useCallback을 사용하면서 콜백함수 안에 넣게 되는데 아래와 같이 코드를 짤 경우 코드 안에 코드가 또 들어간 중첩 함수가 되어버렸다. => 정상적으로 작동하지 않았음
const imageHandler = useCallback(() => {
() => {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
document.body.appendChild(input);
input.click();
input.onchange = async () => {
const inputImage: any = input.files;
const uploadImage = inputImage[0];
if (inputImage !== null) {
const reader = new FileReader();
reader?.readAsDataURL(uploadImage);
reader.onloadend = (finishedEvent: any) => {
const imgDataUrl: any = finishedEvent.currentTarget.result;
localStorage.setItem("imgDataUrl", imgDataUrl);
addImgTag();
};
}
};
};
}, [addImgTag]);
작동이 안되서 무엇인지를 계속 찾으면서 바꾸다가 발견한게 useCallback을 적용할때 최상위 콜백 함수만 메모이제이션을 시키려고 하는데, 처음 useCallback으로 감싸주었던 저 코드는 두 개의 중첩 콜백 함수가 되었던 것
이런식으로 작성을 하면, 중첩 함수는 useCallback의 의도와 맞지 않아 의도랑 다른 결과를 볼 수 있다는 내용을 찾았다
그리하야 useCallback안에서 익명함수로 실행시켯던 base64를 파베의 사진 Url로 바꾸는 고 로직을 함수로 따로 선언해서 아래에서 호출하는 식으로 변경을 해주었다.
[마지막 개선 코드]
const imageHandler = useCallback(() => {
// 원래는 이 함수를 따로 만들지 않고, 바로 input.onchange에다가 붙였는데
// 이렇게 함수를 따로 선언해서 아래서 호출하는 식으로 변경
const handleInputChange = async (event) => {
const inputImage = event.target.files;
const uploadImage = inputImage[0];
if (inputImage !== null) {
const reader = new FileReader();
reader?.readAsDataURL(uploadImage);
reader.onloadend = (finishedEvent) => {
const imgDataUrl = finishedEvent.currentTarget.result;
localStorage.setItem("imgDataUrl", imgDataUrl);
addImgTag();
};
}
};
// input 태그를 만들어서 onChange이벤트 발생시 handleInputChange가 발생하도록 셋팅해줌
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.onchange = handleInputChange;
document.body.appendChild(input);
input.click();
}, [addImgTag]);
'📖 나의 개발일지 (WIL&TIL)' 카테고리의 다른 글
[TIL] 오늘의 개발일지 (0) | 2023.03.30 |
---|---|
[TIL] 오늘의 개발일지 (0) | 2023.03.29 |
[TIL] 오늘의 개발일지 (0) | 2023.03.27 |
[TIL] 오늘의 개발일지 (0) | 2023.03.26 |
[TIL] 오늘의 개발일지 (0) | 2023.03.25 |
댓글