자바스크립트 전역 스코프에 정의된 것은 코드 내의 어디서든지 접근이 가능하다는 것이 문제가 되고는 합니다.

이때 외부에 공유되면 안되거나 충돌날 위험이 있는 경우 즉시 실행 함수를 통해 해결하고는 합니다.

 

즉시 실행 함수(IIFE)

단순하게 함수와의 차이를 보자면 즉시 실행되는지 아닌지의 차이입니다.

함수표현식은 함수를 정의하고, 변수에 함수를 저장하고 실행하는 일련의 과정이 있습니다.

하지만 즉시실행함수는 이러한 과정없이 즉시 실행됩니다.

문법의 차이는 단순히 함수를 괄호 "()"로 랩핑한다는 것입니다.

 

형태

(function() {
    console.log('즉시 실행 함수'); 
}());

var now = (function () { 
    console.log('즉시 실행 함수')    
})();

 

익명, 기명 즉시 실행 함수

(function () { 
	console.log('익명 즉시 실행 함수'); 
}());

(function now() { 
	console.log('기명 즉시 실행 함수'); 
}());

 

사용이유

자바스크립트의 모듈 패턴에서 사용할 수 있습니다.

전역 스코프에 정의하는 것이 아닌 즉시실행함수의 스코프에서 실행컨텍스트가 활성화됩니다.

이렇게되면 유효범위가 다른 곳에서 접근하려고 할때 은닉해주는 성질을 지닐 수 있습니다. (캡슐화)

물론 아예 접근이 안되는 것은 아닙니다. 접근하는 방법은 있습니다.

 

즉..

즉시실행함수로 일반적인 유효범위를 설정하는 언어에서와 같이 private와 public등의 캡슐화를 사용할 수 있습니다.

캡슐화를 해야하는 이유는 무엇일까요? 당연히 충돌을 방지하기 위해서입니다.

즉시실행함수는 플러그인이나 라이브러리에서 많이 사용됩니다.

 

예시

jQuery 라이브러리에서 $라는 전역변수를 사용하고 있는데, 또 다른 라이브러리를 도입하게되었을 경우...

추가 된 라이브러리도 $라는 전역변수가 있으면 충돌이 생기게됩니다.

이때 

즉시 실행 함수를 사용하여 $ 전역 변수의 충돌을 피할 수 있습니다.

(fucntion($) {
 	//.....
})(jQuery); // jQuery 인자로 전달

 

 

 


참고 

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/function

redux-saga 캡쳐

React는 MVC패턴에서 V(View)를 담당하고 있습니다.

앱이 가벼운 경우에는 State로 데이터를 핸들링 할 수 있지만,

앱이 커질 경우에는 데이터 관리를 별도로 해야하는 요구사항이 생기게됩니다.

이때 사용할 수 있는 것이 Redux와 MobX 등, 상태 관리 라이브러리입니다.

 

redux-saga는 redux의 액션 생성자와 reducer의 순수성을 유지하고 사이드 이펙트를 처리하기 위해 사용합니다.

여기서 사이드 이펙트는 부작용을 발생시키는 어떠한 효과가 아니라,

데이터 요청이나 비동기 작업, 브라우저 캐시 같이 순수하지 않은 작업들을 의미합니다.

 

 

Redux-Saga란?

redux-saga는 리덕스의 미들웨어입니다.

리덕스가 액션을 수행하면 redux-saga에서 디스패치하여 redux의 액션을 가로챈 뒤, 액션의 역할을 수행 하고

다시 액션을 발행하여 데이터를 저장하거나 다른 이벤트를 수행시킵니다.

redux-saga 동작 이미지 출처 uzihoon.com/post/181be130-63a7-11ea-a51b-d348fee141c4

 

redux-saga와 비슷한 라이브러리로 redux-thunk가 있지만,

redux-saga는 redux-thunk에 비해 이펙트 함수를 통해 다양한 작업들을 처리할 수 있습니다.

redux-saga가 좀 더 러닝커브가 높습니다.

  • 비동기 작업 시 기존 요청 취소 처리 가능
  • 특정 액션 발생 시 다른 액션이 디스패치 되도록 할 수 있음
  • 웹 소켓 사용 시 channel 기능을 사용하여 효율적 코드 관리가 가능
  • API 요청이 실패 했을때 재 요청하는 작업을 할 수 있음

이 외에도 다양한 비동기 작업들을 처리 할 수 있습니다.

 

 

자주 사용하는 헬퍼 함수

redux-saga는 스토어에 지정된 액션들이 디스패치되었을 때,

task를 만들기 위해 내부 함수들을 감싸는 헬퍼 이펙트를 제공합니다. 

 

all

제너레이터 함수를 배열의 형태로 인자로 넣어주면 병렬로 동시에 실행됩니다.

이때 모든 함수에 대한 결과가 resolve될 때까지 블럭됩니다. 

 

put

특정 액션의 디스패치하도록 합니다.

결과를 스토어에 디스패치(put) 합니다.

 

call, apply

순수 객체만 리턴하는 함수입니다. 오브젝트 메소드 호출을 지원합니다.

첫번째 파라미터는 함수이며 나머지 파라미터는 해당 함수에 넣을 인수 값 입니다.

액션이 발생하면 전달한 함수를 호출하여 실행합니다.

API가 리턴될때까지 블럭되며, 비동기 함수 호출 시 용이합니다.

call과 apply는 두번째 인자 값의 차이만 있습니다.

 

delay

설정된 시간 이후에 resolve를 하는 Promise 객체를 리턴합니다.

제너레이터를 정지하는데 사용할 수 있습니다.

 

takeEvery

액션이 발생하게되면 task를 실행합니다.

task가 종료되기 전에 또 다른 액션이 발생할 경우, 또 하나의 새로운 task를 실행합니다.

 

takeLatest

액션이 발생하게되면 task를 실행합니다.

만약 실행 중인 task가 있다면 기존 task를 종료하고 새로운 task를 실행합니다.

실수로 여러번 클릭했을때를 방지하거나 마지막에 요청된 데이터를 보여줄 때 사용합니다.

 

takeLeading

액션이 발생하게되면 task를 실행합니다.

해당 task의 실행이 완료되기 전까지 뒤에 오는 이벤트들을 블럭합니다.

이후 task가 완료되면 액션에 대해 수신합니다.

 

throttle

초 이내 요청을 한 번만 보냅니다.

마지막 함수가 호출된 후 일정 시간이 지나기 전 재 호출하지 않습니다.

스크롤 이벤트 사용 시 용이합니다.

 

debounce

초 이내 요청을 한 번만 보냅니다.

처음 함수나 마지막 함수만 호출 후 일정시간이 지나기 전 재 호출하지 않습니다.

 

 

제너레이터함수

ES6에 포함된 제너레이터 함수는 function* 키워드로 작성합니다.

redux-saga는 이 제너레이터 함수를 적극 활용한 사례입니다.

제네레이터는 제네레이터함수의 반환이며 redux-saga가 작성한 함수를 호출하여 반환받는 객체가 제너레이터입니다.

*이터레이터 프로토콜과 이터러블 프로토콜을 준수합니다.

// 코드출처: https://mskims.github.io/redux-saga-in-korean/basics/DeclarativeEffects.html 
import { takeEvery } from "redux-saga/effects"
import Api from "./path/to/api"

function* watchFetchProducts() {
  yield takeEvery("PRODUCTS_REQUESTED", fetchProducts)
}

function* fetchProducts() {
  const products = yield Api.fetch("/products")
  console.log(products)
}

yield를 이용하여 이펙트들을 호출하고 수행된 내용을 다시 돌려 받아 그 액션을 수행합니다.

redux-saga는 제네레이터를 통해 이펙트를 수행하는 역할을 합니다.

 


참고, 출처

이터러블 (Iterable)

순회가능한 객체를 말합니다. 

순회 가능한 객체는 Symbol.iterator 심볼 속성을 가지고 있으며 이터레이터 객체를 반환하는 객체를 말합니다.

이러한 것을 이터러블 프로토콜이라고 하며 이터러블 객체라고 합니다.

 

이터레이터 (Iterator)

이터러블 메소드로 반환하는 객체입니다.

next 메소드를 구현하고 있으며 value, done을 반환하는 객체입니다.

반환되는 IteratorResult는 {done: boolean, value: any} 형태의 단순한 객체입니다.

next 메소드를 통해 모든 값을 돌고 나면 done이 true로 나오며 끝납니다.

한번 끝난 이터레이터는 다시 돌아가지 않으며 value가 undefined로 리턴합니다

이러한 것을 이터레이터 프로토콜이라고 합니다.

 

for of 루프는 순회 시작 전 [Symbol.iterator]() 메소드를 호출하여 이터레이터 객체를 얻습니다.

그 후에 순차적으로 next 메소드를 호출하며 하나씩 순회합니다.

보통 이터러블 프로토콜과 이터레이터 프로토콜을 하나의 객체에 모두 구현하는 것이 일반적입니다.

// 영원히 0을 반환하는 무한 이터레이터 예시
var zeroesForeverIterator = {
    [Symbol.iterator]: function () {
        return this;
    },
    next: function () {
        return {done: false, value: 0};
    }
};

 

사용자 이터레이터 객체 생성

이터러블 프로토콜은 obj[Symbol.iterator]: Function => Iterator로 표현할 수 있습니다.

그 후 이터레이터 프로토콜을 따라 value, done가 들어있는 오브젝트를 반환하는 next 메소드를 가진 객체를 반환하면 됩니다. 

const iterable1 = {};

iterable1[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

console.log([...iterable1]); // Array [1, 2, 3]

 

제너레이터 (Generator)  

이터러블, 이터레이터 객체를 만드는 손 쉬운 방법입니다.

동작은 function* 문법을 사용해서 작성하며 next를 통해 실행합니다.

next 메소드를 통해 실행이 되며 yield 문을 만나면 정지합니다. 

next 메소드를 호출함으로써 값을 소비할 수 있습니다. 

function* 문법을 통해 함수가 생성되고 최초로 호출될때 함수 내부의 어떠한 코드도 실행되지 않습니다.

이때 생성자 함수가 반환되는데 이것을 제너레이터 함수라합니다. 그 후 값을 소비하며 함수로 부터 반환되는 객체를 제너레이터라고 합니다.

 

즉, 제너레이터 객체는 이터레이터 객체입니다. 

(이터레이터 프로토콜을 따르고 있습니다.)

 

예시

값이 더이상 업을 경우 value: undefined, done: true가 반환된 것이 확인됩니다.

function* generatorFunc() {
  yield 1;
  yield 2;
  yield 3;
}

const it = generatorFunc();

console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

 

제너레이터 적극 활용한 예로 redux-saga가 있습니다.

 

 

 

 


 

참고 및 정의 출처

리액트 생명주기

 

리액트 컴포넌트는 생성(Mount), 갱신(Update), 제거(Unmount) 주기를 가지게됩니다.

해당 주기에서 사용되는 메소드에 대해 알아보겠습니다.

 

 

생성(Mount)

constructor(props)

메소드를 바인딩 하거나 state를 초기화하는 작업이 없을 경우 사용합니다.

만약, 해당 작업이 없다면 사용하지 않아도됩니다.

 

getDerivedStateFromProps(nextProps, prevState)

props로 받아 온 값을 state로 넣어주고 싶을때 사용합니다.

state를 변경할 때는 setState가 아닌 반환 값으로 변경해야하며 반환 값이 null일 경우에는 아무일도 발생되지 않습니다.

 

render()

반드시 구현되어야하는 유일한 메소드입니다.

이 메소드가 호출되면 this.props와 this.state의 값을 활용하여 값을 반환합니다.

render는 컴포넌트의 state를 변경하지 않고 호출될 때마다 동일한 결과를 반환합니다.

해당 메소드는 브라우저와 직접적으로 상호작용하지 않습니다.

 

componentDidMount(prevProps, prevState)

컴포넌트가 생성된 직후에 호출됩니다. (즉, render 후에 호출)

setState를 통해 작업하면 render가 두번 호출되므로 DOM 노드가 있어야하는 초기 작업에 사용하는 경우를 제외하고는 setState를 사용하지 않습니다. (= state의 초기화는 constructor에서 작업합니다.)

데이터 구독을 설정하기 좋은 위치이며 componentWillUnmount()에서 구독 해제 작업을 반드시 수행해야합니다. (timer, fetch, axios 등)

 

 

갱신(Update)

getDerivedStateFromProps(nextProps, prevState)

생성과 동일

 

shouldComponentUpdate(nextProps, nextState)

컴포넌트 업데이트 직전에 호출되는 메소드입니다.

props  state가 변경 되었을 때 리렌더링 여부를 반환 값으로 정합니다. 

*사용을 잘 안하는 이유

리액트 컴포넌트의 기본 동작은 매 state의 변화마다 다시 렌더링을 수행하는 것이기 때문에 잘 사용되지 않습니다.

해당 메소드는 성능 최적화를 목적으로 두고 있기 때문에 렌더링을 방지하는 목적으로 사용할 경우 버그로 이어질 수 있다. shouldComponentUpdate가 false를 반환하면 리렌더링이 일어나지 않으므로 그 다음 순서의 메소드들은 호출되지 않습니다.

 

render():

생성과 동일

 

getSnapshotBeforeUpdate(prevProps, prevState)

렌더링 결과가 실제 돔에 반영되기 직전에 호출됩니다.

메서드의 이름에서처럼 업데이트 되기 직전에 snapshot(props & states)을 확보하는게 목적입니다.

반환 값이 componentDidUpdate의 세번째 인자로 전달됩니다.

 

componentDidUpdate(prevProps, prevState, snapShot)

최초 렌더링에서는 호출되지 않으며 돔 요소가 업데이트 된 이후 호출되는 메소드입니다. 

getSnapshotBeforeUpdate를 구현했고 리턴 값이 존재한다면 세번째 인자로 넘겨받으며 반환값이 없다면 해당 인자는 undefined입니다.

setState를 사용할 수 있지만 조건문으로 감싸지 않는 경우 무한루프가 되는 경우가 발생할 수 있으므로 주의해야합니다. 이전과 현재의 props를 비교하여 네트워크 요청을 보내야할 경우 이 메서드를 사용합니다.

 

 

제거(UnMount)

componentWillUnmount()

컴포넌트가 DOM에서 삭제된 후 실행되는 메소드로 소멸단계에서 호출되는 유일한 메소드입니다.

componentDidMout에서 생성된 작업등을 정리할 때 사용합니다. (timer, fetch, axios 등)

실행 직후에는 이제 컴포넌트가 리렌더링 되지 않으므로 setState를 사용하면 안됩니다.

 

 

오류처리 (공식문서)

제어 흐름 조작하는데 사용하면 안되며 해당 메소드들은 자기 자신(컴포넌트)에 대한 오류를 감지할 수 없고 하위에 존재하는 컴포넌트에 대한 오류만을 감지할 수 있습니다.

 

getDerivedStateFromError(error)

에러가 발생한 뒤 UI 렌더링을 제어하기 위해 사용한다.

 

componentDidCatch(error, errorInfo)

에러 정보를 기록하려고 사용합니다.

*공식사이트에서 설명하길, 해당 메소드에서 setState를 통해 UI를 렌더링 할 수 있으나 추후 릴리즈에서 사용할 수 없게할 것이라고 적혀있습니다.

 

 


 

참고

+ Recent posts