티스토리 뷰

※ 출처: https://www.udemy.com/course/clean-code-js/


  • 총 섹션 13으로 이루어져 있고 22년 01월 기준 섹션 8까지 올라와 있음.
  • 나머지 강의 아직 안 올라와서 나중에 듣고 업데이트 하기.
    • 앞에 부분은 이전에 들어서 제목만 적음.
    • 01-01 :: ~5장, 26강 early return
    • 01-02 :: ~ 8장, 57강 화살표 함수
    • 01-06 :: ~ 8장, 60강 Closure
  • 이곳저곳에서 다 듣거나 본 내용이라 어렵진 않아 2배속으로 봤는데 막상 적용 시키지 않는 것도 좀 있는듯..
  • 계속 의식하고 적용시키려고 해야할 듯.

2장: 변수 다루기

  1. var 지양하기
  2. function scope & block scope
  3. 전역 공간 사용 최소화
  4. 임시변수 제거하기
  5. 호이스팅 주의하기

3장: 타입 다루기

  1. 타입 검사
  2. undefined & null
  3. eqeq 줄이기
  4. 형변환 주의하기
  5. isNaN

4장: 경계 다루기

  1. min - max
  2. begin - end
  3. first - last
  4. prefix - suffix
  5. 매개변수의 순서가 경계다.

5장: 분기 다루기

  1. 값식문
  2. 삼항 연산자 다루기
  3. Truthy & Falsy
  4. 단축평가
  5. 부정조건문 지양하기
    • 코드를 좀 더 명시적이게 작성하기 위함.
    • 생각을 한번 더 해야한다 → 실수 확률 높음.
    • if 문이 처음부터 오고 true 부터 실행시킨다.
    • 그럼 언제?
      • early return
      • form validation
      • 보안 혹은 검사 로직
// 부정조건문 지양하기
if (!isCondition) {
    console.log('거짓인 경우만 실행');
}

if (isNotCondition) {
    console.log('거짓인 경우만 실행');
}
  1. Default Case 고려하기
  • edge case 를 고려하자.
  • ex) 함수의 인자가 안들어오거나 잘못들어오거나 이런경우 고려하자.
  • 대부분의 라이브러리나 언어에서 default 값 설정을 중요시하게 여김.
    • 유저의 실수를 대비
function safeParseInt(number, radix) {
    return parseInt(number, radix || 10);
}
  1. 명시적인 연산자 사용 지양하기
  • 연산자 우선순위를 생각해서 작성 하지말고 () 와 같은 걸 써서 좀 더 명시적이게 작성하자.
  • 예측 가능하고 누가봐도 읽기 쉽고 디버깅 하기 쉬운...
  1. Nullish coalescing operator
  • null 과 undefined 를 평가할때만 사용해야 한다!
function createElement1(type, height, width) {
    const element = document.createElement(type || 'div');
    element.style.height = String(height || 10) + 'px';
    element.style.width = String(width || 10) + 'px';
    return element;
}

function createElement2(type, height, width) {
    const element = document.createElement(type ?? 'div');
    element.style.height = String(height ?? 10) + 'px';
    element.style.width = String(width ?? 10) + 'px';
    return element;
}

// width, height 가 0인 element 를 생성하고 싶음.
const el = createElement1(span, 0, 0);
console.log(el.style.height) = '10px'; // ???

const el = createElement2(span, 0, 0);
console.log(el.style.height) = '0px'; // good
  • createElement1 의 경우 0은 falsy 값으로 인식되어 기본값 10이 들어가버림.
  • createElement2 의 ?? 연산자는 좌항이 null 이거나 undefined 일 경우만 우항의 값을 대입함.
// No chaining with AND or OR operators 
// syntax error 
null || undefined ?? 'foo'; 

// good 
(null || undefined) ?? 'foo';
  1. 드모르간의 법칙
if (isValidToken && isValidUser) {
    console.log('로그인 성공');
}

// bad -  감싸서 부정연산자를 넣는건 더 복잡해지고 헷갈린다.
// 뒤에 연산이 더 추가된다면 복잡해짐.
if (!(isValidToken && isValidUser)) {
    console.log('로그인 실패');
}

// good - 좀 더 간단해짐 
if (!isValidToken || !isValidUser) {
    console.log('로그인 실패');
}

6장: 배열 다루기

  1. JavaScript의 배열은 객체다.
// 배열인지 체크하고 싶을때 이렇게 사용 추천
Array.isArray(arr); 
  1. Array.length
  • Array.length 가 배열의 길이를 보장하진 않음. 그냥 마지막 인덱스라고 봐야함.
  • JS 에서 배열의 length 는 의식적으로 주의해서 사용하자.
// Array.length 이용하기 
Array.property.clear = function() {
    this.length = 0;
}
function clearArray(array) {
    array.length = 0;
    return array;
}

const arr = [1, 2, 3];

//
arr.clear();
console.log(arr); // []
console.log(clearArray(arr)); // []
  1. 배열 요소에 접근하기
// ex1) bad - 배열[num] 이런식으로 직접 접근하는건 어떤 값인지 명확하지 않음.
function operateTime(input, operators, is) {
    inputs[0].split('').forEach((num) => {
        .... logic
    });
    inputs[1].split('').forEach((num) => {
        .... logic
    });
}

// ex1) good1 - 구조분해할당을 사용해서 접근 (명시적)
function operateTime(input, operators, is) {
    const [firstInput, secondInput] = inputs;

    firstInput.split('').forEach((num) => {
        .... logic
    });
    secondInput.split('').forEach((num) => {
        .... logic
    });
}

// ex1) good2 - 받을때부터 분해해서 받기
function operateTime([firstInput, secondInput], operators, is) {
    firstInput.split('').forEach((num) => {
        .... logic
    });
    secondInput.split('').forEach((num) => {
        .... logic
    });
}

// ex2) bad - 직접 접근은 이게 뭐하는애인지 모름.
function formatDate(targetDate) {
    const date = targetDate.toISOString().split('T')[0];

    ... logic
}

// ex2) good1 - 배열에 하나만 들어있어도 구조분해할당 가능
function formatDate(targetDate) {
    const [date] = targetDate.toISOString().split('T');

    ... logic
}

// ex2) good2 - 유틸함수 만들어 사용
function head(arr) {
    return arr[0] ?? '';
}
function formatDate(targetDate) {
    const date = head(targetDate.toISOString().split('T'));

    ... logic
}
  1. 유사 배열 객체
function generatePriceList() {
    // arguments는 배열이 아님
    // arguments.map is not a function
    console.log(Array.isArray(arguments)); // false
    1. return arguments.map((arg) => arg + '원'); 

    // map 을 사용하고싶으면 Array.from 으로 배열로 바꿔서 사용해야함
    2. return Array.from(arguments.map((arg) => arg + '원'));
}

generatePriceList(100, 200, 300, 400, 500); 
  1. 불변성
  • 배열을 복사한다.
  • 새로운 배열을 반환하는 메서드들을 활용한다.
    • Array.filter(), map(), slice() ...
  1. for 문 배열 고차 함수로 리팩터링
  • for 문 대신 배열의 고차 함수를 사용하자.
  1. 배열 메서드 체이닝 활용하기
// 이런식으로 메서드 체이닝 활용 가능 
function getWonPrice(priceList) {
    return priceList
        .filter(isOverOneThousand)
        .sort(ascendingList)
        .map(suffixWon);
}
  1. map vs forEach
  • return 이 있는가 없는가
  1. continue vs break
  • 고차함수 안에서 break, continue 는 syntax error 임
  • 사용하고 싶을땐 try catch 이나 차라리 for 문 사용
  • every, some, find, findIndex() 를 사용하여 반복을 종료할 수 있음.

7장: 객체 다루기

  1. Shorthand Properties
const firstName = 'subin';
const lastName = 'lee';

const name = someFunction({
    firstName,
    lastName,
});
  1. Computed Property Name
// ex
const handleChange = (e) => {
    setState({
        [e.target.name]: e.target.value,
    });
} 
  1. Lookup Table (순람표)
// bad - 새로운 조건이 추가될때마다 쭉 늘어남
function getUserType(type) {
    switch (key) {
        case 'ADMIN':
            return '관리자';
            break;
        case 'INSTRUCTOR':
            return '강사'
            break;
        default:
            return '해당없음';
    }
}

// good - 객체, computed property 활용
function getUserType(type) {
    // ex1 - 객체 사용
    const USER_TYPE = {
        ADMIN: '관리자',
        INSTRUCTOR: '강사',
        STUDENT: '학생',
        UNDEFINED: '해당 없음',
    }

    // 1
    return USER_TYPE[type] ?? '해당 없음';
    // 2
    return USER_TYPE[type] ?? USER_TYPE.UNDEFINED;

    // ex2 - 바로 리턴, ex1 방법을 더 권장함.
    return (
        {
            ADMIN: '관리자',
            INSTRUCTOR: '강사',
            STUDENT: '학생',
        }[type] ?? '해당 없음';
    )
}
  1. Object Destructuring
// ex1 - 인자의 순서를 지켜야함.
function Person(name, age, location) {
    this.name = name;
    this.age = age;
    this.location = location;
}
const subin = new Person('subin', 29, 'korea');

// ex2 - 구조분해할당 - 인자 순서 안지켜도됨.
// 인자가 3개 이상일때 이런식으로 하는걸 추천함.
function Person({ name, age, location }) {
    this.name = name;
    this.age = age ?? 30;
    this.location = location ?? 'korea';
}
const subin = new Person({
    name: 'subin', 
    age: 29,
    location: 'korea',
});

// ex3 - 필수적인 값을 받고싶을때
function Person(name, { age, location }) {
    this.name = name;
    this.age = age;
    this.location = location;
}
const options = {
    age: 29,
    location: 'korea',
}
const subin = new Person('subin', options);
  1. Object.freeze
  • 대중적인 유틸 라이브러리 (lodash) 사용
  • 직접 유틸 함수 생성 (deepFreeze 처럼)
  • TS 사용 ⇒ readonly
// Object.freeze -> 변화 불가하게 만듬
const STATUS = Object.freeze({
    PENDING: 'PENDING',
    SUCCESS: 'SUCCESS',
    FAIL: 'FAIL',
    OPTIONS: {
        GREEN: 'GREEN',
        RED: 'RED',
    },
});

STATUS.PENDING = 'P2'; // 변화 X
STATUS.KING = 'KING'; // 추가도 안됨
console.log(STATUS.PENDING); // PENDING

Object.isFrozen(STATUS.PENDING); // true

// 깊은 freezing 안됨.
Object.isFrozen(STATUS.OPTIONS); // false 

STATUS.OPTIONS.GREEN = 'G';
STATUS.OPTIONS.YELLOW = 'YELLOW';
console.log(STATUS.OPTIONS.GREEN); // G
console.log(STATUS.OPTIONS.YELLOW); // YELLOW
  1. Prototype 조작 지양하기
  • 이미 JS는 많이 발전했다.
    • 직접 만들어서 모듈화
  • JS 빌트인 객체를 함부로 건들지 말자.
  • 다른 라이브러리들도 프로토타입을 건들진 않음.
  1. hasOwnProperty
// ex1.
const person = {
    name: 'subin',
}

person.hasOwnProperty('name'); // true
person.hasOwnProperty('age'); // false

// ex2. 예약어 보호를 받지 못함 
// -> Object.prototype.hasOwnPrototype.call() 이런식으로 사용
const foo = {
    hasOwnProperty: function() {
        return 'hasOwnProperty';
    },
    bar: 'string',
};
foo.hasOwnProperty('bar') // hasOwnProperty
Object.prototype.hasOwnProperty.call(foo, 'bar') // true

// ex3. 유틸로 만들기
function hasOwnProp(targetObj, targetProp) {
    return Object.prototype.hasOwnProperty.call(
        targetObj,
        targetProp,
    );
}
  1. 직접 접근 지양하기
  • 예측 가능한 코드를 작성해 동작이 예측 가능하게 하자.
  • 데이터에 접근할때는 항상 안전하게 접근하자.
  • 괜히 setter, getter 를 사용하는게 아니다.
  1. Optional Chaining
  2. Extends & Mixin

8장: 함수 다루기

  1. 함수, 메서드, 생성자
  • 함수, 메서드, 생성자를 구분할 수 있고 어쩔때 사용하는지 알아야한다.
  1. argument & parameter
  • Parameter (Formal Parameter)
  • Argument (Actual Parameter)
function example(parameter) {
    console.log(parameter);
}

const argument = 'foo';
example(argument);
  1. 복잡한 인자 관리하기
  • 무조건 인자가 3개 이상이라고 나쁜것은 아니다. 맥락이 중요하다.
  • 객체 구조분해할당 사용하면 좋다.
  • 다른사람이 이 함수를 사용해도 문제 없게 명시적으로 작성.
  1. Default value, Default parameter
// ex1
function createCarousel({
    margin = 0,
    center = false,
    navElement = 'div',
} = {}) {
    ... logic
    return {
        margin,
        center,
        navElement,
    };
}

createCarousel(); // {margin = 0, center = false, navElement = 'div'}

// ex2. 필수로 받아야하는 인자 설정 
const required = (argName) => { throw new Error(`required ${argName}`) };
function createCarousel({
    items = required('items'),
    margin = 0,
    center = false,
    navElement = 'div',
} = {}) {
    ... logic
    return {
        margin,
        center,
        navElement,
    };
}
  1. Rest Parameters
  • 인자의 가장 마지막에 들어와야함.
  • 배열로 들어옴.
function sumTotal(initValue, bonusValue, ...args) {
    console.log(initValue); // 100
    console.log(bonusValue); // 99
    Array.isArray(args); // true
    return args.reduce((acc, cur) => acc + cur);
}

sumTotal(100, 99, 1, 2, 3, 4, 5, 6, 7, 8);
  1. void & return
  • void function 에 불필요하게 return 을 해줄 필요가 없음.
  • API 명세를 잘 읽어서 return 값이 무엇인지 알 필요가 있음.
  • 함수명으로 반환값을 유추할 수도 있으니 네이밍을 잘하자.
  1. 화살표 함수
  • 내부에서 arguments, call, apply, bind 함수 사용 안됨.
  • this 주의해야함.
  • 생성자로 사용할 수 없음.
  • 클래스의 메소드로 사용할때 예외가 많음.
  1. Callback Function
  • 제어권을 위임하는 것.
  • 무조건 나쁘다? 잘못 이해하는 것임.
  1. 순수 함수
  • side effect를 일으키지 않는 함수
  • 예측이 가능한..
  • input이 동일하면 output도 동일해야지.
let num1 = 10;
let num2 = 20;

function impureSum() {
    return num1 + num2;
}
function puerSum(num1, num2) {
    return num1 + num2;
}

// 객체, 배열 => 새롭게 만들어서 리턴
function changeObj(targetObj) {
    // targetObj.num = 100;
    return { ...targetObj, num: 100 };
}
  1. Closure
  • 어색해서 그런지 읽히는게 직관적이지 않음.
  • 어디에 어떻게 써먹을까 했는데 잘쓰면 엄청 유용해보임.
  • 쓰려고 연습해야할듯.
// ex.1
function add(num1) {
    return function sum(num2) {
        return num1 + num2;
    }
}

const addOne = add(1); // function 
const addTwo = add(2); // function
const addThree = add(1)(2) // 3

// ex.2
function add(num1) {
    return function (num2) {
        return function (calculateFn) {
            return calculateFn(num1, num2);
        }
    }
}

function sum(num1, num2) {
    return num1 + num2;
}

function multiple(num1, num2) {
    return num1 * num2;
}

const addOne = add(5)(2); // function
const sumAdd = addOne(sum); // 7
const sumMultiple = addOne(multiple); // 10

// ex.3
function log(value) {
    return function(fn) {
        fn(value);
    }
}

const logFoo = log('foo');

logFoo((v) => console.log(v));
logFoo((v) => console.info(v));
logFoo((v) => console.error(v));
logFoo((v) => console.warn(v));

// ex.4
const arr = [1, 2, 3, 'A', 'B', 'C'];

// 4-1
const isNumber = (value) => typeof value === 'number';
const isString = (value) => typeof value === 'string';

// 4-2
function isTypeOf(type) {
    return typeof value === type;
}
const isNumber = (value) => isTypeof('number');
const isString = (value) => isTypeof('string');

// 4-3
function isTypeOf(type) {
    return function (value) {
        return typeof value === type;
    }
}

const isNumber = isTypeOf('number');
const isString = isTypeOf('string');

arr.filter(isNumber);
arr.filter(isString);

// ex.5
function fetcher(endpoint) {
    return function (url, options) {
        return fetch(endpoint + url, options)
            .then((res) => {
                if (res.ok) {
                    return res.json();
                } else {
                    return new Error(res.error);
                }
            })
            .catch((err) => console.err(err));
    }
}

const naverApi = fetcher('http://naver.com');
const daumApi = fetcher('http://daum.net');

naverApi('/webtoon').then((res) => res);
daumApi('/webtoon').then((res) => res);

// ex.6

9장: 추상화하기

10장: 에러 다루기

11장: Browser & Web API

12장: 도구에 위임하기

13장: 함께하기