이해하기 쉬운 코드작성 방법 정리

JS·2023년 2월 4일
1

Tech Explanation

목록 보기
11/14
post-thumbnail

왜 코드는 이해하기 쉬워야 할까?

우리는 코드를 작성하는 시간도 있지만 코드를 보고만 있는 시간이 많다. 이 흘러가는 시간을 줄이기 위해 코드를 이해하는데 소비되는 시간을 줄여야한다

코드를 완전히 이해한다는 의미

코드를 이해했다는 것의 기준

  • 코드를 자유롭게 수정이 가능하다
  • 버그를 짚어내는 것이 가능하다
  • 수정된 내용이 작성한 다른 부분의 코드와 어떻게 상호작용하는지 알 수 있다

이해하기 쉬운 코드를 작성하기 위한 6가지 테크닉

네이밍

변수명·함수명·클래스명의 이름을 결정할 때는 항상 같은 원리가 적용된다

이름은 일종의 설명문이다. 적절한 이름을 붙일 수 있다는 것은 해당 요소가 바르게 이해되고 설계되었다는 뜻인데, 어울리지 않는 이름을 붙여졌다는 것은 해당 요소가 달성해야 할 역할에 대해 프로그래머 자신조차 이해하지 못했다는 뜻이다

효과적인 네이밍 테크닉

한번에 파악할 수 있는 효과적인 이름을 작성하기 위해서는 다음과 같은 기준이 있다

  • 이름은 효과와 목적을 설명하도록 한다
  • 이름은 발음 가능한 것으로 한다
  • 이름은 검색 가능하도록 한다

이러한 기준을 가지고 네이밍을 잘했는지 확인하려면 이름 가역성이라는 개념을 사용한다. 이름이란 명명의 기반이 된 내용의 설명문을 복원할 수 있어야 한다는 명명 방침인데, 이 방침을 충족하였는지 확인하기 위해서는 루프백 확인을 수행해야 한다

루프백 확인은 이름을 추측할 수 있는 설명문을 생각해보는 것이다. 설명 > 이름 > 설명의 순으로 한바퀴 돌아서 원래로 돌아왔을 때 설명이 일치하면 좋은 이름이다

이름에 정보를 담아내는 법

보편적인 단어를 피하고 구체적인 단어를 선택한다

어디에서 가져오는지, 무엇을 수행하는지, 무엇을 반환하는지에 따라 구체적인 단어를 선택한다

getPage() => fetchPage()
size() => height()

이러한 구체적인 네이밍으로 정확한 의도를 전달할 수 있다

시간의 양이나 바이트의 수 같이 측정치를 포함한다면 단위를 포함시킨다

위험한 요소 표현

잘못 이해할 여지가 있는 이름을 사용하지 않는다

암호화되지 않은 패스워드라면 : password => plainTextPassword
URL Encoded 데이터라면 : data => dataURLEnc

이름은 얼마나 길어야 할까?

좁은 범위에서는 짧은 이름을 사용한다. 좁은 범위에서만 사용되는 변수의 이름에 많은 정보를 담을 필요가 없다

단순하게 약어와 축약형을 사용하는 것은 특정 프로젝트에 국한된 의미를 가진다. 때문에 좋은 생각이 아니며 아무것도 모르고 새로 합류한 사람이 이름을 의미하는 바를 한번에 이해할 수 있을까로 기준으로 생각하는 것이 좋다

이름 포맷팅으로 의미 전달

문법적 차이가 드러나게 서로 다른 개체의 이름에 각자 다른 포맷팅 방식을 적용하는 방식은 코드를 더 읽기 쉽게 해준다

C#의 경우 코딩 컨벤션으로 외부에서 사용할 수 있는 의미로 작성할때 Pascal Case, 일반적인 변수등을 선언할 때 쓰는 Camel Case 등이 있다

코드의 미학

미학적으로 보기 좋은 코드가 사용하기 더 편리하다

일관성과 간결성을 위해서 줄바꿈 재정렬

불규칙적으로 정의된 로직은 메소드를 활용하여 정렬


선언문을 블록으로 구성

추상화 수준 정리하기

코드를 정리할 때는 높은 수준의 추상화 개념과 낮은 수준의 추상화 개념을 분리하도록 한다. 상하가 아니라 기능의 복잡도에 따라 여러 계층으로 분리한다

조건문에서 인수의 순서

인수의 왼쪽은 질문을 받는 표현을 정의하고, 오른쪽에는 비교대상ㅇ르 두는 것이 통계적으로 가독성이 더 좋다

if/else 를 사용할 때는 부정이 아닌 긍정부터

중첩의 최소화

거대한 구문 나누기

개별적인 표현은 그렇게 크지 않지만 한 곳으로 모으면 읽기 힘들어진다
동일한 부분을 요약 변수로 추출하여 함수의 앞부분에 놓아둘 수 있다

드모르간의 법칙 사용하기

작은 것이 아름답다

작은 소프트웨어는 단순하고 다루기 쉬우며 큰 소프트웨어보다 훨씬 뛰어나다

작은 소프트웨어의 장점은 다음과 같다

이해가 쉽다
수정이 쉽다
다른 소프트웨어와 조합하기 쉽다

큰 소프트웨어의 문제점은 다음과 같다

복잡하고 코드를 이해하기 어렵다
예측하지 못하고 사태에 대응하기 어렵다

설명 변수

커다란 표현을 쪼개는 가장 쉬운 방법은 작은 하위 표현을 담을 추가 변수를 만드는 것이다. 하위 표현의 의미를 설명하므로 설명 변수라고도 한다

상관없는 하위문제 추출하기

큰 흐름과 관계가 적은 하위문제를 적극적으로 발견하여 추출하라

코드 블록을 보고 스스로에게 질문하라. 상위수준에서 본 이 코드의 목적은 무엇인가?
코드의 모든 줄에 질문을 던져라. 이 코드는 직접적으로 목적을 위해 존재하는가? 혹은 목적을 위해 필요하긴 하지만 목적 자체와 직접적으로 상관없는 하위문제를 해결하는가?
만약 원래의 목적과 직접적으로 관련되지 않은 하위문제를 해결하는 코드 분량이 많으면 이를 추출하여 함수로 만든다

다음 코드의 상위수준 목적은 주어진 점과 가장 가까운 길을 찾는 것이다

루프 내부의 코드는 대부분 주요 목적과 상관없는 하위문제를 다룬다

이제 원래 코드를 변환시킬 수 있다

코드를 읽는 사람도 밀도 높은 기하 공식에 방해받지 않고 상위수준의 목적에 집중할 수 있게 되어 전반적으로 코드의 가독성이 높아진다

기존의 인터페이스를 단순화하기

자신이 사용하는 인터페이스가 깔끔하지 않다면 깔끔한 덮개(Wrapper)로 보완이 가능하다

자바스크립트가 브라우저 쿠키를 다루는 방식은 깔끔하지 못하다. 개념적으로 쿠키는 이름/값 의 두 짝으로 이루어져 있다

필요한 쿠키를 찾으려면 이 거대한 문자열의 집합에서 구문분석을 직접 수행하여야 한다
하지만 max_result라는 이름을 지닌 쿠키의 값을 읽는 코드이다

이러한 지저분한 코드를 다음과 같이 사용할 수 있는 getCookie()함수를 만들어 깔끔하게 만들 수 있다

테스트 코드

테스트 코드가 읽기 쉬워야 한다는 점은 테스트와 상관없는 실제 코드와 마찬가지로 중요하다. 어떻게 동작하며 어떻게 사용되어야 하는지 비공식적인 문서와 같다

일반적인 설계원리에 따르면 덜 중요한 세부 사항은 사용자가 볼 필요 없게 숨겨서 더 중요한 내용이 눈에 잘 띄게 해야한다

명세 기반 테스트 기법 종류

테스트 기법을 정확히 알고 있어야 원하는 테스트와 테스트 코드의 목적을 명확히 할 수 있다

1. 동등분할
테스트 대상 데이터의 구간을 일정 간격으로 분할하여 케이스를 구성한다
보통 데이터의 구간에 중간값을 대표값으로 사용한디

2. 경계값 분석
분기 또는 반복 구문의 경계 값을 기준으로 케이스를 구성한다

3. 결정 테이블
조건(입력 값)과 행위(결과 값)를 테이블로 구성한다
프로세스 수행 중 요구되는 결정 또는 조건과 프로세스와 관련된 모든 동작을 기술한다

4. 조합
테스트하는 데 필요한 값이 다른 매개변수 값과 최소한 한 번씩은 조합을 하여 케이스를 구성한다
대부분의 결함이 두 개 요소의 상호작용에 기인한다는 것에 착안하여, 요소들의 모든 조합을 다룬다

5. 상태전이
시스템의 각 상태를 중심으로 케이스를 도출하고 전이 상태를 절차로 구성한다
객체의 상태를 구분하고 이벤트에 의해 어느 한 사앹에서 다른 상태로 전이되는 경우의 수를 테스트 케이스로 구성한다. 상태 머신을 사용하는 UI 컴포넌트 테스트시 유용하다

함수형 프로그래밍

코드를 완전히 이해했다는 것의 조건 중 코드를 사이드 이펙트 없이 자유롭게 수정할 수 있다라는 조건이 있었다
이렇게 해결하기 위해선 함수형 프로그래밍 기법을 사용하면 된다

프로젝트를 운영하면서 기능이 추가되면 코드가 복잡해지지 않게 해야한다. 복잡성을 다스리는 비결은 코드베이스의 크기가 커질수록 복잡성이 증가하지 않도록 붙잡아 두는 것이다

바로 여기서 함수형 프로그래밍이 핵심적인 기법이된다

함수형 프로그래밍이란?

함수형 프로그래밍은 추상화 단위를 함수로 한다

함수들을 조립하여 고수준의 동작을 구현한다. 자료구조를 새로 만들어 어떤 요건을 충족시키는 게 아니라, 배열/객체/문자열 등의 흔한 자료구졸르 이용하여 문제를 해결하낟

함수형 프로그래밍에서 함수는 수학적 함수를 의미한다. 입출력이 모두 존재해야하고 입력에 따른 출력은 항상 동일하게 대응되어야 한다

함수형 프로그래밍에서는 관찰할 수 있는 상태 변화를 최소화하려 노려갛ㄴ다. 따라서 함수형 원칙을 고수하는 시스템에 새로운 기능을 추가할 때는 새로운 함수가 지역화되고 비파괴적인 데이터 전이 과정에서 어떻게 동작할 것인지를 파악하는 것이 핵심이다

때문에 뛰어난 프로그래머는 지역변수의 활용을 최대화 한다

수학적 함수

함수형 프로그래밍에서 다루는 수학적함수를 순수함수라 부른다. 순수함수는 동일한 인자에 상응하는 동일한 반환값을 가지는 함수이다

평가시점이 변경되더라도 동일한 결과를 반환하기 때문에 다루기 쉬운 함수가 된다. 객체의 변경이 필요할 경우 새로운 객체를 생성하여 반환한다

외부 변수를 사용하거나 외부 변수를 변경하면 순수 함수가 아니다. 비순수 함수는 평가 시점에 따라 다른 결과값을 가지기 때문에 평가시점을 섬세하게 다루어야 한다

모든 것을 함수로 생각한다

함수형 프로그래밍은 어플리케이션, 함수의 구성요소, 더 나아가 언어 자체를 함수처럼 여기도록 만들고 이러한 함수 개념을 가장 우선순위에 놓는다

정리하자면, 함수형 사고방식은 문제의 해결 방법을 동사(함수)들로 구성(조합)하는 것이다

응용형 삼수를 통해 다형성 만들기

인자로 받은 함수가 알고 있는 인자를 해당 함수에게 평가받아 로직을 완성해나가는 것을 말한다

위의 로직에서 map과 filter와 같은 함수를 응용형 함수라 한다

값 대신 함수를 사용하라

간단한 함수인 repeat로 예시를 들어보자. repeat는 횟수와 값을 받아서 중복된 값을 횟수만큼 갖는 배열을 만드는 함수이다

독립적으로 동작하는 repeat를 구현하는 것도 괜찮지만 반복성이라는 일반성을 가지도록 repeat를 구현할 수 있다면 더 좋을 것이다

repeatedly 함수는 함수형 스타일로 생각하면 어떤 효과를 거둘 수 있는지 잘 보여준다. 값 대신 함수를 사용함으로써 반복성이라는 새로운 가능성이 열렸다

명령형과 함수형 비교하기

명령형 프로그램은 작업 수행에 필요한 전 단계를 노출하여 흐름이나 경로를 자세히 서술한다

반면 선언적 프로그래밍은 독립적인 블랙박스 연산들이 단순하게, 최소한의 제어 구조를 통해 연결되어 추상화 수준이 높닫. 실제 함수형 프로그램은 데이터와 제어 흐름 자체를 고수준 컴포넌트 사이의 단순한 연결로 취급한다

다음은 30세 이상인 users를 거르는 예시이다

추상화 도구 클로저

클로저는 나중에 사용할 목적으로 정의된 스코프에 포함된 외부 바인딩을 캡쳐하는 함수이다. 클로저는 비공개 접근을 제공할 뿐만 아니라 추상화 기법도 제공한다

커링

다인자 함수의 각각의 논리적 인자에 대응하는 새로운 함수를 반환하는 함수를 커리함수라 한다

부분적용

부분적용은 부분적으로 실행을 마친 다음 나머지 인자와 함께 즉시 실행한 상태가 되는 함수이다

함수조립으로 데이터 절반 날리기

여러 함수를 연결하여 더 풍부한 기능의 함수를 조립할 수 있다


Reference

profile
게임 프로그래머 지망생

0개의 댓글