Do IT React.js 도서에서 추가적으로 다루는 내용을 정리해 보겠습니다. React.js 의 기본서는 리액트를 다루는 기술 을 기준으로 추가 내용을 선별 하였습니다.

이번 페이지 에서는 ES6 의 문법 내용을 중심으로 살펴보겠습니다.

책표지


2 ES6 문법 액기스

2-1 Template String (템플릿 문자열)

Python 에서의 f-string (formatted string literals) 과 같은 문법을 f"{변수1} {변수2}" ES6 에서도 Jquery 와 비슷한 형테로 활용 가능 합니다.

${변수명} 를 활용하면 객체명 호출 및 연산 까지도 가능 합니다.

const string1 = '안녕하세요';
const string2 = '반갑습니다';
const greeting = `${string1} & ${string2}`;
const product = {
  name: '리액트 정석',
  price: '32,000원'
};
const price = 10000;
const number = 15;
const boolValue = false;

console.log(greeting);
console.log(`제품 ${product.name} 의 가격은 ${product.price} 입니다`);
console.log(`템플릿 문자열에서 수식 계산도 ${price * number} 가능합니다`);

2-2 Spread Operator

나열형 자료를 추출하거나 연결할 때 사용하는 문법으로 배열[], 객체{}, 함수의 인자 표현식() 내부 에서만 사용이 가능합니다.

배열에서의 활용과, 배열 구조화 할당 예제는 다음과 같습니다.

// ES6 문법
var array1 = ['one', 'two'];
var array2 = ['three', 'four'];

// ES5 := [array1[0], array1[1], array2[0], array2[1]];
var combined = [...array1, ...array2];

// 배열 구조화 할당과 Spread Operator
// ...others 는 앞의 객체들이 처리한 나머지(타입이 다양하게 변환된다) 를 자동추출
var [first, second, three = 'empty', ...others] = array1;

JavaScript 의 특수변수인 arguments 를 사용하면 객체 형태로 각각 할당 합니다. (Pythone 에서 enumerate() 와 동일한 기능을 하는 특수변수)

function func() {
  console.log(arguments);
}
func(1,2,3)
// Arguments { 0: 1, 1: 2, 2: 3, … }

전개 연산자를 활용하면 이와같은 배열 연산을 보다 단순하게 구현할 수 있다. 아래의 2개 함수는 동일한 기능을 하는 함수 입니다.

function func() {
  var args = Array.prototype.slice.call(this, arguments); // 배열로 변환
  console.log(args[0]);         // 0번째 인덱스 값
  console.log(args.slice(1));  // 1번 부터 Slice 한 나머지 값 (배열)
}

function func(...args) {
  console.log(args[0]);         // 0번째 인덱스 값
  console.log(args.slice(1));  // 1번 부터 Slice 한 나머지 값 (배열)
}

function func(first, ...others) {
  console.log(first)    // 0번째 인덱스 값
  console.log(others)   // 0번째 를 Slice 한 나머지 값
}

주의할 점으로는 ...others 객체는 함수 인자 표현식 등에서 맨 마지막 객체 값 으로만 할당하고, 중간에 위치한 값을 할당할 수는 없다. 이때는 ...others 객체에서 별도로 마지막 부분을 추출 해야만 된다.

객체에서 활용과, 객체 구조화 할당 예제는 다음과 같습니다.

var objectOne = { one: 1, two: 2, other: 0 };
var objectTwo = { three: 3, four: 4, other: -1 };

var combined = {
  ...objectOne,
  ...objectTwo,
}; // 중복되는 객체는 뒤의 것으로 OverWriting 된다

객체의 Key 와 Value 값을 각각 추출할 수 있습니다.

var obj1 = {one:"1", two:"2", three:"3"}
// 첫번째 객체의 Key 와 value 를 추출
var {one: myOne} = obj1  // one : one,  myOne : "1"

객체의 내부에 객체가 포함되어 있는 경우에는 아래와 같이 작업을 합니다. 이때 주의할 점으로는 객체 내부의 Key 값은 유효값이 생성 되지만, 추출을 위한 보조객체를 호출하면 Uncaught ReferenceError: obj is not defined 오류가 발생합니다.

var obj1 = {one:"1", two:"2", three:"3"}
var obj2 = {zero:"0", four:"4", obj: obj1}
var { obj : { three }} = obj2
console.log(obj)   // Uncaught ReferenceError 오류를 출력 
console.log(three) // "3"

var { obj : { two : myTwo }} = obj2
console.log(myTwo)  // one, two 모두 객체 추출을 위한 Key 로 값이 할당되지 않습니다.

obj 객체 호출시 오류가 발생하는 이유는 객체 내부의 객체를 추출 하는 경우에는 가장 깊은 단계의 값 만 추출하고 나머지는 호출하기 위한 Key 값 로만 작동 합니다. 즉 **객체 구조화** 의 경우 모든 변수에 값을 할당하지 않습니다

var others = Object.assign({}, combined);
var { other, ...others } = combined;
// others = { one: 1, two: 2, three: 3, four: 4}

2-3 가변변수와 불변변수

let 은 가변변수를 정의 하고, const 는 불변변수를 정의 합니다. const 로 생성된 객체의 경우, 원칙적으로는 객체 자신의 값을 변경할 수 없지만, JavaScript 함수로 별개의 객체를 생성 하는 함수는 const 객체의 값을 변경 가능 합니다.

가변 내장 함수 (push(...items), splice(s, c ...items), pop(), shift()) 를 사용하면, 불변변수 의 상태 와 값이 유동적으로 변하게 되어서 객체 불변성 유지를 통한 안정성에 크게 위협을 줍니다.

const arr2 = [];
// arr2.push(1);          // [1]
// arr2.splice(0, 0, 0); // [0,1]
// arr2.pop();            // [1]

이러한 이유로 위의 내장함수 대신, 무결성 내장 함수 (concat(...items), slice(시작_index, 마지막_index)) 를 사용하여 객체 불변성을 유지하며 작업이 가능합니다. 무결성 함수를 사용한 객체와 배열의 예제는 다음과 같습니다.

const arr3 = [];
const arr4 = arr3.concat(1);   //  [1]
const arr5 = [...arr4, 2, 3];   //  [1, 2, 3]
const arr6 = arr5.slice(0, 1); // arr6 = [1], arr5 = [1, 2, 3]
const [first, ...arr7] = arr5; // arr7 = [2, 3], first = 1

const obj3 = { name: '내이름', age: 20 };
const obj4 = { ...obj3, name: '새이름' }; // { name: '새이름', age: 20}
const { name, ...obj5 } = obj4;          // { age: 20 }

const arr = [1, 2, 3]; 
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
} // 가변 변수의 사용

for (const item in arr) {
  console.log(item);
} // iterator 방식의 for-in 루프와 함께 불변 변수 사용

// forEach 함수 예
arr.forEach((item, index) => {
  console.log(item);
  console.log(index);
});

2-4 클래스

JavaScript 에서는 기본으로 Class 를 지원하지 않아서 ProtoType 메서드를 활용하여 객체를 생성하는 방식으로 클래스와 클래스 메서드를 구현해 왔었습니다.

// ES5 문법
function Shape(x, y) {
  this.name = 'Shape';
  this.move(x, y);
}
// static 타입 선언 예제
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;
};
var s = new Shape(0, 0);
var s2 = Shape.create(0, 0);
s.area(); // 0

function Circle(x, y, radius) {
  Shape.call(this, x, y);
  this.name = 'Circle';
  this.radius = radius;
}
Object.assign(Circle.prototype, Shape.prototype, {
  area: function() {
    return this.radius * this.radius;
  },
});

var c = new Circle(0, 0, 10);
c.area(); // 100

ES6 에서 부터 클래스 개념과 static 선언이 함께 도입 되어 클래스 문법이 완성 되었습니다.보다 자세한 내용은 자바스크립트 객체지향 프로그래밍 을 참고 합니다.

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; }
}
var s  = new Shape(0, 0);
var s1 = Shape.create(0, 0);
s.area(); // 0

class Circle extends 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 c = new Circle(0, 0, 10);
c.area(); // 100

2-5 화살표 함수

기본의 함수 선언 방식은 다음과 같습니다.

function add(first, second) {
    return first + second;
}; 
var add = function(first, second) {
    return first + second;
};

화살표 함수는 인자블록 (())본문 블록({}) 사이에 화살표 기호 (=>) 를 추가하면 됩니다. 이단계 함수 선언 에서도 Arrow Function 을 사용할 수 있습니다.

var add = (first, second) => {
    return first + second;
}

// 2단계 Arrow Function 의 선언
var addNumber = (num) {
    return (value) => num + value;
}
var addNumber = (num) => (value) => num + value;

Class 객체 에서 함수를 호출하는 경우, 참조하는 콜백함수의 실행 범위 에 따라서 호출 가능한 객체 가 제한 됩니다. 이를 보완하는 Method 로는 bind() 를 사용해서 this 객체로 전달과정을 포함해야 합니다.

Arrow Function 을 사용하면 bind() 함수 없이 this 객체 만으로도 전달 가능합니다.

// bind함수를 통해 this scope를 전달한 예
class MyClass {
  value = 10;
  constructor() {
    // bind() 함수를 사용한 객체전달 확장
    var addThis2 = function(first, second) {
      return this.value + first + second;
    }.bind(this);
    // Arrow Function 을 사용하면 bind() 없이 this 객체로 자동 연결된다.
    var addThis3 = (first, second) => this.value + first + second;
  }
}

2-6 객체 확장 표현식 & 구조 분해 할당

선언된 Object 내부에 Key 값들을 추가 할 수 있습니다.

var obj = { a:1}
obj.b = 2;
obj["c"] = 3;

var nameKey = 'other'
obj[`an${nameKey}`] = 4;

2-7 라이브러리 의존성 관리

실행에 필요한 모듈을 모두 호출 되어야 작동이 원활 합니다.

2-8 배열 함수

ES5 에서 추가된 배열함수로 forEach(), map(). recude() 함수가 있습니다.

우선 배열함수를 사용하지 않고 구현하는 예제를 살펴보겠습니다.

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

function parse(qs) {
  // .substr(시작_index, 종료_index) : String 객체 Slice 함수
  var queryString = qs.substr(1);       // '? 제거'
  var chunks = queryString.split('&');  

  var result = {}; 
  for(var i = 0; i < chunks.length; i++) {
    var parts = chunks[i].split('=');  // ['banana','10']
    var key   = parts[0];               // 'banana'
                                          // Number() : String 을 Float 으로 변환   
    var value = Number.isNaN(Number(parts[1])) ? parts[1] : Number(parts[1]);
    result[key] = value;   // 객체의 값 확장 
  }
  return result;
}

parse(qs)

.forEach()

배열 객체를 for 반복문에서 함수처리를 반복하는 경우에는 배열함수 로 Re-Factoring 하면 간결해 집니다. 아래의 예제는 chunks 배열객체를 .forEach() 함수로 반복처리를 해 보겠습니다.

function parse(qs) {
  var queryString = qs.substr(1);
  var chunks = queryString.split('&');  
  var result = {};
  chunks.forEach(function(value, index)) { // value : 개별 값, index : Key Index
    var parts = value.split('=');
    var key   = parts[0];
    var value = Number.isNaN(Number(parts[1])) ? parts[1] : Number(parts[1]);
    result[key] = value;
  }
  return result;
}

덧붙여서 추출한 값들은 배열 구조 할당으로 보다 간결하게 ReFactoring 이 가능 합니다.

function parse(qs) {
  const queryString = qs.substr(1);
  const chunks = queryString.split('&');
  let result = {};
  chunks.forEach((chunk) => {
    const [ key, value ] = chunk.split('='); 
    result[key] = Number.isNaN(Number(value)) ? value : Number(value); 
  });
  return result;
}

.map()

.map() 배열함수는 인자에 포함된 콜백함수가 실행 될 때 마다 새로운 객체를 생성 합니다. 아래의 내용을 실행하면, [ { key: 'banana', value: 'one' }, { key: 'apple', value: '20' }, { key: 'orange', value: '30' } ] 반복문 실행시 마다 새로운 객체를 생성하는 것을 알 수 있습니다.

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

.reduce()

reduce(콜백함수, 초기값 객체) 배열함수는 배열 형태를 객체(숫자,문자,배열,객체,boolean) 형태로 변환 하는 함수 입니다.

// 0 : 데이터 초기값 선언 (Initialization)
function sum(numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}
sum(['1',2,3,4,5,6,7,8,9])

.map() 에서 반복적인 객체를 생성된 데이터 Type 을 합치는 용도로써 .reduce() 메서드를 유용하게 활용할 수 있습니다.

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

  // reduce() 배열 함수를 활용하여 객체 합치기
  var result2 = result.reduce(function(result, item){
    result[item.key] = item.value;
    return result;
  } , {});
  return result;
}

객체 구조화 할당을 활용하면 result2 의 수식을 보다 간단하게 정리할 수 있습니다.

function parse(qs) {
  ...
  var result2 = result.reduce(function(result, {key, value}){
    result[key] = value;
    return result;
  } , {});
  return result;
}

2-9 비동기 함수

urgentWork()

프로그램은 순서대로 실행하는게 원칙 이지만, 이러한 순서에 얽매이지 않고 실행 가능한 방법이 비동기 실행 입니다. JavaScript 에서는 비동기 실행 함수를 urgentWork() 를 사용하면 구현 가능합니다.

// ES5의 예제
function work1(onDone) {
  setTimeout(() => onDone('작업1 완료!'), 100);
}
function work2(onDone) {
  setTimeout(() => onDone('작업1 완료!'), 200);
}

// 앞서 실행된 명령과 별개로 작동
function urgentWork() {
  console.log('긴급 작업');
}

work1(function(msg1) {
  console.log('done after 100ms:' + msg1);
  work2(function(msg2) {
    console.log('done after 300ms:' + msg2);
  });
});
urgentWork(); // 비동기 함수를 실행

Promise

위 방식은 CallBack 형식으로 작업 내용을 알아보기 어려운 단점이 있습니다. 이를 간단하게 작업이 가능한 클래스 객체Promise 클래스를 제공 합니다.

Promise 객체는 then(), catch(), finally() 메서드를 활용하여, CallBack 방식을 Currying 방식으로 구현이 가능 합니다.

// 1. Producer
// New Promise is Created, the Excutor runs automatically.
const promise = new Promise( (resolve, reject) => {
  console.log('doing something...');
  setTimeout( ()=> {
    reject(new Error('no network'));
  }, 2000);
  
  // 2. Consumer : then, catch, finally
  promise
    .then(value => {   // 정상 처리된 경우
      console.log(value);
    })
    .catch(error => {  // 비정상 처리된 경우
      console.log(error);
    })
    .finally( () => {  // 정상/비정상 처리된 후
      console.log('finally')
    });

3 Component

Curly Brace

데이터 원본을 React.js Component 에 전달 합니다. 단위 데이터, 객체, 배열, 함수, JSX 까지 모두 전달이 가능합니다.

{2}, {true}, 
{[1,2,3,4]}, {{ key:'value'}},
{ () => {} }, {<span>JSX Code</span>}

Children Property

Component 중간의 Node 에 데이터를 정의하면 컴포넌트가 이를 인식하여 동작 합니다.

render() {
  return (
      <>
        <ChildComponent> 데이터 TAG </ChildComponent>
        // 속성 으로도 Children 을 입력 가능합니다.
        <ChildComponent children={<b>데이터 TAG</b>} />
      </>
)};

ProtoTypes

데이터 형식을 미리 정의하면, 정의한 내용과 다른 내용이 입력된 경우 오류 메세지를 출력 합니다.

Component Condition : &&

조건이 true 일 때, 지정된 값을 할당 하고, undefined 등의 오류가 발생하는 경우에는 해당 객체의 연성 과정을 무시 하고 진행을 합니다.

let value;
if (obj) {
  value = obj.key1;
};
const value = obj && obj.key1;
{ condition && <Component  /> }

if (value) 판단문에서 truefalse 결과값은 아래의 객체로도 대체 가능합니다.

true : -1, '0', 'false'(문자열), `{}`, `[]`
false : undefined, null, 0, NaN

Component Condition : ||

조건이 false 일 때, 지정된 값을 할당 하고, 유효한 값이 전달된 경우에는 해당 값을 적용 합니다.

const Box = styled.div`
  background: ${props => props.color || 'blue'};
  padding: 1rem;
  display: flex;
`;