프로젝트 기능 개발이 끝나 리팩토링을 진행해보려합니다.
처음에는 무엇부터 시작을 해야하나.. 막막했지만 코드를 한참 들여다보니 할 수 있는 부분들이 보입니다.
리팩토링을 하기에 앞서, 목적이 먼저 있어야할텐데요.
저는 최적화와 가독성 및 유지보수에 두었습니다.
제 코드들을 위 기준에 맞춰 리팩토링하는 과정을 이 시리즈에 담아보려합니다.
우선, 이 글에서는 가독성과 유지보수
에 초점을 맞춰보려합니다.
코드를 바라보다가, 아래와 같은 생각이 들었어요.
간단하게 이루어진, 하나의 태그로 있는 UI를 컴포넌트로 관리하지 않고 그냥 태그로 둬도 괜찮을까?
코드와 화면을 구경해보시죠.
"use client"
// 퀴즈 상세 컴포넌트
const QuizDetails = ({
quizData
}:{quizData:QuizItem}) => {
....
return (
<>
{/*퀴즈 제목*/}
<h1 className={"lg:text-title1 md:text-title2Bold sm:text-title2Bold"}>{quizData.metaTitle}</h1>
{/*퀴즈 질문*/}
<p
className={"text-menu"}
>{quizData.title}</p>
{/*퀴즈 내용*/}
<div
className={"prose w-full"}
dangerouslySetInnerHTML={{__html: quizData.content}}
></div>
....
</>
);
};
export default QuizDetails;
일부분의 코드는 생략했어요. 제가 말하고 싶은 부분에 대한 코드만 적어봤습니다.
QuizDetails
컴포넌트는 ...으로 생략한 부분까지 100줄 가량의 코드들이 있는 퀴즈 상세 UI 컴포넌트입니다.
위 코드를 확인해보면 h1
태그가 나타내는 UI는 퀴즈 제목
, p
태그가 나타내는 UI는 퀴즈 질문,.div
태그에 dangerouslySetInnerHTML
을 통해 퀴즈 내용을 나타내고 있습니다.
물론 주석도 잘 달아놨죠.
초기에 개발할 때, UI가 복잡하지 않으니 단일 태그로로 충분할거야
라는 생각에 위처럼 UI를 작성하였어요.
하지만 이대로 충분한가?
라는 의문이 들었어요.
의문의 이유는 다음과 같았어요.
주석을 달았을지언정, 해당 UI를 보았을 때 어떤 UI인지 단번에 파악하는 것이 더 좋지 않을까?
tailwind className으로 인해 코드가 길어져서 가독성이 좋지 않다.
추후에 퀴즈 제목,질문,내용에 대한 className을 수정하거나 태그를 수정할 때, QuizDetails 컴포넌트를 건드려야한다.
QuizDetails 컴포넌트는 많은 역할들을 하고 있다.
주로 가독성관점에서 이유를 바라본 것 같습니다.
QuizDetails 컴포넌트 100줄 가량 된다고 했었죠. 코드를 오랫동안 보다보면 알겠지만, 코드를 파악하는 것이 누적되면 꽤나 피곤합니다.
때문에 보자마자 바로 파악가능하게 하는 것이 개발 피로 누적을 방지할 수 있는 생각이 들었어요.
만약 위 단일 태그로 감싸진 UI들을 컴포넌트로 만들면
컴포넌트명으로 어떤 역할을 하는지 단번에 파악할 수 있다고 생각했고
className은 굳이 눈으로 볼 필요가 없습니다.
위 UI들을 수정할 때, QuizDetails 컴포넌트도 건드릴 필요가 없죠.
또한 SOLID 원칙의 단일 책임 원칙을 완전히 지키진 못하겠지만 QuizDetails 컴포넌트의 역할을 줄일 수 있게 해주죠.
그래서 위와 같은 이유로 간단한 태그로 감싸진 UI지만 컴포넌트로 분리하려고 합니다.
그렇다면 위 UI들에 대해서 컴포넌트를 만들면 컴포넌트로 데이터를 전달해야할텐데요.
이를 props로 전달할지, children으로 전달할지 고민이 되었어요.
변경후 코드
import React from 'react';
// 퀴즈 제목
function QuizTitle({title}:{title:string}) {
return (
<h1 className={"lg:text-title1 md:text-title2Bold sm:text-title2Bold"}>{title}</h1>
);
}
export default QuizTitle;
<QuizTitle>{quizData.metaTitle}</QuizTitle>
props와 children을 사용했을 때, 어떤 장단점들이 있는지 생각해볼게요.
장점
단점
장점
단점
정리해본 장단점의 따라, 위 경우에는 단순한 데이터를 보여주는 것이기 때문에 props 방식을 사용해보려합니다.
추후를 생각해봐도 위 UI들을 중첩되지는 않을 것 같다는 판단이 들기도 하여서 props로 사용해도 괜찮다고 생각했습니다.
import React from 'react';
// 퀴즈 제목
function QuizTitle({title}:{title:string}) {
return (
<h1 className={"lg:text-title1 md:text-title2Bold sm:text-title2Bold"}>{title}</h1>
);
}
export default QuizTitle;
...
<QuizTitle
title ={quizData.metaTitle}
/>
그래서 변경된 코드는 다음과 같아요.
변경전 코드
"use client"
// 퀴즈 상세 컴포넌트
const QuizDetails = ({
quizData
}:{quizData:QuizItem}) => {
....
return (
<>
{/*퀴즈 제목*/}
<h1 className={"lg:text-title1 md:text-title2Bold sm:text-title2Bold"}>{quizData.metaTitle}</h1>
{/*퀴즈 질문*/}
<p
className={"text-menu"}
>{quizData.title}</p>
{/*퀴즈 내용*/}
<div
className={"prose w-full"}
dangerouslySetInnerHTML={{__html: quizData.content}}
></div>
....
</>
);
};
export default QuizDetails;
변경후 코드
"use client"
// 퀴즈 상세 컴포넌트
const QuizDetails = ({
quizData
}:{quizData:QuizItem}) => {
....
return (
<>
{/*퀴즈 제목*/}
<QuizTitle
title={quizData.metaTitle}
/>
{/*퀴즈 문제*/}
<QuizQuestion
question={quizData.title}
/>
{/*퀴즈 내용*/}
<QuizContent
content={quizData.content}
/>
....
</>
);
};
export default QuizDetails;
변경하니 제가 보기에는 훨씬 깔끔하고 가독성도 더 좋아졌네요. 주석은 있어야하지만 없어도 파악가능할 것 같아요.
위와 같이 처음 리팩토링해본 부분은
측면에서 리팩토링을 해보았어요.
가독성측면에서는 className을 볼 필요없도록 하고 컴포넌트명을 적절히 지어 단번에 파악가능하게 하고
유지보수측면에서는 추후 위 UI들을 수정할 때 관련 컴포넌트만 수정하면 된다는 관점에서 유지보수성이 늘었다고 생각할 수 있습니다.
이렇게 첫번째 리팩토링을 간단한 내용과 작업으로 마무리해봅니다.