#3 우아한 테크러닝 3기 1주차 - 2

TheJang·2020년 9월 17일
0
post-thumbnail

github에만 업로드하고 블로그에 업로드를 안해서 부랴부랴 업로드 하러왔습니다 ㅎㅎ😆

우아한 테크러닝 React&Typescript 2회차

Table of Contents
  • Javascript의 타입과 변수 선언법 :link:
  • Javascript의 값 :link:
  • Javascript의 함수 :link:
  • ES6 이후 변형된 함수 문법 :link:
  • new 연산자와 함수와 인스턴스 객체 :link:
  • ES6이후의 클래스 :link:
  • this와 실행 컨텍스트 :link:
  • 클로저 :link:
  • 비동기와 Promise :link:
  • Flux 패턴 :link:
  • Redux 만들어보기 :link:

Javascript의 타입과 변수 선언법

Javascript의 타입은 원시 타입객체 타입으로 구분되며 변수는 아래와 같이 선언할 수 있다.

var x = 10;
let y = 10;
const y = 10;

Javascript의 값

아래의 코드에서 x변수이며 10에 해당한다.

let x = 10;

Javascript에서 값이라고 정의한 것들은 변수에 넣을 수 있다.

값에 해당하는 데이터는 문자열, 숫자, 객체등이 존재한다.

function foo() {}
let y = foo;

Javascript에서는 함수 또한 객체이며 위와 같이 변수에 할당 가능하다.

Javascript에서 모든 함수는 undefinedreturn으로 명시된 값을 반환한다.

Javascript의 함수

Javascript에서의 함수는 값이며 모든 값은 변수에 대입될 수 있다.

Javasript에서 실행 결과가 값이 되는 것은 이며 그 외의 모든 것은 이다.

Javascript에서 함수를 정의하는 방법은 함수 정의문함수 정의식이 존재한다.

  • 함수 정의문
function foo() {}
  • 함수 정의식
const bar = function bar() {};
bar();

호출한 bar()의 이름은 함수 정의식에서 오른쪽에 대입된 함수의 이름이 아니다.

오른쪽에 정의된 함수의 이름은 bar변수에서는 알 수 없는 익명 함수가 된다.

  • IIFE
(function () {})();

익명 함수를 값으로 만들어 사용하기 위해서는 익명 함수를 ()로 감싸 즉시 호출하면 된다.

  • 함수의 매개변수와 반환값으로서의 함수
function foo(x) {
    x();
    return function () {};
}

const y = foo(function () {});

함수는 값으로 취급되기 때문에 위와 같이 매개변수 x의 형태로 함수를 전달할 수 있다.

또한 foo 함수의 반환값으로 함수가 반환될 수 있으며 반환된 함수는 변수에 저장할 수 있다.

위와 같이 함수를 매개변수로 넘겨주는 것을 콜백 함수라고 하며 호출을 위임하는 역할을 한다.

소프트웨어 공학에서 함수를 인자로 받고 함수를 반환하는 함수를 1급 함수 (High Order Function)이라고 한다

ES6 이후 변형된 함수 문법

ES6이전에서 함수식을 만들때 아래와 같이 만들 수 있다.

const foo = function (x) {};

위의 foo 함수에서 foo 함수를 재귀호출하기 위해서는 오른쪽에 대입된 함수의 이름이 필요하다.

const foo = function foo() {
    foo();
};

ES6 이후에서 함수식을 만들때 =>를 이용한 화살표 함수로 만들 수 있다.

const bar = (x) => {};

화살표 함수는 한줄 함수, 람다 함수라고도 불리며 아래와 같이 하나의 식을 반환하는 경우 자주 사용된다.

const bar = (x) => x * 2;

화살표 함수는 인자가 하나일 경우 ()를 생략할 수 있으며 한줄로 구성될 경우 return 생략이 가능하다.

const foo = x => 10;
const boar = (x, y) => x * y;

new 연산자와 함수와 인스턴스 객체

new 연산자를 호출하면 this를 생성하며 인스턴스 객체를 생성된 this를 반환한다.

function foo() {
    this.age = 10;
}

const y = new foo();
console.log(y); // { age: 10, constructor: object }

생성된 인스턴스 객체 y를 출력하면 constructor가 추가되어 반환되는 것을 볼 수 있다.

생성된 객체의 타입을 확인하기 위해서는 instanceofy instanceof foo와 같이 사용할 수 있다.

ES6이후의 클래스

함수를 new 연산자를 이용해 인스턴스를 생성할 수 있지만 ES6이후 부터는 아래와 같이 클래스 문법을 제공한다.

class bar {
    constructor() {
        this.age = 10;
    }
}

console.log(new bar()); // { age: 10, constructor: object }

보기에는 비슷하지만 코드 구조적인 부분에서 class를 이용하면 조금 더 명시적이다.

function foo() {
    this.age = 10;
}

foo(); // undefined
new foo(); // { age: 10 }

또한 작성된 함수가 인스턴스를 위한 함수이지만 해당 기능으로의 사용을 강제할 수 없다.

this와 실행 컨텍스트

아래 코드에서 this.nameperson객체의 name을 가리킨다.

const person = {
    name: "김민수",
    getName() {
        return this.name;
    },
};

console.log(person.getName()); // 김민수

this가 결정되는 방식들은 실행 컨텍스트로 호출시에 결정된다.

const man = person.getName;

console.log(man());

위의 코드에서 mangetName함수를 저장해 man()과 같이 호출할 수 있다.

하지만 man이 호출되는 시점의 실행 컨텍스트전역 객체이므로 name이 존재하지 않아 오류가 발생한다.

const man = person.getName.bind(person);

console.log(man()); // 김민수
person.getName.call(person); // 김민수

this를 고정하기 위해서는 bind, call, apply 함수를 사용할 수 있다.

클로저

아래의 코드에서 foo 함수는 bar 함수를 반환한다.

function foo(x) {
    return function bar() {
        return x;
    };
}

const f = foo(10);
console.log(f()); // 10

bar함수를 담은 ff()와 같이 호출할 수 있으며 10을 반환한다.

위의 코드의 bar함수에서 접근하는 x변수는 bar함수 스코프의 외부 범위다.

foo 함수가 호출이 완료된 시점에서 foo의 스코프는 사라지지만 반환된 bar에서는 x를 기억한다.

위와 같이 함수 실행결과로 반환된 함수가 외부의 스코프를 기억하고 있는 상태클로저라고 한다.

function makePerson() {
    let age = 10;
    return {
        getAge() {
            return age;
        },
        setAge(x) {
            age = x > 1 && x < 130 ? x : age;
        },
    };
}

p = makePerson();
console.log(p.getAge()); // 10
p.setAge(500);
console.log(p.getAge()); // 10
p.setAge(20);
console.log(p.getAge()); // 20

클로저를 사용하면 p.age와 같이 접근할 수 없으며 위와 같이 값을 캡슐화해 보호할 수 있다.

비동기와 Promise

아래와 같이 setTimeout이나 API를 호출하는 경우 코드가 비동기적으로 실행된다.

setTimeout(function () {
    console.log("foo");
    setTimeout(function () {
        console.log("bar");
    }, 2000);
}, 1000);

위의 코드는 비동기적인 로직을 2 depth를 갖는다. 하지만 더 깊이가 깊어질 경우 콜백 헬이 발생한다.

비동기 로직 작성시 콜백 헬과 같은 코드를 방지하기 위해 나온 것이 Promise다.

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("응답1");
    }, 1000);

    reject();
});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("응답2");
    }, 1000);

    reject();
});

p1.then(p2())
    .then(function (r) {
        console.log(r);
    })
    .catch(function () {});

Promisethen의 콜백 함수는 resolve로 호출되며 catch로 넘겨준 콜백 함수는 reject로 호출된다.

Promise를 조금 더 가독성이 좋게 만들기 위해서 async - await을 이용할 수 있다.

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function main() {
    console.log("1");
    try {
        const result = await delay(3000);
    } catch (e) {
        console.error(e);
    }
    console.log("2");
}

main();

위의 코드를 실행해보면 콘솔에 1이 출력된 후 3초 뒤에 2가 출력된다.

awaitasync키워드가 붙은 함수에서 사용 가능하며 Promiseresolve된 값을 저장할 수 있다.

Promiserejecttry-catch구문의 catch를 이용해 사용할 수 있다.

Flux 패턴

어플리케이션에서 여러 개의 컴포넌트에서 하나의 특정 상태가 필요한 경우가 생긴다.

Flux 패턴은 하나의 store에서 상태에 관한 정보를 저장해 사용하는 패턴이다.

가상돔 사용 이전의 어플리케이션은 하나의 상태가 변경될 시 모든 화면이 다시 그려지기 때문에 불가능 했다.

React에서는 가상돔을 사용해 기존의 DOM과 비교해 다른 부분만 DOM에 반영하기 때문에 가능해졌다.

Redux 만들어보기

reduxstore에 저장된 데이터는 컴포넌트가 직접적으로 데이터를 바꿀 수 없다.

상태를 직접적으로 바꿀 수 없도록 하기위해 클로저를 이용해 state를 은닉화한다.

createStore를 이용해 store를 생성할 수 있고 getState를 이용해 상태를 볼 수 있다.

store에 저장된 값은 반드시 reducer함수가 호출되어 변경되어야 한다.

reducer는 반드시 action을 인자로 받는 dispatch를 이용해 호출되어야 한다.

reducer는 호출될 때 기존 저장된 상태와 실행할 액션 객체를 매개변수로 전달 받는다.

액션 객체의 타입에 type에따라 기존 state의 값을 변경시켜 store에 저장된다.

subscribe함수는 store를 구독하는 listener 배열에 받은 fn을 저장해 dispatch시 마다 실행한다.

  • redux.js
export function createStore(reducer) {
    let state;
    const listener = [];
    const publish = () => {
        listener.forEach(({ subscriber, context }) => {
            subscriber.call(context);
        });
    };
    const dispatch = (action) => {
        state = reducer(state, action);
        publish();
    };
    const subscribe = (subscriber, context = null) => {
        listener.push({ subscriber, context });
    };
    const getState = () => ({ ...state });

    return {
        getState,
        dispatch,
        subscribe,
    };
}

export function actionCreator(type, payload = {}) {
    return {
        type,
        payload: { ...payload },
    };
}
  • index.js

아래와 같이 실제 redux와 동일하게 reducer를 작성할 수 있다.

import { createStore, actionCreator } from "./redux";

const INIT = "init";
const INCREMENT = "increment";
const RESET = "reset";

function reducer(state = {}, { type, payload }) {
    switch (type) {
        case INIT: {
            return {
                ...state,
                count: payload.count,
            };
        }
        case INCREMENT:
            return { ...state, count: state.count ? state.count + 1 : 1 };
        case RESET:
            return {
                ...state,
                count: 0,
            };
        default:
            return { ...state };
    }
}

액션을 조금 더 쉽게 dispatch하기 위해 아래와 같은 헬퍼 함수들을 이용할 수 있다.

  • init : store의 값을 count로 초기화
  • increment : store의 count를 1증가 또는 1로 설정
  • reset : store의 count를 0으로 초기화
function init(count) {
    store.dispatch(actionCreator(INIT, { count }));
}

function increment() {
    store.dispatch(actionCreator(INCREMENT));
}

function reset() {
    store.dispatch(actionCreator(RESET));
}

아래와 같이 실제 store를 생성하고 subscribe 함수를 이용해 로깅 또한 진행할 수 있다.

  1. createStore 함수로 스토어를 생성
  2. getState 함수를 사용해 값을 출력하는 함수 작성
  3. 스토어subscribe 메서드를 이용해 2번에서 작성한 함수로 구독
const store = createStore(reducer);

function update() {
    console.log(store.getState());
}

store.subscribe(update);

아래와 같은 방법으로 actiondispatchstore에 저장된 상태를 변경할 수 있다.

  1. typepayload을 포함한 액션dispatch
  2. 액션을 반환하는 actionCreator 헬퍼 함수를 사용한 후 dispatch
  3. payload를 전달받아 dispatch까지 해주는 헬퍼 함수 이용
store.dispatch({ type: INCREMENT }); // { count: 1 }
store.dispatch(actionCreator(INCREMENT)); // { count: 2 }
increment(); // { count: 3 }

store.dispatch({ type: INIT, payload: { count: 5 } }); // { count: 5 }
store.dispatch(actionCreator(INIT, { count: 0 })); // { count: 0 }
init(10); // { count: 10 }

store.dispatch({ type: RESET }); // { count: 0 }
store.dispatch(actionCreator(RESET)); // { count: 0 }
reset(0); // { count: 0 }

actionCreator, increment, reset함수는 액션을 조금 더 간단히 dispatch하도록 도와주는 헬퍼함수다.

위와 같이 우리가 사용하던 redux와 비슷하게 작동하는 것을 볼 수 있다.

2회차를 하면서 평소 doc에 있던 redux를 어떤식으로 만들었는지 알 수 있게 되는 강의였습니다.

github 주소:https://github.com/jangwonyoon/Woowahan_Tech_Learning_React_Typescript/blob/master/record/week_1/Thursday.md

profile
어제보다 오늘 더 노력하는 프론트엔드 개발자

0개의 댓글