상세 컨텐츠

본문 제목

[Node.js] 2장 알아두어야 할 자바스크립트

21-22/21-22 Node.js

by Kimpeep 2021. 10. 4. 14:01

본문

728x90

1. ES2015+

  • 2015년을 기점으로 매년 자바스크립트 문법 변경 사항이 발표 -> ES2015(=ES6)의 등장
  • 노드 6 버전부터 ES2015 문법 사용 가능
  • ES2015 이상의 자바스크립트를 통틀어 ES2015+라고 표현

(1) const, let

  • 보통 var로 변수를 선언하지만 이제 const와 let이 대체
  • const와 let은 블록 스코프(범위)를 가짐
  • const는 상수처럼 선언 후 값을 바꿀 수 없음. let은 바꿀 수 있음.
아래 코드들은 크롬 개발자 도구(F12)의 Console 탭에 적었습니다.
Enter를 누르면 코드가 실행되고,
코드를 실행하지 않고 줄 바꿈을 하려면 Shift+Enter를 입력합니다.
if (true) {
	var x = 3;
    }
console.log(x); // 3
   
if (true) {
   	const y = 3;
   }
console.log(y); // Uncaught ReferenceError: y is not defined

if (true) {
   	let z = 'string';
   }
console.log(z); // Uncaught ReferenceError: z is not defined
const a = 0;
a = 1; // Uncaught TypeError: Assignment to constant variable.

let b = 0;
b = 1; // 1

const c; // Uncaught SyntaxError: Missing initializer in const declaration
자바스크립트에서 한 번 초기화했던 변수에 다른 값을 할당하는 일은 적다고 합니다. 그러므로 변수 선언 시에는 기본적으로 const를 사용합니다.

 

(2) 템플릿 문자열

  • 큰 따옴표(")나 작은 따옴표(')로 문자열을 감쌀 수 있지만 이제는 백틱(`, Tab위에 있음)으로도 감쌀 수 있음
  • 이로 인해 문자열 안에 변수를 넣을 수 있음
var num1 = 1;
var num2 = 2;
var result = 3;
var string1 = num1 + ' 더하기 ' + num2 + '는 \'' + result + '\'';
console.log(string1); // 1 더하기 2는 '3'

const string2 = `${num1} 더하기 ${num2}는 '${result}'`;
console.log(string2); // 1 더하기 2는 '3'

 

(3) 객체 리터럴

  • 객체의 속성명을 동적으로 생성할 수 있음
  • 두 코드를 비교해보면, sayJS 같은 객체의 매서드에 함수를 연결할 때 더 이상은 콜론(:)과 function을 붙이지 않아도 되고 sayNode : sayNode처럼 속성명과 변수명이 동일한 경우에는 한 번만 써도 가능
  • 코드의 중복을 피하고 코드의 양을 줄이는데 편리함
var sayNode = function() {
  console.log('Node');
};
var es = 'ES';
var oldObject = {
  sayJS: function() {
    console.log('JS');
  },
  sayNode: sayNode,
};
oldObject[es + 6] = 'Fantastic';
oldObject.sayNode(); // Node
oldObject.sayJS(); // JS
console.log(oldObject.ES6); // Fantastic
const newObject = {
  sayJS() {
    console.log('JS');
  },
  sayNode,
  [es + 6]: 'Fantastic',
};
newObject.sayNode(); // Node
newObject.sayJS(); // JS
console.log(newObject.ES6); // Fantastic


(4) 화살표 함수

  • 화살표 함수라는 새로운 함수가 추가
function add1(x, y) {
  return x + y;
}

const add2 = (x, y) => {
  return x + y;
};

const add3 = (x, y) => x + y;

const add4 = (x, y) => (x + y);
function not1(x) {
  return !x;
}

const not2 = x => !x;
  • function 선언 대신 => 기호로 함수를 선언
  • add3과 add4와 같이 화살표 함수 내부에 return 문밖에 없는 경우에는 return문을 줄일 수 있음
  • not2처럼 매개변수가 한 개이면 소괄호로 묶어주지 않아도 됨

 

  • 기존 function과 다른 this 바인드 방식
var relationship1 = {
  name: 'zero',
  friends: ['nero', 'hero', 'xero'],
  logFriends: function () {
    var that = this; // relationship1을 가리키는 this를 that에 저장
    this.friends.forEach(function (friend) {
      console.log(that.name, friend);
    });
  },
};
relationship1.logFriends();
const relationship2 = {
  name: 'zero',
  friends: ['nero', 'hero', 'xero'],
  logFriends() {
    this.friends.forEach(friend => {
      console.log(this.name, friend);
    });
  },
};
relationship2.logFriends();
  • function 선언문을 사용한 relationship1.logFriends() 안에서 각자 다른 함수 스코프의 this를 가지므로 that이라는 변수로 relationship1에 직접적으로 접근하고 있음
  • 화살표 함수를 사용한 relationship2.logFriends() 안에서는 this를 그대로 사용함. 상위 스코프의 this를 그대로 물려받는 것을 볼 수 있음 

(5) 구조 분해 할당

  • 구조분해 할당을 통해 객체와 배열로부터 속성이나 요소를 손쉽게 꺼낼 수 있음
var candyMachine = {
  status: {
    name: 'node',
    count: 5,
  },
  getCandy: function () {
    this.status.count--;
    return this.status.count;
  },
};
var getCandy = candyMachine.getCandy;
var count = candyMachine.status.count;
const candyMachine = {
  status: {
    name: 'node',
    count: 5,
  },
  getCandy() {
    this.status.count--;
    return this.status.count;
  },
};
const { getCandy, status: { count } } = candyMachine;
  • 위 코드와 아래 코드의 getCandy와 count는 같은데 서로 할당하는 방식만 다름
const array = ['nodejs', {}, 10, true];
const [node, obj, , bool] = array;
  • 배열에서의 구조분해 할당은 위치만 맞으면 할당할 수 있음

 

(6) 클래스

  • 자바스크립트에 클래스 문법이 추가
  • 하지만 클래스 기반으로 동작하지 않고 프로토타입 기반으로 동작
var Human = function(type) {
  this.type = type || 'human';
};

Human.isHuman = function(human) {
  return human instanceof Human;
}

Human.prototype.breathe = function() {
  alert('h-a-a-a-m');
};

var Zero = function(type, firstName, lastName) {
  Human.apply(this, arguments);
  this.firstName = firstName;
  this.lastName = lastName;
};

Zero.prototype = Object.create(Human.prototype);
Zero.prototype.constructor = Zero; // 상속하는 부분
Zero.prototype.sayName = function() {
  alert(this.firstName + ' ' + this.lastName);
};

var oldZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(oldZero); // true
  • 프로토타입 상속은 위 코드와 같이 상당히 복잡
class Human {
  constructor(type = 'human') {
  this.type = type;
  }

  static isHuman(human) {
    return human instanceof Human;
  }

  breathe() {
    alert('h-a-a-a-m');
  }
}

class Zero extends Human {
  constructor(type, firstName, lastName) {
    super(type);
    this.firstName = firstName;
    this.lastName = lastName;
  }

  sayName() {
    super.breathe();
    alert(`${this.firstName} ${this.lastName}`);
  }
}

const newZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(newZero); // true
  • 클래스 기반 코드
  • 생성자 함수는 constructor 안에 들어갔고 extends 키워드로 쉽게 상속 가능
  • 클래스에 소속된 함수들과 속성들을 한눈에 살피기 편리함

 

(7) 프로미스

  • 자바스크립트와 노드에서는 주로 비동기를 접함
  • 순서대로 호출해야 하는 상황에서 콜백 함수를 자주 사용하는데 콜백 지옥 현상이 나타났음
  • 이를 극복하기 위해 프로미스(Promise) 객체 등장
const condition = true; // true면 resolve, false면 reject
const promise = new Promise((resolve, reject) => {
  if (condition) {
    resolve('성공');
  } else {
    reject('실패');
  }
});
// 다른 코드가 들어갈 수 있음
promise
  .then((message) => {
    console.log(message); // 성공(resolve)한 경우 실행
  })
  .catch((error) => {
    console.error(error); // 실패(reject)한 경우 실행
  })
  .finally(() => { // 끝나고 무조건 실행
    console.log('무조건');
});
  • 프로미스 내부에서 resolve가 호출되면 then이 실행되고 성공했을 때를 말함
  • reject가 호출되면 catch가 실행되고 실패했을 때를 말함
  • finally는 성공/실패 여부와 상관없이 언제나 실행됨
const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
Promise.all([promise1, promise2])
  .then((result) => {
    console.log(result); // ['성공1', '성공2'];
  })
  .catch((error) => {
    console.error(error);
  });
  • 위와 같이 다중 프로미스가 쓰일 때는 Promise.all을 활용하면 간편함

 

(8) async/await

  • async/awiat문법은 프로미스를 사용한 코드를 한 번 더 깔끔하게 줄임
function findAndSaveUser(Users) {
  Users.findOne({})
    .then((user) => {
      user.name = 'zero';
      return user.save();
    })
    .then((user) => {
      return Users.findOne({ gender: 'm' });
    })
    .then((user) => {
      // 생략
    })
    .catch(err => {
      console.error(err);
    });
}
async function findAndSaveUser(Users) {
  let user = await Users.findOne({});
  user.name = 'zero';
  user = await user.save();
  user = await Users.findOne({ gender: 'm' });
  // 생략
}
  • 프로미스만 사용한 코드보다 훨씬 짧아진 것을 볼 수 있음
  • 중복되는 then을 처리
async function findAndSaveUser(Users) {
  try {
    let user = await Users.findOne({});
    user.name = 'zero';
    user = await user.save();
    user = await Users.findOne({ gender: 'm' });
    // 생략
  } catch (error) {
    console.error(error);
  }
}
  • 에러를 처리하는 부분이 없었으므로 추가로 try/catch 문을 넣어줘야 함

 


2. 프런트엔드 자바스크립트

  • 프런트엔드를 깊게 다루지 않지만 예제들의 프런트엔드에 사용되는 기능들을 설명
  • HTML에서 script 태그 안에 작성하는 부분

(1) AJAX

  • AJAX(Asynchronous Javascript And XML)는 비동기적 웹 서비스를 개발할 때 사용하는 기법
  • XML을 꼭 사용해야 하는 것은 아니고 요즘에는 JSON을 많이 사용
  • 보통 AJAX 요청은 jQueryaxios 같은 라이브러리를 이용해서 보내지만 복잡하고 서버에서는 사용할 수 없으므로 여기에서는 전반적으로 axios를 사용
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Page Title</title>
    <!--AJAX요청: axios-->
    <script src="https://unpkg.com/axios/dist/axios.min.js">
      // get 요청 보내기
      axios.get('https://www.zerocho.com/api/get')
      .then((result) => {
        console.log(result);
        console.log(result.data); // {}
      })
      .catch((error) => {
        console.error(error);
      });
    
      // Promise이므로 async/await 방식으로 변경 가능
      (async () => {
      try {
        const result = await axios.get('https://www.zerocho.com/api/get');
        console.log(result);
        console.log(result.data); // {}
      } catch (error) {
        console.error(error);
      }
    })();

    // Post 방식의 요청 보내기
    (async () => {
      try {
        const result = await axios.post('https://www.zerocho.com/api/post/json', {
          name: 'zerocho',
          birth: 1994,
        });
        console.log(result);
        console.log(result.data); // {}
      } catch (error) {
        console.error(error);
      }
    })();
    
</script>
</head>
<body>
    <h1>AJAX 예제</h1>
</body>
</html>
  • script 태그에 src속성으로 axios요청을 보냄
  • get 요청은 서버에서 데이터를 받아 내리는 요청이고 result.data에는 서버로부터 보낸 데이터가 들어있음
  • axios.get 내부에 프로미스 객체가 있으므로 then과 catch를 사용할 수 있고 async/awiat 방식으로 변경할 수 있음
  • post 요청은 서버에 데이터를 올리는 요청이고 두 번째 인자로 데이터를 넣어 보냄

 

(2) FormData

  • HTML form 태그의 데이터를 동적으로 제어할 수 있는 기능
  • 생성된 FormData 객체에 키-값 형식의 데이터를 저장
const formData = new FormData();
formData.append('name', 'zerocho');
formData.append('item', 'orange');
formData.append('item', 'melon');
formData.has('item'); // true
formData.has('money'); // false;
formData.get('item'); // orange
formData.getAll('item'); // ['orange', 'melon'];
formData.append('test', ['hi', 'zero']);
formData.get('test'); // hi, zero
formData.delete('test');
formData.get('test'); // null
formData.set('item', 'apple');
formData.getAll('item'); // ['apple'];
  • 이렇게 FormData 값을 저장했다면 axios로 폼 데이터를 서버에 보냄
(async () => {
  try {
    const formData = new FormData();
    formData.append('name', 'zerocho');
    formData.append('birth', 1994);
    const result = await axios.post('https://www.zerocho.com/api/post/formdata', formData);
    console.log(result);
    console.log(result.data);
  } catch (error) {
    console.error(error);
  }
})();

 

(3) encodeURIComponent, decodeURIComponent

  • AJAX요청을 보낼 때, 주소에 한글이 있는 경우에 서버가 이해하지 못하는 경우가 있음
  • 이때 window 객체의 메서드인 encodeURIComponent 메서드를 사용
(async () => {
  try {
    const result = await axios.get(`https://www.zerocho.com/api/search/${encodeURIComponent('노드')}`);
    console.log(result);
    console.log(result.data); // {}
  } catch (error) {
    console.error(error);
  }
})();
  • 노드라는 한글 주소가 %EB%85%B8%EB%93%9C라는 문자열로 변환
  • 받는 쪽에서는 decodeURIComponent를 사용
decodeURIComponent('%EB%85%B8%EB%93%9C'); // 노드

 

(4) 데이터 속성과 dataset

  • 보안과 무관한 데이터들을 프런트엔드로 보냄
  • 자바스크립트 변수에 저장할 수 있지만 HTML 태그에 데이터 속성으로도 데이터를 저장할 수 있음
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Page Title</title>
    <script src=''></script>
</head>
<body>
    <!--데이터 속성과 dataset-->
    <ul>
        <li data-id="1" data-user-job="programmer">Zero</li>
        <li data-id="2" data-user-job="designer">Nero</li>
        <li data-id="3" data-user-job="programmer">Hero</li>
        <li data-id="4" data-user-job="ceo">Kero</li>
      </ul>
      <script> 
        console.log(document.querySelector('li').dataset);
        // { id: '1', userJob: 'programmer' }
        
        document.querySelector('li').dataset.monthSalary = 10000;
        console.log(document.querySelector('li').dataset);
        // { id: "1", monthSalary: "10000", userJob: "programmer" }
      </script>
    
</body>
</html>
  • HTML 태그 안에 data-가 붙어있는 속성들이 데이터 속성
  • data-user-job 속성이 자바스크립트로 접근할 때 usesrJob으로 이름이 바뀜
  • 자바스크립트에서 데이터를 dataset에 넣어도 HTML 태그에 반영됨

 

 

Corner Node.js

Editor ojo

728x90

관련글 더보기