성장, 그리고 노력

부족하더라도 어제보다 더 잘해지자. 노력은 절대 배신하지 않는다.

Javascript

[Javascript] 자바스크립트 기본기 다지기(ES6+)

제이콥(JACOB) 2019. 12. 8. 22:27

자바스크립트 개요(ES6+)

자바스크립트(Javascript)는 일급 함수를 지원하는 언어입니다. 또한 동적 언어이며 HTML과 CSS와 함께 웹에서 가장 굴직한 줄기를 차지하고 있습니다. 참고로 Java와는 전혀 다른 언어입니다.

  그런데 자바스크립트를 공부하다보면 ECMAScript, ES6, ES6+, ES7 ... 등의 말을 보게 됩니다. ES는 ECMAScript의 줄임말이며 뒤에는 버전이라고 생각하시면 됩니다(물론 ES1부터 다 있습니다). ECMAScript는 자바스크립트의 표준이며 현재도 계속 ECMA International 표준화 기구에 의해 버전이 업데이트 되고 있습니다. 그리고 ECMAScript2015를 ES6라고 하며, ES6 이후 버전을 통칭하여 "ES6+" 라고 합니다.

아래에서 소개할 문법은 ES6+ 문법이며, 다른 문법은 시중에 있는 기본서를 참고 부탁드립니다. 기본 문법을 아셔야지 이해가 됩니다.

cf) ECMA란?

유럽 컴퓨터 제조사 연합(European Computer Manufacturers Association)의 약자. 스크립트(javascript, JSON, XML 등.. 많음) 표준을 만들고 관리하는 단체 입니다.

cf) ECMAScript stage 란?

이 부분은 그냥 안보시고 넘어가셔도 됩니다. 그냥 제가 궁금했던거라 남깁니다.

TC39 Process에 의해 버전에 들어갈 내용이 결정되며, 이 프로세스는 총 4개의 stage로 나뉩니다.

Proposal (stage 1) - 신청 단계 입니다. ECMAScript의 표준으로 될 확률은 10% 정도에요(개인적인 수치입니다.). 이 단계로 오면 TC39 위원회에 의해 표준으로 적당한지 여부를 판단합니다.

Draft (stage 2) - 초안 단계 입니다. ECMAScript의 표준으로 될 확률은 70% 정도에요(개인적인 수치입니다.). 이 단계로 오면 대부분 표준으로 될 것을 예상합니다.

Candidate (stage 3) - 후보자 단계 입니다. ECMAScript의 표준으로 될 확률은 90% 정도에요(개인적인 수치입니다.). 표준이 되는 가장 유력한 후보이며, 표준이 되기를 기달립니다. 특별한 문제가 없다면 정해진 문법 등의 수정은 절대 없습니다.

Finished (stage 4) - 말 그대로 끝마침 단계 입니다. ECMAScript의 표준 선정되며, 가장 빠른 표준에 포함됩니다.

현재 진행 상황이 궁금하다면 compat-table을 참고하시면 됩니다. stage 0도 있으나, 일반적으로 관심있게 보시면 될 부분은 stage2 이상 부분입니다.

but, 한 가지를 항상 더 고려해야 합니다. 아무리 stage 2 이상에 기재된 문법이더라도 그 문법이 모든 브라우저에서 작동된다는 보장은 없습니다. 따라서 cross browsing을 고려하고 있다면 (polysills, shim, transpilers)라는 키워드로 먼저 관련 정보를 찾아보시는걸 추천드립니다.

 


자바스크립트 기본 문법

Template Literals

기존에 사용하던 "A"+"B"이런 문법을 가독성 좋게 바꿔줌.

// 일반
const hello = (name) => {
    console.log(name + "님 안녕하세요!");
}

// Template Literals
const hello = (name) => {
    console.log(`${name}님 안녕하세요!`);
}

Object Destructuring1 (Structuring)

// 일반
const person = {
  name: "Master Jung",
  age: 28
};

const name = person.name;
const age = person.age;

console.log(name, age);
// Structuring1
const person = {
  name: "Master Jung",
  age: 28
};

const { name, age } = person;

console.log(name, age);

근데 여기서 한 가지 의문점이 생깁니다. 같은 변수명을 사용하면 쉽게 할당되는 것은 알겠는데, 이러면 항상 같은 변수명을 사용해야하나?

물론 아닙니다. 만약 다른 변수명을 사용하고 싶다면 아래와 같이 사용해 주면 됩니다.

// Structuring2
const person = {
  name: "Master Jung",
  age: 28
};

const { name, age: realAge } = person;

console.log(name, realAge);

변경하고 싶은 변수명 옆에 콜론(colon, :)을 입력해 주면 됩니다.


Object Destructuring2 (Structuring)

위에서 배운 개념을 약간 더 심화시켜 보겠습니다.

// Structuring3
const person = {
  name: "Master Jung",
  age: 28,
  family: {
    father: "Zeus",
    mother: "Hera",
    brother: "Pikachu"
  }
};

// 일반적인 경우
const father = person.family.father;
const mother = person.family.mother;
const brother = person.family.brother;

// 우아한 방법
const {name, age, family: {father, mother, brother} } = person;
console.log(nage, age, father, mother, brother);

아래 방법에 훨씬더 가독성이 좋고 직관적이지 않나요? 물론 실제로는 줄바꿈까지 해주면 더 좋겠죠?

const {name, 
       age, 
       family: {father, mother, brother} 
       } = person;

Spread Operators

ES6+ 문법에서 가장 중요한 문법을 꼽으라고 한다면 저는 당연히 이 스프레드 연산자 를 꼽습니다.

가장 사용 빈도수가 높을 뿐더러 매우 유용합니다. 처음에는 개념이 헷갈릴 수 있으니 자주 보시는걸 추천드립니다.

const beforeNumber = [1, 2, 3, 4];
const afterNumber = [5, 6, 7, 8];

const concatNumber = [beforeNumber + afterNumber];
console.log(concatNumber); // ["1,2,3,45,6,7,8"]

위 예제는 두 배열을 합치는 간단한 예제입니다.

그런데 우리가 원하는 결과물이 아니에요. 배열을 합쳤을 때 우리가 생각한 기대는 [1, 2, 3, 4, 5, 6, 7, 8] 이런 모습이었거든요. 한 배열 안에 컨텐츠 인자 값들을 넣고 싶었는데, 전혀 다른 모습이니깐요.

그럼 조금더 발전시켜서 배열 안에 두 배열을 넣으면 되지 않을까요?

const concatNumber = [beforeNumber, afterNumber];
console.log(concatNumber); // [Array(4), Array(4)]

역시나 완벽한 정답이 아닙니다. 한 배열 안에 두 배열이 인자로 들어가버린 형태가 되어버렸습니다. 그렇다면 한 배열 안에 인자를 모두 넣어줄려면 어떻게 해야할까요? 이 과정에서 필요한게 unpack 입니다. 그리고 unpack을 도와주는 것이 spread operator 이고요.

const beforeNumber = [1, 2, 3, 4];
const afterNumber = [5, 6, 7, 8];

const concatNumber = [...beforeNumber, ...afterNumber];
console.log(concatNumber); // [1,2,3,4,5,6,7,8]

훨씬 깔끔하고 간단하지 않나요? 바닐라 자바스크립트로도 같은 결과를 가질 수야 있겠지만, 생산성과 효율성이 다르겠죠.

그리고 이 문법은 단순한 배열 합치기 뿐만 아니라 Object 에서도 많이 쓰입니다.

const ob1 = {
  one:1,
  two:2
}

const ob2 = {
  three:3,
  four:4
}

const concatOb = {...ob1, ...ob2};
console.log(concatOb); // {one: 1, two: 2, three: 3, four: 4}

이것만 봐서는 이게 유용한건지 아닌건지 긴가민가 하시죠? 일단 믿고 꼭 외우고 이해하고 직접 사용해 보시길 바랍니다. 정말 많이 사용하시게 됩니다.

Destructuring

전개 연산자를 이용해서 객체 디스트럭링을 해보겠습니다.

let numObject = {n1: 1, n2: 2, n3: 3};
let {n2, ...nSome} = numObject; 

console.log(n2, nSome); // 2 { n1: 1, n3: 3 }

위와 같이 값이 나오는 이유는 객체의 레스트 속성(rest properties)가 nSome이 되기 때문입니다.
익숙치 않아서 헷갈리는거지 어려운 개념은 아닙니다. 직접 여러가지 해보면 근방 감이 옵니다.


Class

일전에 타입스크립트 강좌에서도 사용한 클래스입니다. 처음 프로그래밍을 공부한다면 가장 처음에 만나는 산인데요(나중엔 익숙...). 간단하게 말해서 "설계도" 라고 생각하시면 되는데, 저도 처음에 이 말을 들었을 때 이해되지 않았어요. 이럴때는 "아, 그냥 클래스는 설계도구나, 무언가에 도안이나봐. 블루프린트(청사진)인가봐."하고 넘어가시면 됩니다.

class Person {                //문법이에요. class 예약어 뒤에 클래스 명을 적어요
  constructor(name, age){     //단어 그대로 한국말로도 생성자라고 합니다. 
                              //Person이라는 설계도를 사용할때 생성자(constructor)에 있는 인자값을 무조건 전달해 주어야 해요. 
                              // 이 값은 비워둬도 됩니다.

    this.name = name;         // 여기서 this는 중요한 개념이지만 처음에는 "본인 클래스를 참조한다"라는 의미로 알아 두세요.
    this.age = age;           // 이것의 의미는 이 설계도에 age값을 전달 받으면 이 값은 이 클래스에서 사용하는 age라는 변수의 값과 같다라는 말이에요
  }
}

const masterJung = new Person("masterJung", 28);  // 설계도를 사용할 때는 new 와 함께 사용하시면 돼요.

console.log(masterJung);      // {name: "masterJung", age: 28}
console.log(masterJung.name); // masterJung

또한 클래스는 extends 키워드를 통해 확장이 가능합니다. 즉 하위 클래스를 만들수 있어요.

설계도를 예를들면 로보트 설계도에서 날개를 추가한다고 할까요? 여기서 생각해야될 것은 날개를 단 로봇도 로봇 설계도에 근간하여 만들어졌기 때문에 로봇 설계도가 가지는 것들을 동일하게 가집니다.

class Person {                
  constructor(name, age){ 
    this.name = name;
    this.age = age;          
  }
}

class Worker extends Person {
  work(){
    console.log("나 일해요, 말 시키지마요");
  }
}

위 예제에서 Worker 클래스는 Person 클래스의 하위 클래스가 됩니다. 그러기 때문에 Worker 클래스를 생성한다면 상위 클래스의 생성자를 그대로 가져가죠.

const worker1 = new Worker("You", 100);
console.log(worker1);         // {name: "You", age: 100}
console.log(worker1.work());  // 나 일해요, 말 시키지마요

Array.map

Map 함수에 대해 알아보겠습니다. Map 함수는 함수가 모든 배열을 돌며, 함수의 결과 값들을 저장해서 새로운 배열 로 만드는 함수 입니다. 말이 참 어렵네요. 바로 예제부터 보겠습니다(이 함수도 중요해요!).

const datas = ["123", "abc", "dfg"];
const defData = datas.map( data => `${data}${data}`);  //map( "실행시킬 함수" )

console.log(defData); // ["123123", "abcabc", "dfgdfg"]

map() 함수 안에 정의된 함수가 실행되며, 새로운 값들을 defData라는 새로운 배열에 저장했습니다. 생각보다 간단하죠?

위 예제를 조금만 변형해 보겠습니다. 물론 결과 값은 같습니다.

const datas = ["123", "abc", "dfg"];

const multpleData = data => `${data}${data}`;
const defData = datas.map(multpleData); 

console.log(defData); // ["123123", "abcabc", "dfgdfg"]

실행해 보시면 결과값은 동일합니다. 좀 더 로직을 분리시켜서 map함수 안에서 실행되는 함수를 미리 선언해 보았습니다. 간혹 map함수 안에 로직이 복잡해 지는 경우가 있는데 그럴때는 이렇게 밖으로 함수 로직을 빼서 작성하는게 가독성과 유지 보수도 좋습니다.

cf) index라는 값을 함수는 전달해 줍니다. 우리가 굳이 로직을 짤 필요가 없고 함수 안에 index라고만 인자값을 추가해 주면 알아서 카운팅해줍니다.

const datas = ["123", "abc", "dfg"];

const multpleData = (data, index) => `${index}: ${data}`;
const defData = datas.map(multpleData); 

console.log(defData); //["0: 123", "1: abc", "2: dfg"]

Array.filter

필터도 정말 유용한 함수 중에 하나입니다. map함수랑 유사하지만, 필터의 경우 다른점은 함수로 구현된 로직에서 참(true)인 값만 새로운 배열 로 작성합니다. map함수는 모든 배열을 돌며 저장 했던거와는 다르죠? 예제로 확인 하겠습니다(이 함수도 중요해요!)

const numbers = [5,2,58,21,84,21,84,543,51,1,0];
const biggerThan50 = numbers.filter(number => number > 50);

console.log(biggerThan50); // [58, 84, 84, 543, 51]

간단하면서도 강력하지 않나요? 이거를 조금만 활용하면 배열을 좀 더 우아하게 다룰 수 있습니다.

let tags = ["이상한말", "좋은말", "칭찬", "최고"];
tags = tags.filter(tag => tag !== "이상한말");

console.log(tags); // ["좋은말", "칭찬", "최고"]

forEach

배열의 모든 인자를 실행하지만, map과 filter와 달리 새로운 배열을 리턴(return)하지 않습니다. 그래서 forEach의 경우 간단한 로직 처리를 할 때 많이 쓰입니다. 예를 들면, LocalStorage에 값을 저장하거나 API 요청을 보내거나 등이요. 물론 map에 비해 사용 빈도수는 낮습니다.

간단하게 정리하자면 새로운 배열을 리턴받고 싶거나 받아야 한다면 map()을 사용, 그렇지 않는 경우에는 forEach를 사용하면 됩니다(단순히 데이터 베이스에 배열값 저장 등). 속도비교 .


push

배열에 마지막에 값을 추가 합니다.

let datas = ["a", "b", "c"];
datas.push("d");

console.log(datas); // ["a", "b", "c", "d"]

includes

배열에 특정 값이 포함되었는지를 체크한다.

let datas = ["a", "b", "c"];
if(!datas.includes("d")){
  datas.push("d");
}

console.log(datas); // ["a", "b", "c", "d"]

reverse

배열에 앞뒤 값을 바꾼다.

let datas = ["a", "b", "c"];

console.log(datas.reverse()); // ["c", "b", "a"]

참고 링크 : mozila


for in 문

[예제1]

  const names = ["jung", "jungKim", "sin"];

  for(let index in names){
    console.log(index, names[index]);
  }

  // 0 jung
  // 1 jungKim
  // 2 sin

[예제2]

  const names = { a: "jung", b: "jungKim", c: "sin" };

  for (let index in names) {
    console.log(index, names[index]);
  }

  // a jung
  // b jungKim
  // c sin

for of 문

for in문과 달리 바로 값을 가져올 수 있습니다.
[예제1]

  const names = ["jung", "jungKim", "sin"];

  for (let name of names) {
    console.log(name);
  }

  // jung
  // jungKim
  // sin

[예제2]

  const names = ["jung", "jungKim", "sin"];

  for (let name of names) { //let 대신 const 사용가능
    console.log(name);
  }

  // jung
  // jungKim
  // sin

예제2에서 특이한 점은 const를 사용할 수 있다는 점입니다. 일반적인 for문은 불가능하지만, for of문의 경우 Symbol.iterator 구현을 통해 각 이터레이터의 값을 가져오기 때문에 const를 사용할 수 있습니다.


Rest parameter

나머지 변수(인자)는 개수가 정해지지 않은 인자를 배열로 받을 수 있는 방법입니다. 바로 예제를 보시죠.

  function concat(x, y, ...restParam){
    return x + y + restParam.join("");
  }

  let result1 = concat("a", "b");
  let result2 = concat("a", "b", "a", "b", "a", "b", "a", "b");

  console.log(result1, result2); //ab abababab
반응형