SLASH21 실무에서 바로 쓰는 Clean Code - TOSS 진유림님 강연 정리노트

Spes Lim·2021년 5월 1일
2

회사 개발 업무 온보딩을 진행하면서 앞으로 기능 추가를 해야 할 때가 있으면 프론트엔드의 코드를 알고 있어야 하고, 사내 프로젝트 코드를 좀 더 유지보수가 쉽도록 설계 하고자 유림님의 클린 코드 강연을 듣게 되었다. 기술적인 성장과 함께 후에 나와 함께 협업하게 될 분을 위해서도 이런 작업은 매우 중요하기도 하고, 미래의 걱정이 될 수 있을 요소들을 해결하지 않으면 편하게 두발 뻗고 잠을 잘 수 없는 나의 성향 때문에도 그렇다.

회사에서 '클린 코드'의 의의란?

"그 코드는 안 건드리는게 좋으실 거예요. 일단 제가 만질게요.^^;;;"

모든 회사의 프로젝트마다 존재하는 이런 지뢰 코드의 특징.

  • 흐름 파악이 어렵고
  • 도메인 맥락 표현이 안되어 있고
  • 동료에게 물어봐야 알 수 있는 코드

솔루션 : 유지보수 시간의 단축 (코드 파악/디버깅/코드리뷰) 을 해줄 수 있는 코드 → 클린 코드!

하.지.만 잘 만들어진 기존 코드에, 새로운 기능을 끼얹는 상황이라면?

긴장의 끈을 놓치면 코드가 들쑥 날쑥해짐을 경험할 수 있다.

  • 안일한 코드 추가의 함정

    • 기능 추가 요청이 새롭게 들어왔다.

    • 예시) 보험에 대한 질문을 입력하는 페이지가 있는데, 내 설계사가 있는 경우 그 설계사 사진이 들어간 팝업을 먼저 띄워달라는 기능 요청 이였다.

    • 아래의 기존 코드를 보자.

      function QuestionPage() {
          async function handleQuestionSubmit() {
              const 약관동의 = await 약관동의_받아오기();
              if (!약관동의){
                  await 약관동의_팝업열기();
              }
              await 질문전송(questionValue);
              alert("질문이 등록되었어요.");
          }
          return (
              <main>
                  <form>
                      <textarea 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("질문이 등록되었어요.");
        }
    		// 팝업 버튼 클릭 핸들러
        async function handleMyExpertQuestionSubmit() {
            await 연결전문가_질문전송(questionValue, 연결전문가.id);
            alert('${연결전문가.name}에게 질문이 등록되었어요.');
        }
        return (
            <main>
                <form>
                    <textarea placeholder="어떤 내용이 궁금한가요?"/>
                    <Button onClick={handleQuestionSubmit}>질문하기</Button>
                </form> 
    						// 팝업 버튼 컴포넌트
                {popupOpened && (
                    <연결전문가팝업 onSubmit={handleMyExpertQuestionSubmit}/>
                )}
            </main>
        )
    
    }

위의 코드를 하나하나 생각대로 설계했지만, 결과적으로 좋지 못한 코드가 되었다. 문제점은 무엇일까?

  • 하나의 목적인 코드가 여기저기 흩뿌려져 있기 때문에 그렇다.
  • 여기서 추가한 코드는 모두 '연결중인 전문가 팝업 코드' 라는 점을 기억해야 한다.
  • 나중에 새로운 코드를 또 다시 추가해야 할 경우, 스크롤을 위 아래로 이동하면서 찾아야 하는 번거로움이 발생한다.
  • 유지보수 용이한 코드 왜 작성하는가? 위에서 언급했듯, 시간 == 자원 이기 때문이다.
  • 기존에 있던 함수가 이제 3가지 태스크를 하게 되는 것도 문제점에 포함이 된다.
    • 세부 구현을 모두 읽어야 함수의 역할을 알 수 있다.
    • 코드 추가와 삭제도 시간이 더 걸리게 되어 있다.
    • 함수의 세부구현 단계가 제각각이라는 문제를 포함한다.
      • handleQuestionSubmit() , handleMyExpertQuestionSubmit() 두 함수는 모두 이벤트 핸들링 함수 이다.
      • handleQuestionSubmit() 함수는 질문 전송 외에도 여러가지 일을 수행 하는 함수이다.
      • 코드의 기능을 지레짐작하게 되는 문제가 발생한다.

깔끔했던 코드가, 작은 기능의 추가로 어지러운 코드가 된다.

PR(Pull Request)의 상황만 보면, 어지러운 코드인지 파악하기 어렵다. 변경점 자체는 문제가 없기 때문이다.

위의 코드의 문제점을 바탕으로, 코드를 다시 리팩토링 해보자.

  • 함수의 세부 구현 단계를 통일한다.
  • 기존 함수의 이름을 handleNewExpertQuestionSubmit() , handleMyExpertQuestionSubmit() 으로 위계를 맞췄다.
  • 각각의 함수는 새로운 전문가에게 질문하는 기능 / 연결중인 전문가에게 질문하는 기능만 수행되도록 리팩토링 했다.
  • 팝업 관련 코드를 하나로 뭉쳤다.
  • 기존에는 팝업을 여는 버튼과 팝업 코드가 떨어져 있었다면, 이를 한데 모아서 새로운 하나의 컴포넌트에 기능을 수행하도록 리팩토링을 했다.
  • 함수 1개 당, 1 task 유지
  • 약관동의 관련 함수를 쪼개서 필요시점에 콜백을 받도록 바꾸었다.

클린 코드 ≠ 짧은 코드

== 원하는 로직을 빠르게 찾을 수 있는 코드

  • 하나의 목적을 가진 코드가 흩뿌려져 있다. → 응집도 높이기
  • 함수가 여러가지 태스크를 수행하고 있다. → 단일 책임 원칙 준수
  • 함수의 세부구현 단계가 제각각이다. → 추상화 단계를 조정해서 핵심 개념 추출하기

무엇을 뭉쳐야 할 까? (Do!)

당장 몰라도 되는 디테일은 숨겨두기만 해도 코드의 목적을 빠르게 파악하는 작업이 용이해진다.

조심해야 할 점 (Don't!)

코드 파악에 필수적인 핵심 정보들을 분리하면 이를 파악하기 위해 모듈을 넘나드는 대참사가 발생한다.

최신 트렌드가, 모든 것을 설명해 주는 것은 아니다.

리액트의 JSX에서 핫하게 쓰이는 선언형 프로그래밍, 그러나 때에 따라서는 명령형 프로그래밍도 유동적으로 사용할 수 있어야 가독성이 더 좋아질 수 있다. 코드의 목적을 빠르게 파악할 수 있기 때문에 그렇다.

단일 책임 원칙을 준수하자.

중요 포인트가 모두 담겨 있지 않는 함수명 → 위험
이런 경우, 나중에 새롭게 다른 기능을 추가할 때 원하는 동작을 이끌어내기 어렵다. 또, 리팩토링할 때 들어가는 수고로움은 배가 될 것이다.

  • 코드에 대한 신뢰 하락을 동반
  • 그 다음부터는 함수명을 믿지 못하게 된다면? 세부 구현을 모두 의심하게 되는 상황을 이끌어낼 수 있다.

조건이 많아지면 한글 이름도 유용하다 (캬... 갓액트...^^)

  • 도메인이 복잡해서 영어 네이밍을 짓는게 오히려 복잡도를 높일 때
  • 상수를 직관적으로 보고 싶을 때
  • 복잡한 조건문이 많아질 때 매우 유용하다.
  • 덤으로, 주석을 달아둔 것 같은 효과를 낸다.^^

마지막으로, 추상화 수준 → 섞여 있으면 코드 파악이 어려워진다.

추상화 단계를 비슷하게 정리해서 추상화 수준이 같은 것 끼리 모으는 작업을 해 두자.

20분의 짧은 강연을 들으면서 많은 것을 배웠다. 그 중 가장 와닿았던 점들은 위의 열거한 원칙이 모두 지켜진다는 가정하에서, 기능을 설계할 때 개발자의 의견이 상대적으로 존중받기 쉬워지는 개발 환경이 된다는 점 이다.

profile
Software Developer

0개의 댓글