요즘 지금까지 해왔던 프로젝트들에 대한 최적화에 대한 리팩토링을 계속해서 고민하고 고치려고 노력중이다.
아직 리팩토링하는 방법, 해야하는 기준에 대해 공부가 잘 안되어있기 때문에 프로젝트의 코드들을 쭉보면서 반복되는 코드들에 대한 리팩토링부터 시작하기로 했다.
1. Board 컴포넌트의 엄청난 반복코드
처음에 이 프로젝트를 구현할 때 왜이렇게 했는지 지금생각해보면 이해가 가지는 않지만, 어쨌든 내가 친 코드니까,, 페이지 좌측에 backgroundnoise를 주는 보드가 있다. ReactAudioPlayer, Slider mui라이브러리를 사용하여 slider를 이용하여 볼륨을 조절하고 자동재생, 반복, 소스(src) 등등을 속성으로 하여 총 11개의 noise를 아래의 사진처럼 하나씩 다 주고 앉아있었던 것... 반복된 코드들부터 리팩토링 해보자~ 하고 열었던 파일에서 이러한 상황이 벌어져있는걸 보고, 나는 옳다구나 하길 잘했다! 라고 생각하여 리팩토링을 시작했다.
가장 먼저 든 생각이 BackgroundSound라는 컴포넌트를 하나 만들어서 11개의 sound에 각각 이 컴포넌트를 사용해야겠다. 라는 생각이 들었다.
// BackgroundSound.tsx
import { Slider } from "@mui/material";
import ReactAudioPlayer from "react-audio-player";
interface propsType {
src: string;
volume: number;
onChange: (value: number) => void;
}
const BackgroundSound = ({ src, volume, onChange }: propsType) => {
return (
<>
<ReactAudioPlayer
preload="auto"
autoPlay
src={src}
loop
volume={volume}
/>
<Slider
className="slider"
value={volume * 100}
onChange={(_e, value) => onChange((value as number) / 100)}
/>
</>
);
};
export default BackgroundSound;
위처럼 컴포넌트를 만들어서 sound마다 각각 다른 src, volume, onChange함수를 props로 받아 재사용하여 리팩토링을 하여 무려 코드를 195줄이나 줄일 수 있었다.
2. CountDownTimer 컴포넌트 반복코드
1번의 문제점과 흡사하다. 타이머의 시간을 설정해주기위한 input태그의 반복이 문제였다. 해당 input태그와 속성들을 함수로 하나 만들어서 리턴해주는식으로 진행하였다. 먼저 hour, minute, second 각각 상태를 구분해서 useState를 3개나 썻던것들을 time이라는 상태로 묶어서 구조분해할당을 이용해 hour, minute, second를 꺼내 쓸 수 있게 해주었다. 그다음 hour이냐 minute냐 second냐에 따라서 매개변수로 전달받아서 재사용할 수 있는 renderInput이라는 함수로 만들어주었다.
// renderInput 함수
const renderInput = (
label: string,
key: keyof TimeStateType,
max: number
) => {
return (
<>
<input
className="number-input"
type="number"
value={time[key]}
onChange={(e) => setInputValue(key, parseInt(e.target.value, 10))}
max={max}
min={0}
/>
<span>{label}</span>
</>
);
};
// 함수 사용코드
{renderInput("hour", "hour", 24)}
{renderInput("min", "minute", 60)}
{renderInput("sec", "second", 60)}
위처럼 매개변수로 시, 분, 초 를 받아 그에따른 input을 반환해주는 기능을 하는 함수를 하나 만들어서 재사용을 해주었습니다.
3. 타이머가 리렌더링 될때마다 todo의 리렌더링
타이머의 초가 갈때마다 리렌더링이 되는것이 부모 컴포넌트가 같은 투두리스트 컴포넌트의 리렌더링으로도 이어지는것을 확인할 수 있었다. 타이머의 리렌더링과 투두리스트 컴포넌트의 리렌더링은 전혀 상관없는 관계였기 때문에 투두리스트 컴포넌트의 리렌더링이 매우 비효율적이라고 생각하여 리팩토링을 진행하였습니다. 투두리스트 컴포넌트에는 필요한 props도 존재하지 않았기 때문에 React.memo를 사용하여 컴포넌트 자체를 메모이제이션을 하여 투두리스트 컴포넌트를 리렌더링이 되지 않게 리팩토링하였습니다.
// TodoList 컴포넌트
const TodoList = React.memo(function TodoList() {
// 컴포넌트 내용
});
export default TodoList;
위처럼 React.memo로 감싸주어 todoList컴포넌트 내부에서의 state의 변화에만 리렌더링이 되고, 타이머의 리렌더링에는 더이상 리렌더링이 발생하지 않았습니다.
리팩토링 전 lighthouse의 성능결과가 80에서 99로 많이 끌어올려졌습니다. 크기가 큰 프로젝트는 아니지만 이렇게 작은 프로젝트부터 성능을 고려해보고, 차근차근 리팩토링해보는 연습도 꾸준히 해야되겠다고 느꼈습니다.