최근에 고급 프로젝트를 진행하면서, 백엔드 응답 데이터를 내가 만든 컴포넌트에 맞춰서 변경해야하는 순간이 찾아왔다...!!
해당 순간이 찾아온 계기는.,,,.,,.
프로젝트의 홈 화면에서 사용자 리뷰어 랭킹을 보여주는 기능을 구현하던 중, 몇 가지 최적화 문제와 코드 중복 문제를 발견하게 되면서부터이다.
[ 문제 발생 배경 ]
이 문제는 다음과 같은 배경에서 발생했다.
1. 백엔드 응답 데이터 형식과 컴포넌트 데이터 형식의 불일치 :
- 현재 백엔드에서 전달되는 랭킹 데이터는 아래와 같이 특정한 형식으로 제공된다.
[
{
"id": 120,
"nickname": "kuromiiiiiiiiiiiiiiii",
"description": "보라색이 짱이에오",
"image": "https://example.com/profile8.png",
"createdAt": "2024-04-25T07:55:55.382Z",
"updatedAt": "2024-05-15T12:48:45.552Z",
"teamId": "4-19",
"followersCount": 7,
"reviewCount": 2
},
...
]
하지만, 이러한 데이터가 내가 만든 컴포넌트에 그대로 전달되기에는 적합하지 않았다.
내가 만든 랭킹카드 컴포넌트에는 랭킹을 나타내기 위해 각 리뷰어의 순위를 추가해야 했고, 또 랭킹카드 컴포넌트 안의 랭킹칩 컴포넌트에는 등수에 따라 다른 색상을 적용해야 했다.
2. 데이터 변환 로직의 중복 :
- 처음에는 백엔드에서 받은 데이터를 컴포넌트 내부에서 직접 변환하여 사용했다. (초반에는 `ReviewerRanking` 컴포넌트와 `RankingCard` 컴포넌트에서 각각 데이터를 변환하고 처리하는 로직이 존재했다.) 그러나 이는 각 컴포넌트마다 중복된 변환 로직을 포함하게 만들었고, 컴포넌트 간의 데이터 흐름을 알기 힘들어져 유지보수와 코드 가독성을 저하시키는 원인이 되었다. 때문에 랭킹칩의 색상을 등수에 따라 다르게 적용하는 과정에서 어려움을 겪었다.
3. 확장성과 유지보수성 문제 :
- 이렇게 데이터 변환 로직이 여러 컴포넌트에 흩어져 있어, 데이터 형식이 변경되거나 새로운 요구사항이 추가될 때마다 모든 관련 컴포넌트를 수정해야 했다. 이는 개발 효율성을 떨어뜨리고, 버그가 발생할 가능성을 높인다는 것을 느꼈다.
- 또한, 컴포넌트 내에 데이터 제한 로직이 존재하여,(요구사항 상으로는 리뷰어 랭킹은 전체 랭킹 데이터 중에서 상위 6개만 취급한다) 데이터의 크기나 형식이 변경될 때마다 이를 일일이 수정해야 한다는 리스크가 존재했다.
[ 해결 과정 ]
- 어댑터(Adapter) 패턴 도입!!
문제를 해결하기 위해 멘토님께도 여쭤보자 멘토님께서는 실무에서는 보통 이럴때 converting함수를 만들어서 백엔드의 응답 데이터를 컴포넌트에 맞춘다고 하셨다. 멘토님의 조언을 듣자 이전 파트3 멘토님으로부터 어댑터(Adapter) 패턴에 대해 배운 게 생각이 났고, 해당 내용으로 구글링을 해본 결과, 실제로 어댑터 패턴을 활용해서 백엔드의 응답 데이터를 컴포넌트에 맞게 조정할 수 있다는 것을 알았다. ( https://velog.io/@superlipbalm/how-i-use-adapter-pattern-in-reactjs )
(사실 어댑터 패턴은 학교 다닐때 드랍해버린 디자인 패턴 수업의 초반에서 배우기도 했었고,
지난 파트3 멘토님(ㄱㅎㅈ멘토님🥹)께서 다뤄주신 내용이기도 한데, 그때는...."에이 멀 복잡하게 생각해 그냥 백엔드 응답 데이터 오는 것대로 뿌려주면 되지 머~"하고 대수롭지 않게 넘겻엇는데 알고보니 이게 현업이랑 실무에서 자주 쓰이는 개념이었다..!!)
그래서 해당 글을 참고하여, 나 역시 데이터 변환 로직을 캡슐화하기 위해 어댑터 패턴을 도입하여 어댑터 함수를 구현했다. 이 어댑터 함수는 백엔드 응답 데이터를 나의 랭킹카드 컴포넌트에서 사용할 수 있는 형태로 변환하는 역할을 한다.
import { Ranking } from '@/components/Home/types/RankingType';
import { RankingColor } from '@/shared/ui/Chip/RankingChip';
export interface AdaptedRankingData {
id: number;
rank: number;
nickname: string;
image: string;
followersCount: number;
reviewCount: number;
color: RankingColor;
}
const getColorByRank = (rank: number): RankingColor => {
switch (rank) {
case 1:
return RankingColor.PINK;
case 2:
return RankingColor.GREEN;
default:
return RankingColor.GRAY;
}
};
//여기가 핵심코드
export const adaptRanking = (data: Ranking[]): AdaptedRankingData[] => {
return data?.slice(0, 6).map((item, index) => ({
id: item.id,
rank: index + 1,
nickname: item.nickname,
image: item.image,
followersCount: item.followersCount,
reviewCount: item.reviewCount,
color: getColorByRank(index + 1),
}));
};
그리고 해당 어댑터 함수에서 이미 순위를 계산하고 있었기 때문에, 리뷰어 랭킹 컴포넌트에서 index로 랭킹을 계산할 필요가 없었다. 또한, 데이터를 6개씩 자르는 로직 역시 리뷰어 랭킹 컴포넌트에서 어댑터 함수로 이동시켰다. 이는 컴포넌트의 역할을 단순화하고, 어댑터 함수의 목적을 더 분명히 하며, 데이터 제한 로직을 중앙 집중화하기 위함이다.
[ 결론 ]
어댑터 함수를 구현하는 것은 쉽지 않았지만, 결론적으로 어댑터 패턴을 적용해봄으로써 나는 다음과 같은 중요한 개선점을 이뤄냈다고 생각한다.
1. 코드 중복 제거
- 어댑터 함수를 도입하여 데이터 변환 로직을 중앙 집중화했다.
2. 컴포넌트의 역할 단순화
- 컴포넌트의 역할을 명확히 하여 유지보수를 용이하게 했다.
3. 효율적인 데이터 처리
- index로 등수(랭킹)을 계산하는 로직, 등수(랭킹)에 따라 랭킹칩의 색깔을 다르게 적용하는 로직, 데이터를 6개로 제한하는 로직을 어댑터 함수로 이동시켜 데이터를 처리하는 데에 효율성을 높였다.
이러한 개선을 통해 코드의 가독성과 유지보수성을 크게 향상시킬 수 있었다. 그래서 앞으로도 API를 연동할 때 백엔드 응답 데이터 형식이 내 컴포넌트 구조와 맞지 않는다면, 어댑터 패턴을 적극적으로 활용하여 유사한 문제를 해결하고, 코드의 일관성을 유지할 계획이다.💜
'개발냥발 > TS (Trouble Shooting)⚡' 카테고리의 다른 글
비제어 컴포넌트에서 검색어 상태 관리하기 (0) | 2024.06.18 |
---|