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)
판단문에서 true 와 false 결과값은 아래의 객체로도 대체 가능합니다.
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;
`;