실무에서 클린 코드의 의의 = 유지보수 시간의 단축 (코드 파악, 디버깅, 리뷰)
function QuestionPage(){
async function handleQuestionSubmit(){
const 약관동의 = await 약관동의_받아오기();
if(!약관동의){
await 약관동의_팝업열기();
}
await 질문전송(questionValue);
alert("질문이 등록되었어요.")
}
return (
<main>
<form>
<textrea placeholder="어떤 내용이 궁금한가요?"/>
<Button onClick={handleQuestionSubmit}>질문하기</Button>
</form>
</main>
)
}
function QuestionPage(){
const [popupOpened, setPopupOpened] = useState(false);
async function handleQuestionSubmit(){
const 연결전문가 = await 연결전문가_받아오기();
if(연결전문가 !== null){
setPopupOpened(true)
} else {
const 약관동의 = await 약관동의_받아오기();
if(!약관동의) {
await 약관동의_팝업열기();
}
await 질문전송(questionValue);
alert("질문이 등록되었어요")
}
}
하나의 목적인 코드가 흩뿌려져 있습니다.
하나의 함수가 여러 가지 일을 하고 있습니다.
함수의 세부 구현 단계가 제각각입니다.
큰 그림을 보며 코드 리팩토링
function QuestionPage(){
const 연결전문가 = useFetch(연결전문가_받아오기);
async function handleNewExpertQuestionSubmit(){
await 질문전송(questionValue);
alert("질문이 등록되었어요");
}
async function handleMyExpertQuestionSubmit(){
await 연결전문가_질문전송(questionValue, 연결저문가.id);
alert(`${연결전문가.name}에게 질문이 등록되었어요.`)
}
}
return (
<main>
<form>
<textrea placeholder="어떤 내용이 궁금한가요?"/>
{연결전문가.connected ? (
<PopupTriggerButton
popup={(
<연결전문가팝업
onButtonSubmit={handleMyExpertQuestionSubmit}
)}> 질문하기</PopupTriggerButton>
): ()}
</form>
</main>
)
function QuestionPage(){
const [popupOpened, setPopupOpened] = useState(false);
async function handleClick() {
setPopupOpened(true);
}
function handlePopupSubmit(){
await 질문전송(연결전문가.id);
alert("질문을 전송했습니다.")
}
return (
<>
<button onClick={handleClick}>
<Popup title="보험 질문하기" open={popupOpened}>
<div>전문가가 설명드려요.</div>
<button onClick={handlePopupSubmit}>확인</button>
</Popup>
</button>
</>
)
}
function QuestionPage(){
const [openPopup] = useMyExportPupup(연결전문가.id);
function handleClick(){
openPopup();
}
return <button onClick={handleClick}>질문하기</button>
}
openPopup 함수만 호출하면 popup 창을 열 수 있지만 오히려 코드 파악은 어려워 졌습니다.
어떤 내용의 팝업을 띄우는지, 팝업에서 버튼을 눌렀을 때 어떤 액션을 하는지 모두 hook 속에 가려져서 파악할 수 없게 되었습니다.
(대표적인 커스텀 훅의 안티 패턴)
무엇을 뭉쳐야 하는가?
뭉치면 쾌적 : 당장 몰라도 되는 디테일
뭉치면 답답 : 코드 파악에 필수적인 핵심 정보
클린 코드 != 짧은 코드 (중요하다!)
클린 코드란 찾고 싶은 로직을 빠르게 찾을 수 있는 코드다.
function QuestionPage(){
const [openPopup] = usePopup();
async function handleClick(){
const confirmed = await openPopup({
title: "보험 질문하기",
contents: <div>전문가가 설명드려요</div>
})
if(confirmed){
await submitQuestion();
}
}
async function submitQuestion(연결전문가){
await 질문전송(연결전문가.id);
alert("질문을 전송했습니다.")
}
return <button onClick={handleClick}>질문하기</button>
}
<Popup
onSubmit={회원가입}
onSuccess={프로필이동}
/>
<Popup>
<button onClick={async () => {
const res = await 회원가입();
if (res.success){
프로필이름();
}
}}>전송</button>
async function handle[질문제출?](){
const 약간동의 = await 약간동의_받아오기();
if(!약간동의) {
await 약간동의_팝업열기();
}
await 질문전송(questionValue);
alert("질문이 등록되었어요.");
}
async function handle연결전문가질문제출(){
await 연결전문가_질문전송(questionValue);
alert(`${연결전문가.name}`에게 질문이 등로되었어요.)
}
async function handle새전문가질문제출(){
await 질문전송(questionValue);
alert("질문이 등록되었어요.");
}
async function handle약간동의팝업(){
const 약관동의 = await 약간동의_받아오기();
if(!약관동의){
await 약관동의_팝업하기();
}
}
<button onClick={async () => {
log("제출 버튼 클릭");
await openConfirm();
}}
<LogClick message="제출 버튼 클릭">
<button onClick={openConfirm}>
</button>
</ LogClick>
const targetRef = useRef(null);
useEffect(() => {
const observer = new IntercectionObserver(
({ isIntersecting }) => {
if (isIntersecting) {
fetchCats(nextPage);
}
}
)
return () => {
observer.unobserve(targetRef.current)
}
}, [])
returb <div ref={targetRef}>더보기</div>
<IntersectionArea onImpression={() => fetchData(nextPage)}>
<div>더 보기</div>
</IntersectionArea>
<div style={팝업스타일}>
<button onClick={async () => {
const res = await 회원가입();
if (res.success) {
프로필이동();
}
}}>전송</button>
</div>
<Popup>
onSubmit={회우너가입}
onSuccess={프로필이동}
/>
const planner = await fetchPlanner(plannerId)
const label = planner.new ? '새로운 상담사' : '연결중인 상담사'
const label = await getPlannerLabel(plannerId)
<button onClick={showConfirm}>
전송
</button>
{isShowConfirm && (
<Confirm onClick={() => {showMessage("성공")}}
)}
<CofirmButton onConfirm={() => {showMessage("성공")}}> 전송 </CofirmButton>
<CofirmButton message="성공">전송 </CofirmButton>
<CofirmButton />
실무 예시: 추상화할 수 있을까요?
리뷰어: 혹시 이 부분을 통째로 추상화할 수 없을까요? await moreAccurateLocation.request()처럼요.
꼭 모달을 연다고 하는 부분을 부모 컴포넌트가 알아야 하나 싶었습니다.
코드 작성자: moreAccurateLocation.check()라는 이름은 어떠신가요?
리뷰어: UltraCallProgress 와 UltraCallComparison이 비슷해 보이는데, 공통 부븐을 추상화할 수는 없을까요?
코드 작성자: 이 부분은 아직까지 코드 중복이 심하지 않아. 추후 유연성을 생각했을 때 다르게 변할 수 있는 부분이 있을 것 같아 일부러 추상화를 깨기 했었는데요.
(성급하게 추상화하다 보면, 유지보수에 어려움을 겪었던 적이 많았습니다) 조금만 더 고민해보겠습니다.
추상화 수준이 섞여 있으면 코드 파악이 어려워요.
<Title>별점을 매겨주세요</Title> // 높은 추상화
<div> // 낮은 추상화
{STARS.map(() => <Star />)}
</div>
<Reviews /> // 높은 추상화
{rating !== 0 && ( // 중간 추상화
<>
<Argreement />
<Button rating={rating} />
</>
)}
<Title>별점을 매겨주세요</Title>
<Stars />
<Reviews />
<ArgreementButton show={rating !== 0}/>