위 코드는 button을 클릭하면, wallet과 wallet의 address, private key, 그리고 public key를 생성할 수 기능이다.
generate() 함수를 사용하여, wallet을 생성한다. 생성한 wallet의 getAddress() 함수를 이용하여 주소값을 만드는데, 이더리움은 ‘0x’를 포함한 hex string으로 값을 만든다. 그래서, toString(‘hex’)와 앞에 ‘0x’를 붙여 address 값을 만든다.
생성한 wallet의 getPrivateKey() 함수와 getPublicKey() 함수를 이용해서 private key와 public key를 생성한다.
2. 지갑의 balance 조회
이더리움 지갑의 balance를 조회하기 위해서는 이더리움에서 제공하는 web3 라이브러리를 사용해야 한다.
web3를 사용하기 위해서는 우선, new 연산자를 이용해 web3 인스턴스를 생성하고, HttpProvider를 이용해서 이더리움 네트워크와 연결해야 한다.(위 코드는 이더리움의 테스트넷인 Ropsten의 infura API를 사용하여 infura에서 얻은 API_KEY로 노드를 연결했다.)
만약, Private 블록체인으로 연결하고 싶다면, localhost:8545로 연결하면 된다.
web3.eth의 getBalance() 함수를 이용해 내가 가진 지갑주소의 balance를 확인할 수 있다.
getTransactionCount() 함수를 사용하여 nonce값을 구하는데, getTransactionCount() 함수 안에 인자 txCount가 nonce 값이다. nonce값이란, 해당 transaction을 발생시키는 account가 발생시킨 총 transaction의 수를 말한다.
2. transaction 생성
1 2 3 4 5 6 7 8 9 10 11 12 13
import Tx from 'ethereumjs-tx';
// 1. transaction 생성에 필요한 txObject 생성 const txObject = { nonce: window.web3.utils.toHex(txCount), to: account, gasLimit: window.web3.utils.toHex(1000000), gasPrice: window.web3.utils.toHex(window.web3.utils.toWei('10', 'gwei')), value: window.web3.utils.toHex(window.web3.utils.toWei('0.01', 'ether')) };
// 2. 'ethereumjs-tx' 라이브러리를 이용해 tx 인스턴스 생성 const tx = new Tx(txObject)
transaction을 생성하기 위해 필요한 txObject를 만들어야 하는데, 이 txObject에는 위와 같은 값들이 들어간다. web3의 utils에 있는 toHex() 함수를 이용해 값들을 hex 값으로 만들었는데, 위 코드의 값들은 예시로 작성한 것이다.
참고로, toWei() 함수는 인자로 들어간 값을 Wei 단위로 변환한다는 뜻이다. 예를 들어, window.web3.utils.toWei(‘10’, ‘gwei’)이면, 10 gwei 값을 wei 값으로 변환하라는 뜻이다.
위에서 만든 txObject를 가지고 transaction을 생성하기 위해서는 ethereumjs-tx 라이브러리를 사용해 tx를 생성한다.
// a private key of the sendAccount const privateKey = 'PK'; const _privateKey = Buffer.from(privateKey, 'hex'); tx.sign(_privateKey)
private key로 서명하기 위해서는 private key 값을 buffer 타입으로 변경하여 사용해야 한다.
buffer 타입이란, Unit8Array 타입으로, 값들이 배열(‘[]’)에 들어가 있다. 처음에 generate() 함수로 wallet을 생성했을 때, 그 wallet 안에 들어있는 private key 값의 타입은 Unit8Array 타입으로 되어있다. 이 private key 값을 위에서 hex 값으로 변경해서 사용했었는데, 이 값을 다시 buffer 타입으로 변경해서 Sign 할 때 사용해야 한다.
4. transaction 보내기
서명된 transaction을 serialize하여 sendSignedTransaction() 함수를 이용해 transaction을 보낸다.
Serialization이란, 네트워크를 통해 데이터를 주고 받을 때, 서로간에 공유하는 규칙이 있는데, 이 규칙에 맞게 데이터를 출력하는 것을 말한다.
sendSignedTransaction() 함수를 사용해 얻은 txId 값은, 이 transaction의 txHash값이 되고, 즉 transaction id가 된다. 이 txHash값으로 Ether Scan에서 이 transaction에 대한 거래내역을 확인할 수 있다.
이더리움 네트워크에서는 트랜잭션이 발생할 때마다, 이에 대한 신뢰 여부를 채굴자들의 합의하에 블록을 생성하여 모든 트랜잭션을 처리한다. 이렇게 트랜잭션을 처리하게 되면, 관련된 Account의 상태가 변화하게 된다. 즉, 이더리움은 이와 같이 상태가 변화하는 상태 전이 과정을 기반으로 작동한다.
트랜잭션 발생 -> 블록 생성 -> 관련된 Account의 상태 변화
모든 Account의 상태 정보는 블록과 블록 내에 연결된 머클 패트리시아 트리로 저장되고 관리된다.
상태 전이(State Transition)
상태 전이란, 특정 시점의 현재 상태 S가 상태 전이 함수(APPLY())에 의해 다른 상태(S’)로 전이되거나 전이에 실패하고 이전 상태로 복귀되는 것을 말한다. Account의 상태는 상태 전이 함수에 의해 전이된다.
APPLY(S, TX) -> S’ [S: 현재 상태] -> (APPLY: 상태 전이 함수) -> [S’: 전이된 상태 또는 실패]
이더리움의 상태(State)는 복수의 상태 변이를 갖지 못하고, 단 하나의 상태 변이만을 갖는다. 즉, Account의 특정 시점의 한 상태는 상태 전이 함수를 통해 단 하나의 상태(single state)로만 전이된다는 것이다. 만약, 하나의 상태가 여러 개의 상태로 전이된다면, 채굴자들은 어떤 상태가 맞는 것인지 판단하고 합의 할 수 없다.
ICONex에서 송금할 때, data를 입력할 수 있다. 여기서 말하는 data는 message를 말하는데, 전달할 message가 있다면 작성할 수 있고, 기본적으로 optional이다.
입력하는 data의 타입은 UTF-8과 HEX 2가지다.
UTF-8
UTF-8이란, 어떤 언어의 문자든 유니코드(unicode)로 인코딩하는 방식으로, 여러 방식이 있으나 이 방식을 가장 많이 사용한다.
유니코드(unicode)란, 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하는 표준 코드를 말한다.
인코딩이란, ‘부호화하다’라는 의미로, 이를 컴퓨터 공학에서 정의하면, 입력 데이터를 컴퓨터속에서 사용하는 코드로 변환하는 것을 말한다.
즉, 문자나 기호들의 집합을 컴퓨터에 저장하거나 통신에 사용할 목적으로 부호화하는 방법이다.
디코딩이란, ‘복호화하다’라는 의미로, 부호화(Encoding)된 정보를 부호화되기 전으로 되돌리는 처리 방식을 말한다.
정보를 표현하기 위한 글자들의 집합을 문자집합(Character Set)이라고 하고, 다음의 2가지 종류가 대표적인 표준코드이다.
ASCII코드
유니코드
ASCII코드는 영문 알파벳을 사용하는 대표적인 문자 인코딩된 표준 코드이다. 그러나, 한글 등 다른 모든 언어를 사용하기 위해서는 이 방식보다 다른 방식을 더 사용한다. 그것은 바로, 유니코드이다.
유니코드란, 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준코드이다.
내가 제작하려고 하는 웹애플리케이션 또는 웹페이지에서 다국어를 지원하기 위해서는 사용된 문자를 유니코드로 인코딩 해야하고, 이때 인코딩하는 방식 중 UTF-8를 주로 사용한다. UTF-8를 사용하면, 전 세계 모든 문자를 표현할 수 있고, 한 문자를 표현하기 위해 1바이트에서 4바이트까지 사용한다.
웹 개발을 할 때, html에서 다음과 같이 작성한다.
1 2 3
<head> <meta charset=UTF-8" /> </head>
위와 같이 작성하면, html 문서의 문자는 UTF-8 방식으로 인코딩하여 유니코드로 변환한다는 뜻이다. 만약, 위와 같이 작성하지 않으면, html 문서를 브라우저에서 구현할 때 한글 등 다른 언어들이 깨져 보일 수가 있다.
참고로, Gwei는 가스(이더리움 네트워크 거래 수수료) 비용을 계산할 때, 주로 사용한다.
ICX의 기본단위
ICON도 이더리움과 비슷한 개념을 사용한다.
ICON의 가장 작은 단위는 loop 이다.
Name
Description
1 icx
10^18 loop(=== 1*e^18 loop)
1 icx
10^9 Gloop(=== 1*e^9 Gloop)
1 Gloop
10^9 loop(=== 1*e^9 loop)
참고로, Gloop는 step(ICON 네트워크 거래 수수료) 비용을 계산할 때, 주로 사용한다.
Ethereum Fee System
이더리움에서 어떤 1개의 트랜잭션을 실행하는 데 사용자가 지불해야하는 수수료를 최대 트랜잭션 실행 비용(Max Transaction Fee)이라고 한다.
Max Transaction Fee(최대 트랜잭션 실행 비용) = Gas Limit(가스 총량) * Gas Price(가스 가격)
Gas Limit(가스 총량)이란, 트랜잭션을 실행하는데 예상되는(추정되는) 총 가스량을 말한다.
Gas Price(가스 가격)이란, 1 가스당 가격을 말한다.
즉, 어떤 사용자가 1개의 트랜잭션을 수행하려고 하면, 그 트랜잭션을 수행하는 것에 대한 수수료(최대 트랜잭션 실행 비용)를 지불해야 하는데, 그 수수료는 Gas Limit(가스 총량) * Gas Price(가스 가격)으로 계산되어진다.
이때, Gas Limit(가스 총량)은 각 트랜잭션에 필요한 총 가스량을 추정(estimated)해서 계산된 값이 나오고, Gas Price(가스 가격)은 1 가스당 가격으로 이 가격이 높을수록 마이너들이 보상을 많이 받기 때문에, 트랜잭션이 빨리 처리가 된다. 즉, 트랜잭션 실행 속도가 빠르게 된다.
Step Limit(스텝 한도)이란, 트랜잭션을 실행하는데 예상되는(추정되는) 총 스텝양을 말한다.
Step Price(스텝 가격)이란, 1 스텝당 가격을 말한다.
즉, 어떤 사용자가 1개의 트랜잭션을 수행하려고 하면, 그 트랜잭션을 수행하는 것에 대한 수수료(최대 트랜잭션 실행 비용)를 지불해야 하는데, 그 수수료는 Step Limit(스텝 한도) * Step Price(스텝 가격)으로 계산되어진다.
ICON에서 송금 등의 트랜잭션 실행을 할 때, 기본적으로 Step Limit이 100000이다. 이 값은 default로 사용자가 Step Limit의 값을 변경할 수 있다. 그러나, Step Limit의 최대값과 최소값은 정해져 있어서 그 범위안에서만 변경이 가능하다.
Step Price는 0.00000001 ICX (10 Gloop)로 고정되어있다.
어떤 트랜잭션을 실행하려면, 사용자 어카운트에 있는 icx 잔액이 최대 트랜잭션 실행 비용보다 많아야 한다.
만약 적거나, 트랜잭션 실행 도중에 step을 모두 사용하면, 해당 트랜잭션은 중단되고 이전 상태로 복귀되지만, 사용된 step은 발신자에게 반환되지 않는다.
만약 step limit을 잔액보다 높게 설정하더라도, 해당 트랜잭션에 필요한 step 만큼만 소진된다.
Side effect란, 애플리케이션의 부작용들(데이터 요청 등의 비동기 작업/브라우저 캐시 같은 순수하지 않은 것들)을 말한다.
middleware란, 액션이 dispatch 되어서 리듀서에서 이를 처리하기 전에 그 사이에서 중간자로써 지정된 특정 작업을 하는 것을 말한다.
Redux-Saga를 왜 사용하는가?
비동기 처리 등 순수하게 액션만을 전달하는 것 이상으로 발생하는 상황들(side effects)을 다룰 때, 이 미들웨어를 사용한다.
Redux-Thunk 대신에 Redux-Saga를 사용하는 이유는 무엇인가?
redux-thunk를 사용하면, 비동기 처리를 Action Creator(액션 생성자 함수)에서 작성하게 되어, 로직이 복잡해진다. 기본적으로 Redux에서 Action Creator에서는 액션을 객체형태로 생성하여 dispatch 하는데, redux-thunk에서는 액션을 함수형태로 생성하여 dispatch 하여, Action Creator에 비동기처리 코드나 관련된 로직이 들어가게 된다.
그런데, Redux-saga는 비동기 처리를 기술하는 전용의 방식인 task로 쓰여있다. 즉, 비동기 처리를 각 saga(task)에서 명령을 내리고, 동작을 미들웨어에서 처리한다. 그래서, redux에서의 action creator에서 기존과 그대로 액션을 객체형태로 생성하여 dispatch 할 수가 있고, saga에서의 코드도 더 간단해진다. 결국, Action Creator는 본래의 모습을 되찾아, action 객체를 생성하여 돌려주는 순수한 상태로 돌아가게 된다.
결론적으로, Redux-Saga를 사용하게 되면, 비동기 처리를 액션 생성자로부터 분리하여 작성하기 때문에, 가독성도 좋고 콜백 처리 등에 더 수월하다.
어떤 비동기 액션이 dispatch 되면, Reducer에 먼저 도달한다. 정확하게는 액션이 Reducer로 지나가는 것을 본 후, Redux-Saga에 액션을 처리한다. Reducer는 순수 함수라는 규칙이 있기 때문에, 비동기 액션을 처리하지 못한다.
이 비동기 액션을 Redux-Saga에 있는 watcher saga가 보고 있다가, watcher saga에 등록되어 있는 task를 수행한다. 이 watcher saga의 역할은 어떤 비동기 액션이 dispatch 되면, 어떤 task를 수행하도록 등록하는 것이다. 이때, takeEvery라는 헬퍼 이펙트를 사용하는데, 이 이펙트는 여러개의 task를 동시에 시작할 수 있다. 즉, 1개 혹은 아직 종료되지 않은 task가 있더라도 새로운 task를 시작할 수 있다.
위 task가 수행될 때, 먼저 첫번째 줄에 있는 delay(2000)이 yield 된다. delay는 설정된 시간 이후에 resolve를 하는 Promise 객체를 리턴하는 함수이다. 이 Promise가 미들웨어에 yield 될때, 미들웨어는 Promise가 끝날때까지 Saga를 일시정지 시킨다.(generator 함수의 특징) 즉, 이 부분은 동기적으로 동작한다.
2초후, Promise가 한번 resolve 되면, 미들웨어는 saga(task)를 다시 작동시키면서, 다음 yield까지 코드를 실행한다. 이런 방식으로 saga(task)에서 1개씩 yield되고, 그 yield 된 것이 미들웨어에 의해 동작이 완료되면, 그때 그 다음줄에 있는 객체가 yield된다.
어떤 객체를 yield할때, 앞에 오는 것들(put, call 등)을 이펙트라고 한다. 이펙트란, 미들웨어에 의해 수행되는 명령을 담고있는 간단한 자바스크립트 객체를 말한다. 미들웨어가 saga에 의해 yield 된 이펙트를 받을때, saga는 이펙트가 수행될때까지 정지되어 있다.
saga에서 put을 통해 객체를 dispatch하면, 이 객체는 reducer로 가게된다.
두 saga를 모두 한번에 실행하게 해주기 위해, rootSaga를 이용해 사용한다.
1 2 3 4 5 6 7
// 모든 Saga들을 한번에 시작하기 위한 단일 entry point 이다. export default function* rootSaga() { yield all([ incrementAsync(), watchIncrementAsync() ]) }
Redux-Saga 사용방법
Redux-Saga를 사용하는 방법은 다음과 같다.
saga 미들웨어 작성(sagas.js)
비동기 처리를 위한 task 작성(제너레이터 함수) - worker saga
각각의 어떤 비동기 액션을 처리하기 위해 watch 함수를 작성하고, 그 안에 takeEvery 이펙트 펠어 함수를 사용 - watcher saga
모든 saga들을 한번에 실행하기 entry point 작성 - rootSaga 작성
rootSaga에 들어있는 두 saga가 호출된 결과의 배열을 yield 한다. 이것은 생성된 두 제너레이터가 병렬로 시작된다는 것을 의미.
import React from 'react'; import ReactDOM from 'react-dom'; import { store } from 'redux/store/store'; import { Provider } from 'react-redux'; import App from 'app/App.js';
import { createStore, applyMiddleware, compose } from 'redux'; import createSagaMiddleware from 'redux-saga'; import rootReducer from 'redux/reducers/rootReducer'; import rootSaga from 'redux/sagas/rootSaga'; import persistState from 'redux-localstorage'; import { composeWithDevTools } from 'remote-redux-devtools';
[8] store.js에서 store 생성후, index.js의 Provider에 제공한다.
1 2 3 4 5 6 7 8 9 10 11 12 13
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import { store } from 'redux/store/store'; import { Provider } from 'react-redux'; import App from 'app/App.js';
Web Worker란, script 실행을 main thread(UI thread)가 아니라 background thread에서 실행할 수 있도록 해주는 API이다.
*thread란, 어떤 프로그램 내에서 실행되는 흐름의 단위로, 보통 한 프로그램이 하나의 thread를 가지고 있다. 하나의 thread를 갖는다는 의미는, 어떤 이벤트(행위)가 발생했을 때, 그 하나의 이벤트를 처리할 때까지 다른 일은 못하게 된다는 뜻이다. 즉, 한번에 하나씩 처리한다는 뜻이다.
둘 이상의 thread를 동시에 실행하는 방식을 multi thread라고 한다. 웹 브라우저상의 javascript는 싱글 thread이기 때문에, 한번에 하나의 일 밖에 처리를 못한다. 그래서, 웹페이지에서 script가 실행되면, 해당 웹 페이지는 실행 중인 script가 종료될 때까지 응답 불가 상태, 즉 다른 일을 처리할 수가 없다.
이때, Web Worker를 사용하면, 시간이 오래 걸리는 javascript 작업 등을 background에서 처리하게 하여, 사용자의 UI를 방해하지 않고, 작업을 수행할 수 있다. 즉, script의 multi thread가 가능하게 된다.
Web Worker를 왜 사용하는가?
Web Worker를 사용하는 이유는, script 작업이 복잡하여 시간이 오래 걸리는 경우에도, 사용자의 다른 UI 작업에 방해를 주지 않기 위한 multi thread가 가능하기 때문이다.
Web Worker의 활용
사용자의 UI 작업(UI thread)에 방해 없이 계속 수행해야하는 작업이 있을 경우
background에서 오랜시간 동안 작업해야 하는 경우
원격에 있는 리소스에 대한 액세스 작업(localstorage를 액세스 하는 경우)이 있을 경우
복잡한 수학적 계산 작업이 있을 경우
Web Worker를 사용하기 예전 상황
스크립트가 수행을 하는데 오랜 시간이 걸릴 때, 브라우저는 무반응 스크립트에 대한 경고를 보여줌
Main script가 실행되는 HTML 파일(웹페이지)과 background script가 실행되는 Worker가 있다. HTML 파일(웹페이지)과 Worker간의 데이터를 서로 주고 받으면서, script가 실행되는 방식이다.
이때, 데이터를 송신할 때는 postMessage 메서드를 사용하고, 데이터를 수신할 때는 onmessage 이벤트 핸들러를 사용한다. HTML 파일(웹페이지)에서 데이터를 송신하거나 수신할 수 있고, Worker에서도 데이터를 송신하거나 수신할 수 있다.
postMessage는 다수의 window 창간의 정보교환을 목적으로 사용한다. workers는 DOM에 대한 접근 권한이 없어서 직접 웹페이지를 조작이 불가능하다.
Web Worker의 기본적인 사용방법
코드상으로 Web Worker를 사용하기 위해서는 다음과 같이 해야한다.
Worker 실행 파일(worker.js) 작성
Worker를 호출할 HTML 파일(웹페이지)에서 Worker를 호출
Worker 종료
Worker 실행 파일(worker.js) 작성
Worker가 실행할 script, 즉 javascript(worker.js) 파일을 만들고 작성한다. 이때, on message 이벤트 핸들러와 postMessage 메서드를 같이 작성한다. 왜냐하면, HTML 페이지로부터 전달 받을 데이터를 onmessage 이벤트 핸들러로 수신받아 처리한 후, postMessage 메서드로 다시 HTML 페이지에 전달해줘야 하기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12
// worker.js
//웹페이지로부터 메세지를 수신하여 시간이 오래 걸리는 작업 등을 처리 onmessage = function(e){ var receiveData = e.data;
//워커를 호출한 곳(웹페이지)으로 결과 메시지를 전송 if(receiveData) { var sendData = "I'm working for you" postMessage(sendData) } }
Worker를 호출할 HTML 파일(웹페이지)에서 Worker를 호출
New 연산자와 Worker 생성자 함수를 이용해 worker 인스턴스를 생성한다. 생성한 worker 인스턴스를 사용해서, Worker(worker.js)에 보낼 데이터를 postMessage 메서드를 이용해 작성하고, 후에, Worker(worker.js)에서 처리된 후 받을 데이터를 on message 이벤트 핸들러를 이용해 작성한다.
Pk를 암호화하기 위해 pw를 사용하는데, 이 pw를 직접 암호화키로 사용하지 않고, 이 pw를 암호화하여 암호화한 값을 암호화 키로 사용해서 pk를 암호화한다. 이때, pw를 암호화하는 알고리즘은 Scrypt인데, 비밀번호는 특성상 복호화할 필요가 없기 때문에 단반향 알고리즘 중 하나인 Scrypt를 사용한다.
Keystore 파일에 들어갈 때는, kdf: ‘Scrypt’ 이런식으로 들어가는데, kdf는 암호화 알고리즘 이름을 말하고, Scrypt 알고리즘을 사용한다는 뜻이다.
참고로, kdfparams 들어가는 것 중, n은 CPU/memory 비용을 말한다. 즉, 값이 클수록 암호화 파워가 증가한다.
ES6 class의 constructor에서 super 메소드를 사용하는 이유는, 자식 클래스에서 super 메소드를 사용함으로써 자식 class의 constructor 내부에서 부모 클래스의 constructor를 호출한다. 즉, 부모 클래스의 인스턴스를 생성해서, this를 사용할 수 있게 된다. 만약 super 키워드를 사용하지 않으면, this에 대한 참조 에러가 발생한다. 다시 말하면, super 메소드를 호출하기 이전에는 this를 참조할 수 없다는 것을 의미한다.
리액트에서 컴포넌트를 class형으로 만들때는, 모두 기본적으로 리액트에 있는 ‘Component’라는 부모 클래스로부터 상속받아 자식 클래스로 시작하게 된다. 그래서 자식 클래스에서는 super 메소드를 사용해서 , 부모 클래스(Component)의 constructor를 호출, 즉 Component 클래스의 인스턴스를 생성해서 this를 사용할 수 있게 되는 것이다. 그 다음에, this를 이용해 프로퍼티를 초기화할 수 있다.
정리하면, constructor를 사용한다면, 반드시 super()를 불러와야 한다. super()를 사용하지 않으면, this가 초기화되지 않는다.
그러면, super()에 props를 사용해야 하는가?
constructor 안에서 this.props에 접근하고 싶다면, super(props)를 사용한다. 만약, super() 이렇게만 사용하면 다음과 같이 나온다.
1 2 3 4 5 6
class MyApp extends React.Component{ constructor(props){ super(); console.log(this.props); // this.props is undefined } }
위 코드에서 super(props)라고 사용해야 this.props에 접근할 수 있다.
그러나, constructor가 아닌 다른 곳에서 this.props를 사용하고 싶으면, constructor 안에 props를 넘겨줄 필요가 없다. 왜냐하면, React에서 자동으로 세팅해주기 때문이다.
이 방법이 더 복잡하므로, class fields 문법으로 주로 사용하는 것이 더 편리!
componentWillMount
컴포넌트가 DOM에 삽입되기 전(Mount 되기 전)에 호출되는 API이다. 즉, render가 되기 전이기 때문에, 컴포넌트가 브라우저에 나타나기 전(화면에 나가기 전)에 호출되는 API이다.
사용 이유
mount 직전에 하고 싶은 것들(i.e. 방문자가 어떤 페이지를 방문했는지 Google Anlaytics에 신호할 때)이 있을 경우 사용
루트 컴포넌트에서 APP과 관련된 외부 API를 설정할 때 사용
React v16.3 이후부터는 해당 API가 deprecated 되어서, 사용하면 안된다. 기존에 이 API에서 하던 것들을 constructor와 componentDidMount에서 처리 가능하다.
componentDidMount
컴포넌트가 DOM과 삽입(Mount)되고, render가 된 이후에 호출되는 API이다.
이 API는 페이지가 첫 렌더링 될 때, 호출되는 API이다. 즉, 이 컴포넌트가 사용된 페이지가 처음 로드 될때, 또는 refresh로 다시 이 페이지가 불러올 때 등으로 인해 처음으로 렌더링 될 때, 호출되는 API이다.
사용 이유
DOM에 대한 접근이 필요한 모든 설정을 수행할 때 사용(DOM의 속성을 읽거나 직접 변경하는 작업 등)
해당 컴포넌트에서 필요로하는 데이터를 요청하기 위해 사용(axios, fetch 등을 통한 ajax 요청)
컴포넌트가 새로운 props를 받게됐을 때 호출되는 API이다. 즉, 새로운 props로 어떠한 작업을 수행하기 전에, 이 새로운 props를 인자로하여 이 API가 호출된다. 새로 받게될 props는 nextProps로 조회 가능하고, this.props는 현재의 props를 말한다.
사용 이유
props를 받아서 state를 변경해야 하는 경우 유용하다.
다음과 같이 작성할 수 있다.
1 2 3 4 5
componentWillReceiveProps(nextProps) { if (this.props.percent !== nextProps.percent) { this.setUpPercent(nextProps.percent); } }
이 API는 update가 발생하기 전 어떤 작업이 필요한 경우 사용하는 API로써, shouldComponentUpdate에서 true를 반환했을 때만, 호출되는 API이다. shouldComponentUpdate가 이미 사용되고 있는 컴포넌트에서 componentWillReceiveProps를 대신 사용한다. 이 API가 호출되고 난 다음에는 render() 함수가 호출된다.
사용 이유
애니메이션 효과를 초기화할 때 사용
이벤트 리스너를 없앨 때 사용
이 API에서는 this.setState()를 사용하면 무한 루프가 일어나게 되므로 사용하면 안된다.
예를 들어, props가 업데이트 되기 전에 어떤 element를 fade out 애니메이션 효과를 주고 싶은 경우에는 다음과 같이 사용한다.
A 페이지에서 로그인 컴포넌트를 사용하고, B 페이지에서는 장바구니 컴포넌트를 사용한다고 하자. 그러면, A 페이지가 로드가 되면, 로그인 컴포넌트가 마운트가 된다. 그러다가, B 페이지로 이동하게 되면, 로그인 컴포넌트는 언마운트 되고, 장바구니 컴포넌트가 마운트가 된다. 즉, 페이지 이동으로 인한 기존 페이지에서의 컴포넌트가 그 페이지의 DOM에서 분리되는 것을 Unmount 라고 한다.
만약 componentDidMount에서 addEventListner를 사용했는데, 다른 페이지에서는 이 addEventListner를 사용하고 싶지 않다면, componentWillUnmount에서 이 컴포넌트가 Unmount 되기 전에, 이 addEventListner를 제거해야 한다.(removeEventListener)
왜냐하면, addEventListner는 window.addEventListner 이렇게 사용하기 때문에(window 객체의 메소드), 전역으로 다른 페이지에도 영향이 간다. 그래서, 이 페이지에만 사용할 것이라면, 이 페이지의 컴포넌트가 Unmount 될 때, addEventListner를 제거해야 한다.