최근 코딩 테스트를 많이 풀면서 기본적인 자료구조의 내장 메서드에 대한 이해도가 엄청 많이 올라갔다.
이 높아진 이해 덕분에 회사 코드를 리팩토링 하는데 엄청 많은 도움을 받을 수 있었다.
그래서 이를 리팩토링에 어떻게 잘 활용했는지 한번 기술해보려 한다.
아래는 회사 코드를 변형하여 로직만 가져온 코드다
const forwards = players.filter((player) => player.role === 'forward');
const midfielders = players.filter((player) => player.role === 'midfielder');
const defenders = players.filter((player) => player.role === 'defender');
const goalkeepers = players.filter((player) => player.role === 'goalkeeper');
위의 코드처럼 같은 배열을 4번이나 반복하여 filter하는 것이 불편하게 느껴졌다.
한번만 반복문을 돌려서도 충분히 만들 수 있을 것 같았기 때문이다.
그러나 이 코드를 작성할 때는 마땅히 방법을 찾지 못하여 그냥 넘어갔었다.
하지만 이번에 다시 코드를 봤을 때는 reduce로 해결할 수 있겠다는 생각이 바로 들었다.
최근 코딩테스트를 자주 풀면서 reduce 메서드에 대한 이해가 깊어졌는데
기존에는 arr.reduce((a,b)=>a+b) 이렇게 밖에 사용할 줄 몰랐으나
이제 reduce의 acc, cur 그리고 초깃값의 개념에 대해서 완벽하게 이해를 하게 되었다.
그래서 초깃값을 잘 지정해두고 reduce 콜백 내부에서 switch 문으로 분기처리를 하면 될 것 같았다.
const { forwards, midfielders, defenders, goalkeepers } = players.reduce(
(acc, player) => {
switch (player.role) {
case 'forward':
acc.forwards.push(player);
break;
case 'midfielder':
acc.midfielders.push(player);
break;
case 'defender':
acc.defenders.push(player);
break;
case 'goalkeeper':
acc.goalkeepers.push(player);
break;
default:
acc.all_rounders.push(player);
break;
}
return acc;
},
{
forwards: [],
midfielders: [],
defenders: [],
goalkeepers: [],
} as Record<string, Player[]>,
);
이렇게 초깃값을 key는 각 포지션으로 value는 빈 배열로 설정해두고
players 배열에 반복문을 돌면서 각 case에 맞는 배열에 push해주고 수정된 acc를 리턴했다.
기존 코드와 비교했을 때 같은 배열을 4번이나 반복하던 것을 단 한번으로 줄일 수 있었다.
코드가 길어졌다는 단점이 있으나 로직만 다른 파일로 분리하면 될 것 같아서 매우 만족스러웠다.
위 코드를 리팩토링 한 뒤에 같은 파일에서 또 다른 문제를 발견했다.
<h3>forwards</h3>
<ul className="list">
{forwards.map((player) => <PlayerCard key={player.key} />))}
</ul>
<h3>midfielders</h3>
<ul className="list">
{midfielders.map((player) => <PlayerCard key={player.key} />))}
</ul>
<h3>defenders</h3>
<ul className="list">
{defenders.map((player) => <PlayerCard key={player.key} />))}
</ul>
<h3>goalkeepers</h3>
<ul className="list">
{goalkeepers.map((player) => <PlayerCard key={player.key} />))}
</ul>
기존에도 나름 map반복문을 활용해서 코드를 작성해두긴 했지만 이것 역시 하나로 묶을 수 있을 것 같았다.
그러기 위해 일단 위에서 리팩토링 했던 코드를 수정했다.
const { forwards, midfielders, defenders, goalkeepers } = players.reduce(...생략)
이렇게 reduce의 반환 결과인 객체를 바로 구조분해할당으로 받아오는 구조에서
const playerPositionGroups = players.reduce(...생략)
이렇게 구조분해하지 않고 객체를 그대로 넘겨받도록 수정했다.
코딩테스트를 많이 풀면서 시간복잡도를 줄이기 위해 해시맵을 자주 사용하다보니
객체도 반복문을 돌릴 수 있다는 것을 알게 되었기 때문이다.
그래서 객체 메서드인 Object.entries를 활용해서 반복을 제거해보았다.
{Object.entries(playerPositionGroups).map(([group, list]) => (
<>
<h3>{group}</h3>
<ul className="list">
{list.map((player) => <PlayerCard key={player.key} />)}
</ul>
</>
))}
기존에 12줄로 불필요하게 반복된 코드를 6줄로 깔끔하게 줄여서 매우 편안해졌다.
더 좋은 점은 이제 변경이 필요할 때 단 하나의 PlayerCard만 수정하면 된다는 점이다.
정말 훨씬 좋아졌다! 이번 리팩토링은 가독성에 문제가 있었다기보단
코드 성능, 기술적인 문제를 개선할 수 있었다.
이번 경험을 통해서 왜 자료구조 및 JS 기본기가 중요한지 새삼 느낄 수 있었고
조금이라도 좀 더 좋은 코드를 작성하는 개발자가 된 것 같아서 너무 재미있었다.
정리가 잘 된 글이네요. 도움이 됐습니다.