본문 바로가기

Back-End/Node.js

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

반응형

1. ➡ 새로운 문법들

2. ➡ 서버와 통신하기 위해 프론트엔드에서 사용하는 자바스크립트 코드들


1. ES2015+

ES2015(=ES6) 이상의 자바스크립트 문법을 알아야 한다. 노드 6버전부터 ES2015 문법을 사용할 수 있다.

 

1.1 const, let

보통 var로 변수를 선언하지만, 이제는 const와 let으로 대체한다.

  • const: =상수. 한 번 값을 할당하면 다른 값으로 할당할 수 없다. (할당하려 하면 에러 발생함)
  • let: =변수.

이들의 공통적인 특징인 블록 스코프(범위)를 알아보자.

블록 스코프

블록 스코프: if, while, for, function 등에서 볼 수 있는 중괄호({와 }사이)

if (true) {
   var x = 3;
}
console.log(x);	// 3

if (true) {
   const y = 3;
}
console.log(y);	// Uncaught ReferenceError: y is not defined

var와 달리 const, let은 블록 스코프를 가지므로 블록 밖에서는 변수에 접근할 수 없다.

함수 스코프 대신 블록 스코프를 사용하면 호이스팅 같은 문제가 해결되고 코드 관리도 수월해진다.

사용법

기본적으로 const를 사용하고, 다른 값으로 할당해야 하는 상황이 생겼을 때 let으로 변경한다.

 

1.2 템플릿 문자열: `(백틱)

기존의 작은/큰 따옴표로 감싸는 문자열과 달리 ES2015 문법에는 백틱(`)으로 감싸는 새로운 문자열이 생겼다.

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

이전보다 훨씬 깔끔하게 작성할 수 있고, 작은/큰 따옴표도 함께 사용할 수 있다.

 

1.3 객체 리터럴(Object Literal)

객체 리터럴

Object 객체를 생성하기 위해 객체 리터럴 표현(= { ... } )을 이용하는 것. (1.2객체 생성 방법 > 1)리터럴 참고)

전체 객체 멤버들은 { } 내부에 콤마(,)로 구분되어 있고, 각 멤버는 콜론(:)으로 이름과 초기값이 구분된다.

  • 예제
const obj = {
   name: 'daeun',
   age : 10,
   increaseAge(i) {
      this.age + i;
   }
};

객체 리터럴과 Object 생성자

객체 리터럴을 이용하여 객체를 생성하는 방법과 Object 생성자를 이용하여 객체를 생성하는 방법의 내부적인 과정은 동일하다. 일단 (1) Object 생성자를 이용하여 최초의 Object 객체를 생성하고 나서, (2) 리터럴 표현에서 정의한 멤버를 동적으로 추가하는 것이다.

const obj = {};
const obj2 = new Object();

 

위의 obj와 obj2는 동일하다.

 

또한 앞에서 언급한 obj와 동일한 구조의 객체는 객체 리터럴 표현을 이용하여 아래와 같은 방법으로 만들 수도 있다.

const obj = {};
obj.name = 'daeun';
obj.age = 10;
obj.increaseAge = (i) => this.age + i;

 

객체 리터럴을 사용하여 속성에 다른 객체를 할당하는 중첩된 표현도 가능하다. 아래는 obj 객체의 parent 속성에 다른 객체를 할당하는 코드이다.

const anotherName = '자바스크립트';
const obj = {
   name: 'daeun',
   age: 10,
   parent: {
      name: 'yeobin',
      job: 'sister',
   },
   etc() {
      this.name + "의 또 다른 이름: " + anotherName
   }
};

parent의 job 속성은 아래와 같이 접근할 수 있다.

console.log(obj.parent.job);

etc의 속성값처럼 객체 리터럴에서 속성의 값은 고정된 값 뿐만 아니라, 계산된 식을 반환하는 값을 할당할 수도 있다.

(+) Object Literal 참고 자료

ES2015 new 객체 리터럴

객체 리터럴에 편리한 기능들이 추가되었다. 아래 예제를 통해 살펴보자.

var sayNode = function() {
    console.log('Node');
};
var es = 'ES';

before

oldObject 객체에 동적으로 속성을 추가하는 코드

var oldObject = {
    sayJS: function() {
        console.log('JS');
    },
    sayNode: sayNode,
};
oldObject[es + 6] = 'Fantastic';
oldObject.sayNode();    // Node
oldObject.sayJS();  //JS
console.log(oldObject.ES6); // Fantastic

after

const newObject = {
    sayJS() {
        console.log('JS');
    },
    sayNode,
    [es + 6]: 'Fantastic',
};
newObject.sayNode();    // Node
newObject.sayJS();  // JS
console.log(newObject.ES6); // Fantastic

분석하기

  • 이제 sayJS 같은 객체의 메서드에 함수를 연결할 때 클론(:)과 function을 붙이지 않아도 된다.
const newObject = {
    sayJS() {
       console.log('JS');
    },
}
  • 또 sayNode: sayNode 처럼 속성명과 변수명이 동일한 경우에는 한 번만 작성해도 된다. 자바스크립트는 아래와 같은 경우가 많이 나오는데, 이제 코드의 중복을 피할 수 있어서 편리하다.
{ name: name, age: age} // ES5
{ name, age } // ES2015
  • 또한 객체의 속성명도 동적으로 생성할 수 있다. 이전에는 'ES6' 속성명을 만들려면 객체 리터럴(oldObject) 바깥에서 [es + 6]을 해야 했다. 하지만 ES2015에선 객체 리터럴 안에서 동적 속성을 선언할 수 있다. (newObject[es+6])
const newObject = {
    [es + 6]: 'Fantastic',
}

 

1.4 화살표 함수

기존의 function() {} 을 화살표 함수를 이용하여 간단하게 표현할 수 있다. 이를 변수에 대입하면 나중에 재사용할 수 있다.

내부에 return문밖에 없는 경우

function add(x, y) {
    return x + y;
}

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

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

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

add1, add2, add3, add4는 모두 같은 기능을 하는 함수이다. 

function not1(x) {
    return !x;
}

const not2 = x => !x;

not1, not2는 같은 기능을 하는 함수이다.

  • 중괄호 대신 바로 return할 식을 작성하여 코드를 줄일 수 있다.
  • add4처럼 보기 좋게 소괄호로 감쌀 수도 있다.
  • not2처럼 매개변수가 한 개면 매개변수를 소괄호로 묶지 않아도 된다.

this 바인드 방식

before

var relationship1 = {
   name: 'daeun',
   friends: ['nero', 'hero', 'xero'],
   logFriends: function() {
      var that = this;	// relationship1을 가리키는 this를 that에 저장함
      this.friends.forEach(function (friend) {
         console.log(that.name, friend);
      });
   },
};
relationship1.logFriends();

after

const relationship2 = {
    name: 'daeun',
    friends: ['nero', 'hero', 'zero'],
    logFriends() {
        this.friends.forEach(friend => {
            console.log(this.name, friend);
        });
    },
};
relationship2.logFriends();
  • 콘솔 결과
daeun nero
daeun hero
daeun zero
  • before
    • relationship1.logFriends() 안의 forEach문에서는 function 선언문을 사용했다.
    • 각자 다른 함수 스코프의 this를 가지므로 that 이라는 변수를 사용하여 간접적으로 relationship1에 접근한다.
  • after
    • relationship2.logFriends() 안의 forEach문에서 화살표 함수를 사용했다.
    • 바깥 스코프인 logFriends()의 this를 그대로 사용할 수 있다. 즉, 상위 스코프의 this를 그대로 물려받는 것이다.
  • 즉, 기본적으로 화살표 함수를 쓰되, this를 사용해야 하는 경우에는 화살표 함수와 함수 선언문(function) 중에서 하나를 고르면 된다.

상위 스코프의 this를 그대로 물려받을 경우화살표 함수 사용

상위 스코프의 this를 물려받지 않고 해당 함수의 this를 가질 경우함수 선언문(function) 사용

 

1.5 구조분해 할당

객체와 배열로부터 속상이나 요소 꺼내기

객체와 배열로부터 속성이나 요소를 쉽게 꺼낼 수 있다.

before

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;

after

const candyMachine = {
    status: {
        name: 'node',
        count: 5,
    },
    getCandy() {
        this.status.count--;
        return this.status.count;
    },
};
const { getCandy, status: { count }} = candyMachine;
  • candyMachine 객체 안의 속성(getCandy, status.count)을 찾아서 변수(getCandy, count)와 매칭한다. count처럼 여러 단계 안의 속성도 찾을 수 있다.
  • 위 코드를 통해 getCandy와 count 변수를 초기화할 수 있다.
  • 다만, 구조분해 할당을 사용하면 함수의 this가 달라질 수 있다. getCandy 함수를 사용해보면, 달라진 this를 원래대로 바꿔주려면 bind 함수를 따로 사용해야 한다.

 

배열에 대한 구조분해 할당 문법 ➡ 변수명 지어주기

before

var array = ['nodejs',j {}, 10, true];
var node = array[0];
var obj = array[1];
var bool = array[3];

after

const array = ['nodejs', {}, 10, true];
const [node, obj, , bool] = array;
  • const [ 배열의 첫 번째 요소, 두 번째 요소, 세 번째 요소, ... , n 번째 요소 ]
  • 세 번째 요소인 10에는 변수명을 지어주지 않았으므로 10은 무시한다.

구조분해 할당 문법은 코드 줄 수를 상당히 줄여주므로 유용하다. 특히 노드는 모듈 시스템(3.3절 참고)을 사용하므로 이러한 방식을 자주 쓴다.

 

 

2.6 클래스

클래스 문법도 추가 되었다. 그러나 다른 언어처럼 클래스 기반으로 동작하는 게 아니라, 여전히 프로토타입 기반으로 동작한다.

즉, 프로토타입 기반 문법을 보기 좋게 클래스로 바꾼 것이다.

(*프로토타입이란?)

 

프로토타입 상속 예제 코드

before

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

Human 생성자 함수가 있고, 그 함수를 Zero 생성자 함수가 상속한다.

(*생성자 함수란?)

Zero 생성자 함수를 보면 상속받기 위한 코드가 상당히 난해하다. Human.apply와 Object.create 부분이 상속받는 부분이다.

after

 

 

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

프론트엔드에 사용되는 기능들을 설명한다. HTML에서 script 태그 안에 작성하는 부분이다.

 

2.1 AJAX

AJAX = Asynchronous Javascript And XML.

비동기적 웹 서비스를 개발할 때 사용하는 기법 즉, 페이지 이동 없이 서버에 요청을 보내고 응답을 받는 기술이다.

웹 사이트 중에서 페이지 전환 없이 새로운 데이터를 불러오는 사이트들이 대부분 AJAX 기술을 사용하고 있다고 보면 된다.

XMLHttpRequest 객체를 사용하는 Javascript AJAX와, jQuery AJAX 방식이 있는데

보통 AJAX라 하면 jQuery AJAX 방식을 말한다.

이름에는 XML이 들어있지만 최근에는 JSON 포맷을 많이 사용한다.

 

더 자세한 정보는 이전 글을 참고하자.

2021.04.11 - [Web/Node.js] - AJAX란

 

AJAX란

1. AJAX AJAX = Asynchronous Javascript And XML. 비동기적 웹 서비스를 개발할 때 사용하는 기법 즉, 페이지 이동 없이 서버에 요청을 보내고 응답을 받는 기술이다. 웹 사이트 중에서 페이지 전환 없이 새

nno3onn.tistory.com

 

2.2 axios

AJAX 요청은 보통 jQuery를 이용하여 보내는데, jQuery AJAX와 못지 않게 Axios 라이브러리도 많이 사용된다.

브라우저에서는 기본적으로 XMLHttpRequest 객체를 제공하는데, XMLHttpRequest 객체를 사용하여 서버와 통신하는 Javascript AJAX는 사용 방법이 복잡하고 서버에서는 사용할 수 없으므로 불편하다.

따라서 axios 사용법에 대해 배워보자.

 

axios 사용하기

서버에게 요청해야 하므로 HTML 파일에서 작성된다. axios 라이브러리는 다음과 같이 작성하면 사용할 수 있다.

프론트엔드에서 사용하려면 HTML 파일을 하나 만들고, 그 안에 script 태그를 추가하면 된다.

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

 

GET 요청 보내기

요청의 한 종류인 GET 요청을 보내보자. axios.get() 함수의 인수로 요청을 보낼 주소를 넣으면 된다.

 

axios.get의 내부에 new Promise가 들어있으므로 then과 catch를 사용할 수 있다.

result.data에는 서버로부터 보낸 데이터가 들어있고,

then(성공), catch(실패), then(항상) 으로 실행할 수 있다.

axios.get('https://localhost:3000')
    .then((result) => {	// 성공시
        console.log(result);
        console.log(result.data); // {}
    })
    .catch((error) => {	// 실패시
        console.error(error);
    })
    .then(() => { // 항상
        ...
    });

또한 프로미스이므로 async/await 방식으로 변경할 수도 있다.

(익명함수로 작성하였으므로 즉시 실행을 위해 코드를 소괄호로 감싸서 호출해주었다)

(async () => {
    try {
        const result = await axios.get('https://localhost:3000');
        console.log(result);
        console.log(result.data); // {}
    } catch (error) {
        console.error(error);
    }
 })(); 

 

POST 요청 보내기

POST 요청을 하면 데이터를 서버로 보낼 수 있다.

(async () => {
    try {
        const result = axios.post('https://localhost:3000', {
            name: 'nno3onn',
            birth: 1997,
        });
        console.log(result);
        console.log(result.data);  // {}
    } catch (error) {
        console.error(error);
    }
})();

 

 

2.3 FormData

HTML form 태그의 데이터를 동적으로 제어할 수 있다. 주로 AJAX와 함께 사용된다.

 

FormData 사용하기

먼저 FormData 생성자로 formData 객체를 만든다.

  • append('key', 'value')  : key-value 저장하기. 하나의 key에 여러 value를 저장할 수 있다.
  • has('key')  : key가 있는지 없는지 여부 확인하기(boolean)
  • get('key')  : key의 value[0] 하나만 가져오기
  • getAll('key')  : key의 모든 value 가져오기
  • delete('key')  : key 삭제하기
  • set('key', 'value')  : key의 이전 value값을 작성한 value로 덮어쓰기
const formData = new FormData();
formData.append('name', 'nno3onn');
formData.append('item', 'orange');
formData.append('item', 'banana');
formData.has('item'); //true
formData.has('money'); //false;
formData.get('item'); // orange
formData.getAll('item'); // ['orange', 'banana'];
formData.append('test', ['hi', 'bye']);
formData.get('test'); // hi, bye
formData.delete('test');
formData.get('test'); // null
formData.set('item', 'apple');
formData.getAll('item'); // ['apple'];

 

FormData 서버로 보내기

이제 axios로 작성한 FormData를 서버로 보내면 된다.

( async () => {
    try {
        const formData = new FormData();
        formData.append('name', 'nno3onn');
        formData.append('birth', 1997);
        const result = axios.post('https://localhost:3000/api/post/formdata', formData);
        console.log(result);
        console.log(result.data);
    } catch (error) {
        console.error(error);
    }
})();

axios의 두 번째 인수에 데이터를 넣어 보내면 된다.

 

 

2.4 encodeURIComponent, decodeURIComponent

한글 주소를 처리하기 위한 메소드.

 

주소에 한글이 들어가는 경우에 AJAX 요청을 보내게 되면 서버가 한글 주소를 이해하지 못하는 경우가 있다.

이런 경우에 window 객체의 메소드인 encodeURIComponent 메소드를 사용하면 된다.

요청은 encodeURIComponent, 받는 쪽은 decodeURIComponent를 사용하면 된다.

노드에서도 사용할 수 있다.

 

사용하기

한글 주소 부분만 encodeURIComponent / decodeURIComponent 메소드로 감싸면 된다.

( async() => {
    try {
        const result = await axios.get(`localhost:3000/api/search/${encodeURIComponent('노드')}`);
        console.log(result);
        console.log(result.data); // {}
    } catch (error) {
        console.error(error);
    }
})();

이렇게 하면 '노드'라는 한글 주소가 %EB%85%B8%EB%93%9C 라는 문자열로 변환된다.

받는 쪽에서도 문자열을 한글 주소로 변환하자.

decodeURIComponent('%EB%85%B8%EB%93%9C'); // 노드

한글이 다시 원상복구되었다. 

 

 

2.5 데이터 속성(=data-)과 dataset

데이터 속성

노드를 웹 서버로 사용하게되면 클라이언트(프론트엔드)와 빈번하게 데이터를 주고 받게 되는데,

이때 서버에서 보내준 데이터를 프론트엔드 어디에 넣어야 할지 고민하게 된다.

 

프론트엔드에 데이터를 보낼 때 가장 먼저 고려해야 할 것은 "보안"이다. 특히 비밀번호와 같이 민감한 데이터는 프론트엔드로 보내면 안된다.

보안과 무관한 데이터들은 자유롭게 프론트엔드로 보내도 된다. 그런 데이터들은 자바스크립트 변수에 저장해도 되지만, HTML5에도 HTML과 관련된 데이터를 저장하는 공식적인 방법이 바로 데이터 속성(data attribute)이다.

 

<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' }
</script>

HTML 태그의 속성으로 'data-'로 시작하는 데이터 속성들(data-id, data-user-job)을 넣었다.

나중에 이 데이터들을 사용하여 서버에 요청을 보내게 된다.

 

.dataset

첫 번째 태그의 데이터 속성에 접근한다.

 

데이터 속성의 장점

자바스크립트로 쉽게 접근할 수 있다.

console.log(document.querySelector('li').dataset);
// { id: '1', userJob: 'programmer' }

<script> 태그를 보면 dataset 속성을 통해 첫 번째 <li> 태그의 데이터 속성에 접근하고 있다.

단, 데이터 속성 이름이 조금 변경된다. 앞의 접두어 data- 는 사라지고 - 뒤에 위치한 글자는 대문자가 된다.

따라서 data-id는 id로, data-user-job은 userJob이 된다.

 

반대로 dataset에 데이터를 넣어도 HTML 태그에 반영된다.

dataset.monthSalary = 10000; 

다음과 같이 작성하게 되면

data-month-salary='10000'

이라는 속성이 생긴다.

반응형