CleanCodeJavascript
- Style Guide
- javascript 코드를 작성할 때 읽기 쉽고 재사용 가능하며 리팩토링 가능하게끔 도와준다.
- 지침일 뿐이다
Reference
#
변수 (Variables)#
1. 의미 있고 발음하기 쉬운 변수 이름 사용#
2. 동일한 유형의 변수에 동일한 어휘 사용#
3. 검색가능한 이름 사용작성할 코드보다 읽을 코드가 더 많기 때문에, 코드를 읽기 쉽고 검색 가능하게 작성해야 한다.
buddy.js, ESLint 같은 도구들이 이름이 정해져있지 않은 상수들을 발견하고 고칠수 있게 해준다.
#
4. 의도를 나타내는 변수들을 사용#
5. 자신만 알아볼 수 있는 작명 피하기명시적인 것이 암시적인 것보다 좋다
#
6. 문맥상 필요없는 것들 쓰지 않기#
7. 기본 매개변수가 short circuiting 트릭이나 조건문 보다 깔끔하다.기본 매개변수는 종종 short circuiting 트릭보다 깔끔하다.
기본 매개변수는 매개변수가 undefined 일 때만 적용됩니다.
'', "", false, null, 0, NaN
같은 falsy 한 값들은 기본 매개변수가 적용되지 않는다.
#
함수 (Functions)#
1. 함수 인자는 2개 이하가 이상적이다매개변수의 개수를 제한하는 것은 함수 테스팅을 쉽게 만들어 주기 때문에 중요하다.
매개변수가 3개 이상일 경우 테스트 경우의 수가 많아진다.
그리고 함수에게 너무 많은 역할을 하게 만든 것이다.
대부분의 상위 객체는 1개의 인자만으로 충분하다.
JavaScript 를 사용할 때 많은 보일러 플레이트 없이 바로 객체를 만들 수 있다.
- 그러기 때문에 많은 인자들을 사용해야 한다면 객체를 이용할 수 있다.
ES6 비구조화(destructuring) 구문의 장점
- 어떤 사람이 그 함수의 Signature (인자의 타입, 반환되는 값의 타입 등)를 볼 때 어떻게 사용되는지 즉시 알 수 있다.
- 함수에 전달된 인수 객체의 지정된 기본타입 값을 복제하며 SideEffect 발생을 방지한다.
- 인수 객체로부터 비구조화된 객체와 배열을 복제되지 않는다. ?
Linter 를 사용하면 사용하지 않는 인자에 대해 경고해주거나 비구조화없이 코드를 짤 수 없게 한다.
#
2. 함수는 하나의 행동만 해야한다. *소프트웨어 공학에서 가장 중요한 규칙이다.
함수가 1개 이상의 행동을 할 때 작성, 테스트, 가독이 어려워진다.
반대로 수정, 가독성이 좋아진다.
#
3. 함수명은 함수가 무엇을 하는지 알 수 있어야 한다.#
4. 함수는 단일 행동을 추상화 해야한다.추상화 된 이름이 여러 의미를 내포하고 있다면 그 함수는 너무 많은 일을 하게끔 설계된 것이다.
함수들을 나누어서 재사용가능하고 테스트 하기 쉽게 만들자
#
5. 중복된 코드 작성 피하기중복된 코드가 있다는것은 어떤 로직을 수정해야 할 일이 생겼을 때,
수정해야 할 곳이 한 곳 이상이라는 뜻이다.
사소한 차이점 때문에 중복된 코드를 작성하는 코드가 있다.
중복된 코드를 제거한다는 것은 하나의 함수/모듈/클래스를 사용하여 사소한 차이점을 처리할 수 있도록 추상화를 만드는 것을 의미한다.
잘 추상화 하지 못한 코드는 중복된 코드보다 나쁠 수 있으므로 여러 원칙들을 따르는 것이 좋다.
#
6. Object.assign 을 사용해 기본 객체 만들기#
7. 매개변수로 플래그 사용하지 않기 *플래그를 사용하는 것 자체가 그 함수가 한가지 이상의 역할을 하고 있다는 것을 뜻한다.
boolean 기반으로 함수가 실행되는 코드가 나뉜다면 함수를 분리하자.
#
8. 사이드 이펙트를 피하기 1함수는 값을 받아서 어떤일을 하거나 사이드 이펙트를 만들어 낸다.
파일에 쓰여질 때
전역 변수 수정
파일 작성할 때
- 파일을 작성하는 함수나 클래스가 여러개 존재하면 안되며 반드시 하나만 있어야 한다.
사이드 이펙트 예
- 어떠한 구조체도 없이 객체 사이의 상태를 공유하는 행위
- 무엇이든 쓸 수 있는 변경가능한 데이터 유형을 사용하는 행위
#
9. 사이드 이펙트 피하기 2자바스트립트에서 기본타입 자료형은 값을 전달하고 객체와 배열을 참조를 전달한다.
우리가 만든함수는 장바구니 배열에 변화를 주며 이 변화는 구매목록에 어떤 상품을 추가하는 기능을 한다. 장바구니 배열을 사용하는 어느 다른 함수가 있다면 이러한 추가에 영향을 받습니다.
- bad case
- 유저가 구매 함수를 호출한다.
- 네트워크를 요청하고 서버에 장바구니 배열을 보낸다.
- 네트워크 연결이 좋지 않아 구매함수는 다시한번 네트워크 요청을 보내야 하는 상황이 생겼다.
- 사용자가 네트워크 요청이 시작되기 전에 실수로 원하지 않는 상품의 장바구니에 추가 버튼을 실수로 클릭하였다.
- 실수가 있고난 뒤 네트워크 요청이 시작되면 장바구니에 추가 함수 때문에 실수로 변경된 장바구니 배열을 서버에 보내게 된다.
- good cade
- 장바구니에 추가 함수는 항상 장바구니 배열을 복제하여 수정하고 복제본을 반환하는 것이다. 장바구니 참조를 보유하고 있는 다른 함수가 다른 변경 사항의 영향을 받지 않게 된다.
- 실제로 입력된 객체를 수정하고 싶은 경우가 있을 수 있지만 이 예제를 생각하면 그런 경우는 거의 없다. 그리고 대부분의 것들이 사이드 이펙트 없이 리팩토링 될 수 있다.
- 큰 객체를 복제하는 것은 성능 측면에서 값이 매우 이러한 프로그래밍 접근법을 가능하게 해 줄 좋은 라이브러리가 존재한다.
이는 객체와 배열을 수동으로 복제하는 것처럼 메모리 집약적이지 않게 해주고 빠르게 복제해준다.
#
전역 함수 피하기전역 환경을 사용하는 것은 JavaScript 에서 나쁜 관행이다.
- 다른 라이브러리들과의 충돌이 있을 수 있다.
- diff 메서드를 Array.prototype 에서 쓰면 다른 라이브러리와 충돌 할 수 있다. ES6 의 classes를 사용해서 전역 Array 를 상속하자.
bad case
good case:
명령형 프로그래밍보다 함수형 프로그래밍을 지향하자#
함수형 언어는 더 깔끔하고 테스트하기 쉽다.
bad case
good case
#
조건문 캡슐화 하bad case
good case
#
부정조건문 사용하지 않기bad case
good case
#
조건문 작성 피하기if 문 대신 다형성을 이용하면 가능하다. clean code 컨셉은 함수는 단 하나의 일만 수행한다 이다. 함수나 클래스에 if 문을 쓴다면 함수나 클래스가 한가지 이상의 일을 수행하고 있다는 것과 같다.
bad case
good case
#
타입-체킹 피하기 1 일관성 있는 API 사용하기JavaScript는 타입이 정해져 있지 않은 자유로움 때문에 발생하는 버그에 타입-체킹 이외에 피할 수 있는 많은 방법들이 존재한다. bad case:
good case:
#
타입-체킹 피하기 2 TypeScript 도입bad case:
good case:
#
죽은 코드 지우기죽은 코드는 중복된 코드 만큼이나 좋지 않다. 호출되지 않는 코드는 지우자. 그 코드가 여전히 필요해도 그 코드는 버전 히스토리에 안전하게 남아있을 것이다.
bad case
good case
#
객체와 자료구조 (Objects and Data Structures)#
getter와 setter를 사용하세요.JavaScript는 public private 같은 키워드가 없다. 그래서 getter setter를 사용하여 객체의 데이터에 접근하는 것이 객체의 속성을 찾는 것보다 훨씬 낫다.
- 객체의 속성을 얻는 것 이상의 많은 것을 하고싶을 때, 코드에서 모든 접근자를 찾아 바꾸고 할 필요가 있다.
- set 할 때 검증로직을 추가하는 것이 코드를 더 간단하게 만든다.
- 내부용 API 를 캡슐화 할 수 있다.
- getting 과 setting 할 때 로그를 찾거나 에러처리를 하기 쉽다.
- 클래스를 상속해서 디폴트 동작을 재정의할 수 있다.
- 서버에서 객체 속성을 받아올 때 lazy load 할 수 있다.
bad case:
good case
#
객체에 비공개 멤버를 만드세요클로져를 이용하면 가능하다
bad case
#
클래스 Classes#
ES5 의 함수보다 ES6의 클래스 사용하기ES5의 클래스에서 상속, 메소드 정의 가 어려웠다. 상속이 필요한 경우라면 클래스를 사용하는 것이 좋다. 크고 더 복잡한 객체가 필요한 경우가 아니라면 클래스보다 작은 함수를 사용하자.
bad case
good case
#
메소드 체이닝 사용하기JavaScript 에서 Method Chaining 은 jQuery Lodash 등 많은 라이브러리에서도 사용하는 유용한 패턴이다. 코드를 간결하고 이해하기 쉽게 해준다. 클래스 함수에서 단순히 모든 함수의 끝에 this 를 리턴해주는 것으로 클래스 메소드를 추가로 연결할 수 있다.
bad case
good case:
#
상속보단 조합(composition)을 사용하세요상속 보다 조합을 사용했을 때 이득이 더 많다.
상속을 쓰는 좋은 예시
- 상속관계가 has-a 관계가 아니라 is a 일 때 (사람 -> 동물 vs 유저 -> 유저정보)
- 기반 클래스의 코드를 다시 사용할 수 있을 때 (인간은 모든 동물처럼 움직일 수 있다.)
- 기반 클래스를 수정하여 파생된 클래스 모두를 수정하고 싶을 때 (이동시 모든 동물이 소비하는 칼로리를 변경하고 싶을 때)
bad case
good case
#
SOLID#
단일 책임 원칙 (Single Responsibility Principle, SRP)하나의 클래스에 너무 많은 기능들이 있고 이 작은 기능들을 수정할 때 이 코드가 다른 모듈들에 어떠한 영향을 끼치는지 이해하기 어려울 수 있다.
bad case:
good case:
#
개방/폐쇄 원칙 (Open/Closed Priciple, OCP)소프트웨어 개체(클래스, 모듈, 함수 등)는 확장을 위해 개방적이어야 하며 수정시엔 폐쇄적이어야 한다. 사용자가 .js 소스 코드 파일을 열어 수동으로 조작하지 않고도 모듈의 기능을 확장하도록 허용해야 한다.
bad case:
good case:
#
리스코프 치환 원칙 (Liskov Subsititution Priciple, LSP)- 매우 간단하지만 강력한 원칙.
- 자료형 S가 자료형 T의 하위형이면, 프로그램이 갖추어야 할 속성들(정확성, 수행되는 작업 등)의 변경사항 없이, 자료형 T의 객체를 자료형 S의 객체로 치환할 수 있어야 한다는 원칙이다.
부모 클래스와 자식클래스를 가지고 있을 때 베이스 클래스와 하위 클래스를 잘못된 결과 없이 서로 교환하여 사용할 수 있다.
정사각형은 직사각형이지만 상속을 통해 is-a 관계를 사용하여 모델링 한다면 문제가 발생한다.
good case
#
인터페이스 분리 원칙 (Interface Segregation Principle, ISP)클라이언트는 사용하지 않는 인터페이스에 의존하도록 강요 받으면 안된다.
JavaScript 에 타입 시스템이 없다 하더라도 중요하고 관계가 있는 법칙이다. 가장 좋은 예는 방대한 양의 설정 객체가 필요한 클래스 이다. 대부분의 경우 설정들이 전부다 필요하지 않기 때문에 클라이언트가 방대한 양의 옵션을 설정하지 않는 것이 좋다. 설정을 선택적으로 할 수 있다면 무거운 인터페이스를 만드는 것을 방지할 수 있다.
bad case
good case
#
의존성 역전 원칙 (Dependency Inversion Principle, DIP)- 상위 모듈은 하위 모듈에 종속되어서는 안된다. 둘 다 추상화에 의존해야 한다.
- 추상화는 세부사항에 의존하지 않습니다. 세부사항은 추상화에 의해 달라져야 한다.
상위모듈이 하위 모듈의 세부사항을 알지 못하게 한다. 모듈간의 의존성을 감소시키는 데에 있다.
bad case
good case
#
테스트 Testing테스트는 배포보다 중요하다. 테스트 없이 배포한다는 것은 당신이 짜 놓은 코드가 언제든 오작동해도 이상하지 않다는 얘기와 같다. 테스트에 얼마나 시간을 투자할 지는 팀에 달려있지만 Coverage 가 100% 라는 것은 개발자에게 높은 자신감과 안도감을 준다. 훌륭한 테스트 도구를 보유해야 하는 것 뿐만 아니라 훌륭한 Coverage 도구를 사용해야 한다는 것을 의미한다.
테스트 코드를 작성하지 않다는 것은 그 무엇도 변명이 될 수 없다. 훌륭하고 많은 JavaScript 테스트 프레임워크들이 있다. 새로운 기능/모듈 들을 짤 때 테스트 코드를 작성하자. 만약 테스트 주도 개발 방법론(Test Driven Development, TDD) 이 당신에게 맞는 방법이라면 훌륭한 개발 방법이 될 수 있다.
bad case
good case
#
동시성 (Concurrency)#
Callback 대신 Promise 를 사용하기bad case
good case
#
Async/Await 는 Promise 보다 더욱 깔끔하다#
에러 처리 Error Handling에러를 뱉는다는 것은 좋은 것이다. 프로그램에서 무언가가 잘못되었을 때 런타임에서 성공적으로 확인되면 현재 스택에서 함수 실행을 중단하고 (노드에서) 프로세스를 종료하고 스택 추적으로 콘솔에서 사용자에게 그 이유를 알려준다.
#
단순히 에러확인만 하지 말기console.log 를 통해 콘솔에 로그를 기록하는 것은 에러 로그를 잃어버리기 쉽기 때문에 좋은 방법이 아니다. try/catch 로 어떤 코드를 감쌌다면 그 코드에 에러가 날것을 대비한 것이므로 그에대한 계획이 있거나 어떠한 장치를 해야한다.
bad case:
good case:
#
Promise 가 실패된 것을 무시하지 말기단순히 에러확인만 하지 말기 와 같은 이유이다
bad case
good case
#
포맷팅 Formatting포멧팅 체크를 자동으로 하주는 많은 도구들이 있다. 개발자들끼리 포맷팅에 대해 논쟁하는 것 만큼 시간과 돈을 낭비하는 것이 없다. 자동으로 서식을 교정해주는 것(들여쓰기, 탭이냐 스페이스냐, 작은따옴표냐 큰따옴표냐) 에 해당하지 않는 사항에 대해서는 몇가지 지침을 따르는 것이 좋다.
#
일관된 대소문자를 사용하자bad case:
good case
#
함수 호출자와 함수 피호출자는 가깝게 위치하기어떤 함수가 다른 함수를 호출하면 그 함수들은 소스 파일안에서 서로 수직으로 근접해 있어야 한다. 이상적으로는 함수 호출자를 함수 피호출자 바로 위에 위치시켜야 한다. 코드를 읽을 때 신문을 읽듯 아래로 읽기 때문에 코드를 작성할 때도 읽을 떄를 고려하여 작성해야 한다.
bad case
good case
#
주석 Commnets#
주석은 단지 그 로직이 복잡하다는 것을 말할 뿐이다.주석을 다는 것은 사과해야 할 일이며 필수적인 것이 아니다. 좋은 코드는 코드 자체로 말한다
bad case
good case:
#
주석으로 된 코드를 남기지 말기버전 관리 도구가 존재하기 때문에 코드를 주석으로 남길 이유가 없다.
#
코드 기록을 주석으로 남기지 말기버전 관리 도구를 이용해야 하는 것을 꼭 기억하자. 죽은 코드도 불필요한 설명도 특히 코드의 기록에 대한 주석도 필요하지 않다. 코드의 기록에 대해 보고 싶다면 git log를 사용하자.
bad case:
good case:
#
코드의 위치를 설명하지 말자이건 정말 쓸데 없습니다. 적절한 들여쓰기와 포맷팅을하고 함수와 변수의 이름에 의미를 부여하자
bad case:
good case: