ES6 최신 문법 활용

September 18, 2021

ES6 최신 문법으로 깔끔한 코드 만들기

주말에 뭘 공부해야할까 방황하다가 운명처럼 ES6 최신 문법 꿀팁을 보게되었습니다.
꿀팁을 나만 알고 있을 수 없지!
다들 멋지고 행복한 개발자가 되자구요!!

우선 많이들 알고있는 삼항 조건 연산자부터 시작하겠습니다.

삼항 조건 연산자(Ternary Operator)

// ❌ Bad Code 💩
function getResult(score) {
  let result;
  if (score > 5) {
    result = "👍";
  } else if (score <= 5) {
    result = "👎";
  }
  return result;
}

gerResult라는 함수는 score라는 인자값을 받아서 score가 5보다 크면 👍를 작거나 같으면 👎를 리턴하는 함수입니다.

일단 조건이 두개이기 때문에 사실 else if로 작성할 필요도 없고 값을 바로 리턴하기 때문에 지역 변수도 필요 없습니다.

삼항 조건 연산자는 물음표(’?‘)를 사이에 두고 왼쪽에 조건을 오른쪽에 ‘true일때 결과 : false일때 결과’를 작성해줍니다.

// ✅ Good Code ✨
function getResult(score) {
  return score > 5 ? "👍" : "👎";
}

함수가 깔끔, 간단해졌습니다!

널 병합 연산자(Nullish Coalescing Operator)

// ❌ Bad Code 💩
function printMessage(text) {
  let message = text;
  if (text == null || text == undefined) {
    message = "Nothing to display 😜";
  }
}

printMessage함수는 전달받은 text인자가 있다면 바로 message에 할당을 하고 text인자가 null이나 undefined라면 ‘Nothing to display 😜‘를 할당 해주는 함수입니다.

정말 많이 쓰이는 코드라고 생각하는데요,
널 병합 연산자를 사용하면 코드가 훨씬 간단해집니다.

// ✅ Good Code ✨
function printMessage(text) {
  const message = text ?? "Nothing to display 😜";
}

삼항 조건 연산자랑 비슷하게 생겼습니다.
물음표 두개(’??‘)를 사용해주면 왼쪽 코드가 null 또는 undefined일 경우에만 오른쪽 코드를 실행합니다.

또한 널 병합 연산자는 함수에도 사용할 수 있습니다.

const result = getInitialState() ?? fetchFromServer();
console.log(result);

function getInitialState() {
  return null;
}
function fetchFromServer() {
  return "Hello";
}

여기서 주의할 점은 논리적 OR 연산자(Logical OR Operator)와 혼용하면 안된다는 것입니다.

논리적 OR 연산자(Logical OR Operator)

// 🚨 Logical OR operator ||
function printMessage(text) {
  const message = text || "Nothing to display 😜";
  console.log(message);
}

논리적 OR 연산자는 왼쪽 코드가 falsy일 경우 오른쪽 코드를 실행합니다.
falsy일 경우는 null, undefined, false, 0, -0, NaN, "", ”, 등이 있습니다.

기본 매개변수(Default Function Parameter)

// 🚨 Default parameter
function printMessage(text = "Nothing to display 😜") {
  console.log(text);
}
printMessage("Hello");
printMessage(null);
printMessage(undefined);
printMessage(0);
printMessage("");

비슷한 개념으로 기본 매개변수도 있습니다.
인자에 Default값을 입력할 수 있어요.
널 병합 연산자, 논리적 OR 연산자와 또 다른 점은 전달 받은 인자의 값이 undefined일 경우에만 Default Parameter을 사용한다는 점입니다.

구조 분해 할당(Object Destructuring)

const person = {
  name: "Julia",
  age: 20,
  phone: "0107777777",
};

// ❌ Bad Code 💩
function displayPerson(person) {
  displayAvatar(person.name);
  displayName(person.name);
  displayProfile(person.name, person.age);
}

displayPerson이라는 함수는 person이라는 인자를 전달받아서 각각 필요한 함수를 호출하면서 person의 name, person의 age를 함께 전달하고 있습니다.
위 코드는 ‘person.‘이 반복적으로 쓰이고 있습니다.

이런 반복을 줄이고자 다음과 같이 함수 안에서 name과 age라는 지역 변수를 설정할 수도 있는데요
위의 코드에 비해서 반복되는 ‘person.‘의 갯수는 적어지긴 했지만 여전히 ‘person.‘이 반복되고 보기에도 코드의 양이 늘어난 것을 볼 수 있습니다.

// ❌ Bad Code 💩
function displayPerson(person) {
  const name = person.name;
  const age = person.age;
  displayAvatar(name);
  displayName(name);
  displayProfile(name, age);
}

이 코드는 구조 분해 할당을 이용하면 조금 더 깨끗하게 변경할 수 있습니다.

// ✅ Good Code ✨
function displayPerson(person) {
  const { name, age } = person;
  displayAvatar(name);
  displayName(name);
  displayProfile(name, age);
}

person에 있는 key와 동일한 이름으로 중괄호(’{}‘}안에 설정해 주면 person 객체에 있는 것들이 자동으로 할당됩니다.
더이상 person은 반복될 필요가 없고 name, age로 간편하게 변수에 접근해서 데이터를 읽을 수 있습니다.

전개 구문(Spread Syntax)

const item = { type: "👔", size: "M" };
const detail = { price: 20, made: "Korea", gender: "M" };

item과 detail이라는 두가지의 객체를 하나로 묶을 수 있는 방법은 어떤 것들이 있을까요?

// ❌ Bad Code 💩
item["price"] = detail.price;

item에 detail의 key 와 value 들을 수동적으로 넣어줄 수 있습니다.
하지만 기존에 존재하는 객체를 변경하는 것은 좋지 않다고 합니다.

// ❌ Bad Code 💩
const newObject = new Object();
newObject["type"] = item.type;
newObject["size"] = item.size;
newObject["price"] = detail.price;
newObject["made"] = detail.made;
newObject["gender"] = detail.gender;
console.log(newObject);

이렇게 기존의 자바스크립트 문법을 이용해서 새로운 객체를 만들어서 객체 각각에 item과 detail에 있는 key와 value들을 수동적으로 하나씩 할당해 주는 방법도 있습니다.
이 코드도 좋은 코드라고 할 수 없습니다.

// ❌ Bad Code 💩
const newObject2 = {
  type: item.type,
  size: item.size,
  price: detail.price,
  made: detail.made,
  gender: detail.gender,
};
console.log(newObject2);

그렇다면 이 코드는 어떨까요?
이전 코드보다는 간단해졌지만 이 코드도 별로 좋지 않아보이네요..!

이렇게 하나씩 수동적으로 복사하는 것이 아니라 조금 더 간편하게 할 수 있는 방법이 있는데요

// ✅ Good Code ✨
const shirt0 = Object.assign(item, detail);
console.log(shirt0);

이렇게 이전 버전에서도 사용할 수 있는 Object.assign() 함수를 이용해서 묶고싶은 item과 detail을 전달하면 shirt0이라는 변수에는 item과 detail의 모든 key와 value들이 합해져서 할당됩니다.

조금 더 최신 문법을 사용하고싶다면 전개 구문을 이용해주면 됩니다.

// ✅ Better! Code ✨
const shirt = { ...item, ...detail, price: 30 };
console.log(shirt);

중괄호(’{}‘)를 이용해서 새로운 객체를 만들어 주고 전개 구문을 이용해서 item과 detail에 있는 모든 key와 value를 가져옵니다.
만약 detail에 있는 price 값을 덮어씌우고 싶다면 뒤에 원하는 값으로 업데이트를 할 수도 있습니다.

전개 구문은 객체 뿐만 아니라 배열에서도 쓸 수 있습니다.

// Spread Syntax - Array
let fruits = ["🍉", "🍊", "🍌"];

// fruits.push('🍓');
fruits = [...fruits, "🍓"];
console.log(fruits);

// fruits.unshift('🍇');
fruits = ["🍇", ...fruits];
console.log(fruits);

fruits라는 배열에 다른 과일을 추가한다고 가정합시다.
기존 배열에 추가하는거라면 push(), unshift()를 사용합니다.
하지만 새로운 버전의 배열로 만들고싶다면 전개구문을 사용합니다.
배열 뒤쪽에 추가를 원햐면 전개 구문 뒤쪽에, 배열 앞쪽에 추가하고 싶다면 전개 구문 앞쪽에 추가해주면 됩니다.
직관적이죠!

const fruits2 = ["🍈", "🍑", "🍍"];

let combined = fruits.concat(fruits2);
combined = [...fruits, "🍒", ...fruits2];
console.log(combined);

그리고 기존의 fruits배열과 fruits2배열을 묶고 싶다면 concat()이라는 배열 API를 사용해도 되고 전개 구문을 이용해서 묶어줄 수도 있습니다.
그리고 중간에 다른 아이템을 추가하고 싶다면 중간에 ’🍒‘를 추가해주면 됩니다.

선택적 체이닝(Optional Chaining)

const bob = {
  name: "Julia",
  age: 20,
};
const anna = {
  name: "Julia",
  age: 20,
  job: {
    title: "Software Engineer",
  },
};

bob과 anna가 있습니다.
bob은 아직 일자리를 구하지 못했고 anna는 일자리를 구했습니다.

// ❌ Bad Code 💩
function displayJobTitle(person) {
  if (person.job && person.job.title) {
    console.log(person.job.title);
  }
}

위 함수는 person을 전달받아서 person에 job이 있고 job에 title이 있다면 그 job의 title을 출력하는 함수입니다.

이렇게 값이 있을수도 있고 없을수도 있는 경우 ’&&‘연산자를 이용하기 보다는 깔끔하게 선택적 체이닝을 이용할 수 있습니다.

// ✅ Good Code ✨
function displayJobTitle(person) {
  if (person.job?.title) {
    console.log(person.job.title);
  }
}

person.job뒤에 물음표(’?‘)를 붙여주면 job이 있다면 job안에 title이 있는지 없는지 검사가 되고 job이 비어있다면 바로 false가 되므로 if문은 실행되지 않습니다.

그리고 선택적 체이닝과 앞에서 소개한 널 병합 연산자를 함께 쓰면 아래와 같은 코드로도 작성이 가능합니다.

// ✅ Good Code ✨
function displayJobTitle(person) {
  const title = person.job?.title ?? "No Job Yet 🔥";
  console.log(title);
}
displayJobTitle(bob);
displayJobTitle(anna);

템플릿 리터럴(Template Literals)

const person = {
  name: "Julia",
  score: 4,
};

// ❌ Bad Code 💩
console.log(
  "Hello " + person.name + ", Your current score is: " + person.score
);

person에 있는 데이터를 다른 문자열과 함께 출력하는 경우에는 ’+‘연산자로 계속해서 연결해주기 보다는 아래와 같이 백틱()키를 이용해서 문장 안에서 필요한 데이터에 접근해서 출력할 수 있습니다.

// ✅ Good Code ✨
console.log(`Hello ${person.name}, Your current score is: ${person.score}`);

여기서도 person이 반복되니까 앞에서 살펴본 구조 분해 할당을 이용해서 name과 score를 각각 할당해준 다음에 출력해주면 조금 더 심플하게 보여줄 수 있습니다.

// ✅ Good Code ✨
const { name, score } = person;
console.log(`Hello ${name}, Your current score is: ${score}`);

그리고 한단계 더 나아가서 이렇게 전역으로 뭔가를 설정하고 코드를 작성하기 보다는 재사용이 가능하도록 함수를 만들면 나중에 두고두고 재사용 할 수 있고
또, 문자열이 변경이 되야한다면 함수 한곳에서만 수정해주면 되니까 함수로 변경해 두는것이 미래의 확장성, 유지보수성에도 좋습니다.

// ✅ Good Code ✨
function greetings(person) {
  const { name, score } = person;
  console.log(`Hello ${name}, Your current score is: ${score}`);
}

반복문(Loops)

const items = [1, 2, 3, 4, 5, 6];

items의 숫자들 중 짝수인 경우에 한해서 4로 곱한 후 총 합을 구한다고 가정해 봅시다.

// ❌ Bad Code 💩
function getAllEvens(items) {
  const result = [];
  for (let i = 0; i < items.length; i++) {
    if (items[i] % 2 === 0) {
      result.push(items[i]);
    }
  }
  return result;
}

function multiplyByFour(items) {
  const result = [];
  for (let i = 0; i < items.length; i++) {
    result.push(items[i] * 4);
  }
  return result;
}

function sumArray(items) {
  let sum = 0;
  for (let i = 0; i < items.length; i++) {
    sum += items[i];
  }
  return sum;
}

const evens = getAllEvens(items);
const multiple = multiplyByFour(evens);
const sum = sumArray(multiple);
console.log(sum);

getAllEvens 함수는 짝수인 경우를 구하는 함수
multiplyByFour 함수는 getAllEvens함수에서 구해진 짝수에 4를 곱해주는 함수
sumArray 함수는 multiplyByFour 함수에서 구해진 값을 모두 더하는 함수 입니다.
위 함수들을 보면 모두 for문을 이용해서 수동적으로 작성된 것을 볼 수 있습니다.
위 함수들을 각각 배열 API를 이용하면 빠르고 간단하게 원하는 값을 얻을 수 있습니다.

// ✅ Good Code ✨
const evens = items.filter((num) => num % 2 === 0);
const multiple = evens.map((num) => num * 4);
const sum = multiple.reduce((a, b) => a + b, 0);
console.log(sum);

위와 같이 items라는 배열에 filter()함수를 이용해서 돌면서 해당하는 숫자만 필터링 하고
map()함수를 이용해서 기존에 있는 값을 하나씩 바꿔줍니다.
그리고 마지막으로 reduce()함수를 사용해서 하나의 결과값으로 반환합니다.
이런 경우에는 함수로 따로 만들어줄 필요도 없습니다!

그리고 배열 API같은 경우는 배열 자기 자신을 항상 리턴하기 때문에 각각 변수에 할당할 필요 없이 아래와 같이 깔끔하게 채이닝을 해줄 수 있습니다.

// ✅ Good Code ✨
const result = items
  .filter((num) => num % 2 === 0)
  .map((num) => num * 4)
  .reduce((a, b) => a + b, 0);
console.log(result);

이렇게 보니까 훨씬 깔끔하고 한눈에 이해하기도 좋습니다!

Promise -> async/await

// ❌ Bad Code 💩
function displayUser() {
  fetchUser() //
    .then((user) => {
      fetchProfile(user) //
        .then((profile) => {
          updateUI(user, profile);
        });
    });
}

이렇게 promise를 계속 중첩해서 사용하면 가독성이 좋지 않고 코드가 꼬이기 쉽습니다.
그래서 두가지 이상의 promise를 연결해서 사용해야 한다면 async/await를 이용하면 조금 더 깔끔하고 또 순차적으로 확인할 수 있기 때문에 가독성도 높여줍니다.

// ✅ Good Code ✨
async function displayUser() {
  const user = await fetchUser();
  const profile = await fetchProfile(user);
  updateUI(user, profile);
}

async/await 은 편하지만 비동기 코드가 동기 코드처럼 읽히게 해주기 때문에 상황에 알맞게 사용해야겠습니다.

Set

const array = ["🐶", "🐱", "🐈", "🐶", "🦮", "🐱"];
console.log(array);

위 배열 안에 중복되는 동물을 제거한 새로운 배열을 만든다고 가정해봅시다.

console.log([...new Set(array)]);

배열이라는 자료구조는 중복을 허용하는 자료구조입니다.
각각의 index에 해당하는 자료가 들어있습니다.
반면 Set()이라는 자료구조는 중복을 허용하지 않습니다.
그래서 array의 배열을 전달해서 새로운 Set()이라는 자료구조를 만들었습니다.
그리고 전개 구문을 용해서 배열안에 아이템들을 각각 나열해주고 배열을 나타내는 대괄호(’[]‘)로 배열로 묶어주었습니다.
설명하면 이렇게 길지만 코드는 한줄!

위 문법들을 잘 활용하면 프로 개발자에 한발 더 다가갈 수 있을 것 같습니다.

꼭 Bad Code와 Good Code를 쳐보면서 신세계를 느껴보세요!!

참고

https://www.youtube.com/watch?v=BUAhpB3FmS4


Written by @Soojin Kim

넥스트이노베이션의 기술, 문화, 뉴스, 행사 등 최신 소식 및 넥스트이노베이션이 겪은 다양한 경험을 공유합니다.