-
[Portfolly] KISS 원칙으로 코드를 바꿔보자Studying/Proj 과정 2023. 7. 27. 03:02
개요
Portfolly 프로젝트 중 재사용 컴포넌트라고 만든 코드가 조금 많이 복잡해져버리는 문제가 발생했습니다.
보다 단순한 KISS 원칙을 적용해 재사용 컴포넌트를 만드는 과정을 정리해보려고 합니다.
기존의 고집스러운 코드
MemberProfile.tsx 컴포넌트는 props에 따라 다양한 형태의 사용자 프로필을 반환하는 함수형 컴포넌트입니다.
type 이라는 props가 그 생김새를 결정합니다.
게시판에서 사용되는 프로필의 경우 type='board', 댓글에서 사용되는 프로필은 type='comment' 이런 식으로 전달합니다.
그래서 작성한 게 바로 다음 코드입니다.
interface MemberProfile { type: 'board' | 'comment' | 'portfolio' | 'blackboard'; member: Member; date?: string; } const ImageSizes: any = { board: 65, comment: 35, portfolio: 100, blackboard: 65, }; const MemberProfile = ({ type, member, date }: MemberProfile) => { const itemPic = member.imageUrl === null ? circleNoImg : member.imageUrl; return ( <MemberProfileWrapper gap={15}> <Image src={itemPic} url={`/members/${member.id}`} shape="circle" size={ImageSizes[type]} /> {type === 'portfolio' && <Label text={member.name} type={type} url={`/members/${member.id}`} />} {type !== 'portfolio' && ( <ColumnWrapper> <Label text={member.name} type={type} url={`/members/${member.id}`} /> <SmallText color={type === 'blackboard' ? 'white' : ''}>{date}</SmallText> </ColumnWrapper> )} </MemberProfileWrapper> ); };
코드가 좀 괴랄합니다. 코드 중복은 있어선 안 되는 것이라고 착각하고 절대적인 코드 수를 줄이는 데에만 집중한 결과였습니다. 마치 러시아 문학을 읽듯 눈을 찌푸리고 조건부를 한글자 한글자 자세히 들여다봐야 2%정도 이해가 되는 것 같습니다.
작성하는 당시에는 머리가 굴러가는 대로 이해가 됐지만, 지금에 와 다시 보니 제가 작성했는데도 다시 이해하려면 오래 걸릴 것 같습니다.
이는 당연하게도 다른 팀원들이 사용하기 불편했고, 재사용 하려다 사용 조차 불가능한 컴포넌트가 되어버렸습니다.
KISS 원칙을 적용한 코드
그때 멘토님께서 해결책으로 제시해준 것이 바로 KISS 원칙이었습니다.
interface MemberProfile { type: 'board' | 'comment' | 'portfolio' | 'blackboard'; member: Member; date?: string; } const ImageSizes: any = { board: 65, comment: 35, portfolio: 100, blackboard: 65, };
일단 인터페이스 및 디자인 토큰은 기존과 동일합니다.
KISS원칙을 따르는 코드는 다음과 같습니다.
interface IComponentFactory { [key: string]: JSX.Element; } const renderProfile = (type: string, name: string, id: number, date: string) => { const ComponentFactory:IComponentFactory = { portfolio: <Label text={name} type={type} url={`/members/${id}`} />, blackboard: ( <ColumnWrapper> <Label text={name} type={type} url={`/members/${id}`} /> <SmallText color='white'>{date}</SmallText> </ColumnWrapper> ), comment: ( <ColumnWrapper> <Label text={name} type={type} url={`/members/${id}`} /> <SmallText color='white'>{date}</SmallText> </ColumnWrapper> ), board: ( <ColumnWrapper> <Label text={name} type={type} url={`/members/${id}`} /> <SmallText color='white'>{date}</SmallText> </ColumnWrapper> ) } return ComponentFactory[type]; }
renderProfile 메서드를 만들어 다양한 프로필을 만드는 데 필요한 인자들 type, name, id, date 를 받고, 컴포넌트 팩토리 형식으로 type에 따라 제각기 다른 형태의 프로필을 반환합니다.
조건부 렌더링을 하지 않고, 그냥 타입 별로 다른 컴포넌트의 생김새를 전부 다 완전하게 정의하는 것입니다. 비슷한 생김새의 코드가 반복되니 무식해보이지만 사실 유지보수 하기에는 처음 작성한 코드보다 훨씬 간편한 코드가 완성됩니다.
더 다양한 형태의 MemberProfile이 필요한 경우 renderProfile에 추가해주면 되는 간편한 보수성까지 지닙니다.
만약 renderProfile 메서드가 너무 길어질 경우 helpers.tsx 같은 유틸 함수 전용 파일로 분리해서 사용하면 되겠습니다.
const MemberProfile = ({ type, member, date }: MemberProfile) => { const itemPic = member.imageUrl === null ? circleNoImg : member.imageUrl; return ( <MemberProfileWrapper gap={15}> <Image src={itemPic} url={`/members/${member.id}`} shape="circle" size={ImageSizes[type]} /> {renderProfile(type, member.name, member.id, date as string)} </MemberProfileWrapper> ); };
조건부 렌더링이 필요 없어지다보니 return문 안쪽이 단순해졌습니다.
깨달은 점
이번 경험을 통해 잘못된 습관을 고칠 수 있었습니다.
어디서 한 가지 방법이 좋더라! 라고 듣고나면 그 방식대로만 코드를 작성하기 일쑤였는데, 이번 기회에 다양한 디자인 패턴을 공부하면서 경우에 따라 직접 장단점을 분석하고 가장 도움되는 코드를 골라서 적용할 줄 알아야 겠다고 깨달았습니다.
'Studying > Proj 과정' 카테고리의 다른 글
[Portfolly] 프로젝트를 마치며 (0) 2023.07.27 [Portfolly] 코드 Depth를 최대한 줄여보자 (0) 2023.07.27 [Portfolly] Five Lines of Code 규칙을 (최대한) 지켜보자 (0) 2023.07.27 [Portfolly] 스프린트 3주차 회고 (0) 2023.07.23 [Portfolly] 스프린트 2주차 회고 (0) 2023.07.16