[React] ES6 문법 정리

참고 문헌 : Do it 리액트 프로그래밍 정석

Intro

리액트를 현업에서 주로 쓴다는 말은 들었지만 실제로 이렇게까지 써먹을 줄은 사실 잘 몰랐다. 난 분명 프론트앤드를 지망한 적이 없었으나 먹고살기 위해선 내가 해야 할 일이 프론트앤드라는 것을 인정하고 열심히 공부하는 수 밖에.

한동안 PS 위주로 포스팅을 했다. 이제부턴 기술들에 대한 포스팅을 주로 올릴까 한다.

템플릿 문자열

var string1 = 'Hello';
var string2 = 'Nice to meet you';
var result = string1 + ' ' + string2
var product = {name : 'chicken', price : '18000'};
var message = product.name + '의 가격은' + product.price + '입니다.';

위의 코드는 우리가 흔히 보던 자바스크립트의 문자열 사용 방법들이다.
특히 후자같은 경우엔 C++에서도 주로 저런 짓을 많이했다. 상당히 불편하다.

var string1 = 'Hello';
var string2 = 'Nice to meet you';
var result = '${string1} ${string}';
var product = {name : 'chicken', price : '18000'};
var message = '${product.name} 의 가격은 ${product.price} 입니다.';

아마 파이썬에서 format 함수를 써 본 사람들은 비슷한 면을 발견할 수 있을 것이다. ES6에서는 이러한 형태의 문자열 처리를 지원한다.

전개 연산자

var array1 = ['1','2'];
var array2 = ['3','4'];

이 두가지 배열을 합쳐야 한다면 여러가지 방법이 있겠지만 크게 두가지 방법이 있다.
1. 일일히 값을 가져와서 하나의 큰 배열을 만든다.
2. concat을 활용한다.
하지만 ES6에서는 그렇게까지 할 필요는 없고 다음과 같이 하면 된다.

var result = [...array1, ...array2];

... 즉 전개연산자를 사용하면 데이터 전체를 다 집어넣을 수 있다.

가변 변수, 불변 변수

var num = 1;

기존에 javascript는 var를 통해 변수를 선언했다. 물론 지금이라고 그렇게 하면 안되는 것은 아니다. 하지만 마치 다른 언어와 마찬가지로 가변(변할 수 있는), 그리고 불변(변하지 않는) 변수를 선언할 수 있는 키워드가 추가되었다.

let num = 2;
const num2 = 1;

let은 가변 변수, const는 불변 변수다. let은 흔히 우리가 알던 변수와 같다.
const는 불변 변수다. 하지만 값을 다시 할당할 수 없는거지 값을 바꿀 수는 있다. 내장 함수를 통해 값에 변화를 줄 수는 있다.

const data = [];
data.push(1);//[1]
data.pop();//[]
data.push(2);//[2]
data.splice(0,0,0);//[0,2]
가변 내장함수무결성 내장 함수
push(...items)concat(...items)
splice(s, c, items)slice(0, s), concat(...items, slice(s+c)
pop()slice(0, len-1)
shift()slice(1)

클래스

사실 기존의 javascript가 그렇게 익숙하지는 않아서 처음에 이해하기 상당히 어려웠다. ES6문법은 java가 익숙하다면 그렇게 어렵지 않다.

class shape{
	static create(x, y) {return new Shape(x, y);}
  	name = 'shape';
	constructor(x, y){
    	this.move(x, y);
    }
	move(x, y){
    	this.x = x;
      	this.y = y;
    }
	area(){
    
      return 0;
    }
}

기존의 java와 거의 유사하다. 이걸 기존의 문법으로 만든다면?

function Shape(x, y) {
  this.name = 'Shape';
  this.move(x, y);
}
Shape.create = function(x, y) {
  return new Shape(x, y);
};
Shape.prototype.move = function(x, y) {
  this.x = x;
  this.y = y;
};
Shape.prototype.area = function() {
  return 0;
};

물론 java 처럼 필드변수 따로 선언해주고 그러지는 않는다.

class Circle extend Shape{
	constructor(x, y, radius){
    	super(x, y);
      	this.radius = radius;
    }
  	area(){
    	if(this.radius === 0) return super.area();
      	return this.radius * this.radius;
    }
}

상속은 이런 식으로 구현한다.

화살표 함수

var add = (first, second) =>{
	return first + second;
};
var add = (first, second) => first + second;
var addAndMultiple = (first, second) => ({add : first + second, multiply : first * second});

이런 식으로 선언하는 함수의 장점은 다음과 같다.
1. 커링 구조에서 계단형 함수가 만들어지지 않는다.

function addNum(num){
	return function(value){
    	return num + value;
    }
}
var addNum = num => value => num + value;
  1. 콜백 함수의 this 범위로 생기는 오류를 피하기 위해 bind 처리를 할 필요가 없다.
class MyClass{
	value = 10;
	constructor(){
    	var add = function(first, second){
        	return this.value + first + second;
        }
        var add2 = (first, second) => this.value + first + second;
    }
}

객체 확장 표현식

기존의 javascript에서는 객체와 객체의 값을 선언하기 위해 키 이름과 값을 같이 할당했다. 하지마 ㄴES6에서는 그렇게 하지 않는다.

var x = 0;
var y = 0;
var obj = {x, y};
var randomKeyString = 'other';
var combined = {
  ['one' + randomKeyString] : 'some value',]
};
var obj2 = {
	x,
  	methodA() {console.log('method A');},
  	methodB() { return 0;},
};

구조 분해 할당

var list = [0,1];
var item1 = list[0];
var item2 = list[1];
var item3 = list[2] || -1;

var temp = item2;
item2= item1;
item1 = temp;

var obj = {
  key1: 'one',
  key2: 'two',
};

var key1 = obj.key1;
var key2 = obj.key2;
var key3 = obj.key3 || 'default key3 value';
var newKey1 = key1;

이렇게 표현할 수 있는 코드를

var list = [0, 1];
var [
  item1,
  item2,
  item3 = -1,
] = list;
[item2, item1] = [item1, item2];

var obj = {
  key1: 'one',
  key2: 'two',
};
var {
  key1: newKey1,
  key2,
  key3 = 'default key3 value',
} = obj;

var [item1, ...otherItems] = [0, 1, 2];
var { key1, ...others } = { key1: 'one', key2: 'two' };

이렇게도 표현할 수 있다. || 연산자는 헷깔리기 쉬운 건데 이런 문제를 해결할 수 있다.

배열 함수

forEach()

우리가 생각하는 그 forEach 맞다.

const qs = 'banana=10&apple=20&orange=30';

function parse(qs) {
  var queryString = qs.substr(1); // querystring = 'banana=10&apple=20&orange=30'
  var chunks = queryString.split('&'); // chunks = ['banana=10', 'apple=20', 'orange=30']
  var result = {};
  for(var i = 0; i < chunks.length; i++) {
    var parts = chunks[i].split('=');
    var key = parts[0];
    var value = Number.isNaN(Number(parts[1])) ? parts[1] : Number(parts[1]);
    result[key] = value;
  }
  return result;
}

우선 '&' 을 기준으로 문자열을 분리하고 '='를 기준으로 문자열을 분리한 다음 key와 value로 나눈다. 그 후 해당 key값에 value를 집어넣는다.
forEach를 쓰면 굳이 저런 식으로 for문을 돌리지 않아도 된다. 뭐 사실 java든 c++이든 흔히 보던 문법 아닌가 .

function parse(qs) {
  const queryString = qs.substr(1); // querystring = 'banana=10&apple=20&orange=30'
  const chunks = queryString.split('&'); // chunks = ['banana=10', 'apple=20', 'orange=30']
  let result = {};
  chunks.forEach((chunk) => {
    const [ key, value ] = chunk.split('='); // key = 'banana', value = '10'
    result[key] = value; // result = { banana: 10 }
  });
  return result;
}

map()

Python을 써 본 사람들이라면 자주 들어봤을 함수다. 파이썬에서 map 함수가 하는 역할은 특정 형태의 값으로 변환 해주는 것이었다.

myList = [1, 2, 3, 4, 5]
def add_one(n): 
	return n + 1 
result2 = list(map(add_one, myList))
print(myList)
print(result2)
[1,2,3,4,5]
[2,3,4,5,6]

이런 식으로 말이다. javascript도 비슷한다.

function parse(qs){
	const queryString = qs.substr(1);
  	const chunks = qs.split('&');
  	const result = chunks.map((chunk) =>{
    	const [ key, value ] = chunk.split('=');
      	return {key : key, value : value };
    });
  return result;
}
result = [
	{key : 'banana', value = '10'},
    {key : 'apple', value = '20'},
    {key : 'orange', value = '30'}
];

reduce

배열을 객체로 변환하고 싶을 때 사용하는 함수

function parse(qs) {
  const queryString = qs.substr(1);
  const chunks = queryString.split('&');
  return chunks
    .map((chunk) => {
      const [ key, value ] = chunk.split('='); // key = 'banana', value = '10'
      return { key, value }; // { key: 'banana', value: '10' }
    })
    .reduce((result, item) => ({
      ...result,
      [item.key]: item.value,
    }), {});
}
result : {banana : '10', apple : '20', orange : '30' }

비동기 함수

기존에 javascript는 비동기 작업을 위해 setTimeout() 형식으로 시간 지연을 주는 식으로 처리하였다.

function work1(onDone) {
  setTimeout(() => onDone('작업1 완료!'), 100);
}
function work2(onDone) {
  setTimeout(() => onDone('작업1 완료!'), 200);
}
function work3(onDone) {
  setTimeout(() => onDone('작업3 완료!'), 300);
}

work1(function(msg1) {
  console.log('done after 100ms:' + msg1);
  work2(function(msg2) {
    console.log('done after 300ms:' + msg2);
    work3(function(msg3) {
      console.log('done after 600ms:' + msg3);
    });
  });
});

ES6에서는 Promise 로 해결할 수 있다.

const work1 = () =>
  new Promise(resolve => {
    setTimeout(() => resolve('작업1 완료!'), 100);
});
const work2 = () =>
  new Promise(resolve => {
    setTimeout(() => resolve('작업2 완료!'), 200);
});
const work3 = () =>
  new Promise(resolve => {
    setTimeout(() => resolve('작업3 완료!'), 300);
})

const nextWorkOnDone = (msg1) => {
	console.log('done after 100ms:' + msg1);
  	return work2();
};

work1()
  .then(nextWorkOnDone)
  .then((msg2) => {
	console.log('done after 200ms:' + msg2);
  	return work3();
})
  .then((msg3) => {
    console.log('done after 600ms:' + msg3);
});

디바운스

어떤 내용을 입력하다 특정 시간동안 대기하고 있다면 마지막에 입력된 내용을 바탕으로 서버 요청을 하는 방법.
ex. 연관 검색어

export function debounde(func, delay){
	let inDebounce;
    return function(...args){
    	if(inDebounce){
        	clearTimeout(inDebounce);
        }
      	inDebounce = setTimeout(
          () => func(...args), delay);
    }
}

const run debounce(val => console.log(val), 100);
run('a');
run('b');
run(2);
//100ms 이후 2가 출력됨. 

스로틀

디바운스와 다르게 입력되는 동안에도 바로 이전에 요청한 작업을 주기적으로 실행함.
ex. 페이스북의 타임라인. 스크롤을 내리는동안 다음 내용이 계속 출력됨.

export function throttle(func, delay) {
  let lastFunc;
  let lastRan;
  return function(...args) {
    const context = this;
    if (!lastRan) {
      func.call(context, ...args);
      lastRan = Date.now();
    } else {
      if (lastFunc) clearTimeout(lastFunc);
      lastFunc = setTimeout(function() {
        if ((Date.now() - lastRan) >= delay) {
          func.call(context, ...args);
          lastRan = Date.now();
        }
      }, delay - (Date.now() - lastRan));
    }
  }
}

var checkPosition = () => {
  const offset = 500;
  const currentScrollPosition = window.pageYOffset;
  const pageBottomPosition = document.body.offsetHeight - window.innerHeight - offset;
  if (currentScrollPosition >= pageBottomPosition) {
    // fetch('/page/next');
    console.log('다음 페이지 로딩');
  }
};
var infiniteScroll = throttle(checkPosition, 300);
window.addEventListener('scroll', infiniteScroll);

좋은 웹페이지 즐겨찾기