ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Udemy 클린코드 JS 강의 정리
    JavaScript & TypeScript 2022. 1. 6. 22:48

    ※ 출처: 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장: 함께하기

킹수빈닷컴