[TIL] Clean Code & 선언적 프로그래밍

박예슬·2022년 9월 4일
0

❗토스 SLASH 의 실무에서 바로 쓰는 Frontend Clean Code 동영상을 시청하고 정리한 내용입니다아ㅏㅇ~

실무에서 클린코드의 의의

클린코드가 의미있는 이유는 무엇일까?

흐름 파악이 어렵고, 도메인 맥락 표현이 안 되어 동료에게 물어봐야 알 수 있는 코드 는 개발할 때 병목이 되고,
유지보수할 때 시간이 오래 걸리며 심하면 기능 추가가 불가능한 상태가 된다.
또, 성능도 안 좋은 경우가 많아 유저 입장에서도 쾌적하지 못하다.

결국, 실무에서 클린 코드의 의의는 = 유지보수 시간의 단축이라고 볼 수 있다.

읽기 좋은 깔끔한 코드는 코드를 파악하는 시간, 코드리뷰의 시간, 버그가 났을 때 디버깅 시간을 단축시킨다.

처음엔 클린하다.

처음 코드를 설계하고, 새로운 파일에서 짤 땐 깨끗하다.
하지만, 기존 코드에 기능을 추가하는 상황이라면?
남이 짠 코드, 혹은 내가 지난주에 짠 코드에 기능을 붙이다보니, 신경을 쓰지 않으면 코드가 들쭉날쭉 더러워지게 된다!!!


코드를 안일하게 추가한다면..?

🔎 예시코드 : 처음의 코드 vs 기능을 추가한 나쁜 코드


1. 하나의 목적인 코드가 흩뿌려져 있다.
왼쪽 코드에서 파란색 바탕의 코드가 같은 기능을 목적으로 하는 코드다.
이들이 뚝뚝 떨어져 있어, 나중에 기능을 추가할 때 스크롤로 이동하며 미로찾기를 해야한다.

2. 하나의 함수가 여러가지 일을 하고 있다.
handleQuestionSubmit() 함수가 여러기능을 포함하고 있어,
세부 구현을 모두 읽어야 함수의 역할을 알 수 있게 된다.
코드 추가 및 삭제도 시간이 더 걸리게 된다.

3. 함수의 세부구현 단계가 제각각이다.
handleQuestionSubmit() 함수handleMyExpertQuestionSubmit() 함수는 둘 다 이벤트 핸들링 관련 함수다.
이름은 비슷하지만, 전자 함수는 질문전송 외에도 여러가지 일을 하고 있어서 읽을 때 어지럽다.
코드를 이상하게 지레짐작하게 된다.

👉 깔끔했던 코드가 작은 기능을 하나 추가했더니 어지러운 코드가 됐다!!

큰 그림을 보며 코드 리팩토링하기

1. 함수의 세부 구현 단계를 통일한다
기존 함수의 이름을 바꿔, handleNewExpertQuestionSubmit() 함수handleMyExpertQuestionSubmit() 함수 으로 위계를 맞췄다.
전자는 새로운 전문가에게 질문하는 로직만, 후자는 연결 중인 전문가에게 질문하는 로직만 넣는다.

2. 하나의 목적인 코드는 뭉쳐 두기
팝업 관련 코드를 하나로 뭉쳤다.
기존에는 팝업을 여는 버튼과 팝업 코드가 동떨어져 있었는데, 이를 모아 PopupTriggerButton 컴포넌트를 만들었다.
띄워줄 팝업은 props로 내려보낸다.

3. 함수가 한 가지 일만 하도록 쪼개기
함수 하나에서 하나의 일만 하도록 쪼갠다.
약관동의 관련 함수를 쪼개서 필요한 시점에 부르도록 바꿨다.

😱 코드가 길어진다..?

클린코드는 짧은코드가 아니다!
= 원하는 로직을 빠르게 찾을 수 있는 코드이다!!

원하는 로직을 빠르게 찾으려면?

  • 하나의 목적을 가진 코드가 흩뿌려져 있어요
    👉 응집도를 높여서 뭉쳐두어야 한다.
  • 함수가 여러 가지 일을 하고 있어요
    👉 단일책임 원칙에 의거하여 쪼개줘야 한다.
  • 함수의 세부구현 단계가 제각각이에요
    👉 추상화단계를 조정해서, 핵심개념을 필요한 만큼만 노출해야 한다.

1. 응집도 : 같은 목적의 코드는 뭉쳐 두자

코드가 흩어져 있다면, 파악도 한 번에 안 되고 버그 발생 위험도 높은 코드다.
👉 커스텀 훅을 사용해서 한 군데로 뭉쳐보자!

❗주의할 점
보기 더럽다고 일단 전~부 뭉쳐두면, 코드 파악이 어려워질 수 있다.
ex) 커스텀팝업 관련된 내용을 전부 뭉쳐서, openPopup 함수를 호출하면 커스텀 팝업을 열도록...
어떤 내용의 팝업을 띄우는지, 팝업에서 버튼을 눌렀을 때 어떤 액션을 하는지가 중요한 포인트인데, 이들이 모두 훅 속에 가려져서 알 수가 없게 된다.

💡무엇을 뭉쳐야 할까?
= 당장 몰라도 되는 디테일
이것들만 숨겨둔다면, 짧은 코드만 보고도 빠르게 코드의 목적을 파악하는 게 쉬워진다.
코드 파악에 필수적인 핵심 정보는 오히려 뭉치면 답답해진다. 여러 모듈을 넘나들며 흐름을 따라가야하는 참사가 생길 수 있다.

💡어떻게 읽기 좋게 응집할까?

먼저, 남겨야 할 핵심 데이터와 숨겨도 될 세부구현을 나눠본다.
위의 코드에서 핵심데이터는 팝업 버튼 클릭 시 수행하는 액션과, 팝업의 제목, 그리고 내용이다.
세부구현은 팝업을 열고 닫을 때 사용하는 상태와, 컴포넌트의 세부 마크업, 그리고 팝업의 버튼 클릭 시 특정 함수를 호출해야 한다는 바인딩!

여기서 핵심 데이터만 남기고, 세부 구현을 숨기면 파악하기 쉬운 코드가 된다.

openPopup 이라는 커스텀 훅 안에 모든 코드를 다 숨기는 것이 아니라,
세부구현만 숨겨놓고 핵심 데이터인 팝업 제목, 내용, 액션은 바깥에서 넘기는 것!
👉 세부구현을 읽지 않고도, 어떤 팝업인지 파악할 수 있다.

선언적 프로그래밍

핵심 데이터만 전달받고, 세부 구현은 뭉쳐 숨겨두는 개발 스타일
즉, "팝업, 너에게 선언한다! 핵심데이터는 이러이러하다!" 라고 선언하면,
팝업이 이미 해둔 세부구현을 바탕으로 해당 내용을 뿌려주는 스타일

// 예시
<Popup
	onSubmit={질문전송}
	onSuccess={홈으로이동}
/>
       
// 재사용 
<Popup
	onSubmit={회원가입}
	onSuccess={프로필로이동}
/>

👉 '무엇'을 하는 함수인지 빠르게 이해가 가능하다
👉 세부구현은 안쪽에 뭉쳐두어 신경 쓸 필요가 없다
👉 '무엇'만 바꿔서 쉽게 재사용 할 수 있다

선언형으로 뭉쳐두지 않고, '어떻게' 해야 할지 하나하나 명령(세부구현을 작성)하는 방식은 명령형 프로그래밍 이라고 한다.
선언적 프로그래밍도 내부를 까보면, 명령형으로 작성되어 있다.

세부 구현이 모두 노출되어 있어 이를 커스텀하기 쉽지만, 읽는 데 오래걸리고 재상용하기 어렵다는 단점이 있다.

선언적인 코드가 무조건 좋을까?
no!
두 방법 모두 유동적으로 사용하자
: 리액트의 JSX 문법을 사용하면, HTML에서도 선언형 프로그래밍을 할 수 있다는 장점이 있지만, prop으로 '어떻게 해야 하는지'를 세부적으로 넘겨야 하는 경우에는 명령형 설계도 필요하다.

2. 단일책임 : 하나의 일을 하는 뚜렷한 이름의 함수를 만들자

  • 중요 포인트(기능)가 모두 담겨 있지 않은 함수명은 위험!
    : 읽는 이가 예상한대로 코드가 동작하지 않으며, 이는 코드에 대한 신뢰하락으로 이어진다.
    👉 한 가지 일만 하는, 명확한 이름의 함수

👉 한 가지 일만 하는, 기능성 컴포넌트 : 리액트 컴포넌트로 기능 분리

ex) 요소 두개가 겹쳐 있는지 판별하는 IntersectionObserver 코드

👉 조건이 많아져, 이름짓기가 불편하다면 한글 이름도 유용하다.

3. 추상화 : 핵심 개념을 뽑아내자

컴포넌트로 추상화

제출액션과, 성공액션이라는 중요한 개념만 남기고 나머지를 추상화함

함수로 추상화

전문가 정보를 받아와, 응답 값에따라 다른 라벨을 보여주는 코드에서, 세부구현을 함수안에 모두 추상화시킨 코드

얼마나 추상화할 것인가?

// leve 0. 버튼을 클릭하면 컴펌 창을 띄우고, 여기서 컨펌 버튼을 클릭하면 특정 메시지를 띄우는 구체적인 코드
<Button onClick={showConfirm}>
  	전송
</Button>
{isShowConfirm && (
  	<Confirm onClick={() => {showMessage("성공")}}/>
)}

// level 1. 버튼 눌렀을 때 컨펌창을 띄우는 기능을 컨펌버튼이라는 컴포넌트로 추상화
// onConfirm을 통해 내가 원하는 액션을 넘길 수 있음
<ConfirmButton
  onConfirm={() => {showMessage("성공")}}
>
	전송
</ConfirmButton>

// level 2. message라는 prop만 넘겨서 컨펌창에 원하는 메시지를 보여주도록 간단하게 추상화
<ConfirmButton message="성공">
  	전송
</ConfirmButton>

// level 3. 모든 기능을 추상화
<ConfirmButton />
  

👉 상황에 따라 필요한 만큼 추상화하자!

❗ 주의사항
추상화 수준이 섞여 있으면 코드 파악이 어려울 수 있다
한 레벨으 코드 안에 추상화 수준이 섞여 있으면, 코드파악이 어려워진다.
구체적으로 작성된 노란색 컴포넌트를 보고 초록색 컴포넌트도 구체적으로 작성되어있다고 짐작할 수 있다. (작은 코드라 생각했는데 복잡한 코드가 나오게 됨)
전체적인 코드가 어느 수준으로 구체적으로 기술된지 파악할 수 없게 된다

추상화 단계를 비슷하게 정리해주면, 물 흐르듯이 파악할 수 있다.

클린코드를 적용해보자

  1. 담대하게 기존 코드 수정하기
    구조 뜯기를 두려워하지 말자!
    (Pull Request에 File changes를 줄이고 싶다면, Mother branch를 따서 리팩토링한 PR을 추가로 만드는 방법을 사용하자!)

  2. 큰 그림 보는 연습하기
    그 때는 맞고 지금은 틀릴 수 있다!

  3. 팀과 함께 공감대 형성하기
    명시적으로 이야기하는 시간이 필요하다!

  4. 문서로 적어보기
    클린 코드란 모호한 개념이니까, 글로 적어야 명확해질 수 있다.
    '이 코드가 어떤 점에서 향후 위험할 수 있는지, 어떻게 개선할 수 있는지', 나만의 원칙을 적어보자!


오늘부터 TIL 을 다시 작성하려고 한다!
부트캠프를 할 때, 내용이 부실해도 하루일기처럼 열심히 적었는데
끝나고 나서는 공부하는데 의의가 있다는 마음가짐으로 그냥 개인 노트에 공부만했었었다.
근데, 뭔가 TIL을 쓰지 않으니까 점점 마음가짐도 헤이해지는 느낌적인 느낌...?
하루 공부한 것 중 impact 있는 것을 TIL로 하거나, 하루 공부내용을 간단하게라도 추려보자!

profile
공부중인 개발자

0개의 댓글