2. 알아두어야할 자바스크립트
포스트
취소

2. 알아두어야할 자바스크립트

기존 블로그에 작성했던 포스트를 이전한 글입니다.

해당 포스트는 NODEJS를 학습하며 정리한 내용에 대한 포스트입니다.




🌈 알아두어야할 자바스크립트

💻 호출 스택

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function first() {
  second();
  console.log('첫 번째');
}
function second() {
  third();
  console.log('두 번째');
}
function third() {
  console.log('세 번째');
}

first();

// 세 번째 -> 두 번째 -> 첫 번째

쉽게 파악이 안된다면 호출스택을 머리속으로 그려보자

호출 스택

호출 스택(함수의 호출, 자료구조의 스택)

  • Anonymous는 가상의 전역 컨텍스트(항상 있다고 생각하는게 좋음, 실행이 모두 끝나면 사라짐)

  • 함수 호출 순서대로 쌓이고, 역순으로 실행됨

  • 함수 실행이 완료되면 스택에서 빠짐

  • LIFO 구조라서 스택이라고 불림




💻 이벤트 루프

이벤트 루프

  • 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 담당

  • 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프라고 부른다.

백그라운드

  • setTimeout 같은 타이머나 이벤트 리스너들이 대기하는 곳

  • 자바스크립트가 아닌 다른 언어로 작성된 프로그램이라고 봐도 무방

  • 여러 작업이 동시에 실행될 수 있음

태스크 큐

  • 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보냄

  • 정해진 순서대로 콜백들이 줄을 서 있으므로 콜백 큐 라고도 함

  • 콜백들은 보통 완료된 순서대로 줄을 서 있지만, 특정한 경우 순서가 바뀌기도 함

1
2
3
4
5
6
7
8
function run() {
  console.log('3초 후 실행');
}
console.log('시작');
setTimeout(run, 3000);
console.log('');

// 시작 -> 끝 -> 3초 후 실행

비동기 함수 setTimeout(run, 3000)은 호출되면 백그라운드에 쌓인다.

호출스택에 다음 코드인 console.log('끝') 이 불려서 실행되는 와중에도 백그라운드에서 3초를 세며 자신의 동작인 run을 대기한다. ( 호출스택과 백그라운드가 동시에 작동된다.)

3초가 지나면 백그라운드에 남아있는 동작 run이 태스크 큐로 이동한다.

호출 스택에 모든게 끝나서 텅텅 비어있다면(annonymous 포함) run을 호츨스택에 올려 console.log('3초 후 실행')이 동작한다.

Promise의 then/catch가 setTimeout보다 더 먼저 작동함(새치기)




💻 화살표 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let relationship1 = {
  name: 'zero',
  friends: ['nero', 'hero', 'xero'],
  logFriends: function () {
    let that = this; // relationship1을 가리키는 this를 that에 저장
    this.friends.forEach(function (friend) {
      console.log(that.name, friend);
    });
  },
};
relationship1.logFriends();
// zero nero
// zero hero
// zero xero

화살표 함수가 기존 function() {}을 대체하는건 아님(this가 달라짐)

  • logFriends 메소드의 this값에 주목

  • forEach의 function의 this와 logFriends의 this는 다름

  • that이라는 중간변수를 이용해서 logFriends의 this를 전달

1
2
3
4
5
6
7
8
9
10
let relationship2 = {
  name: 'zero',
  friends: ['nero', 'hero', 'xero'],
  logFriends() {
    this.friends.forEach(friend => {
      console.log(this.name, friend);
    });
  },
};
relationship2.logFriends();

forEach의 인자로 화살표 함수가 들어간 것에 주목

  • forEach의 화살표함수의 this와 logFriends의 this가 같아짐

  • 화살표 함수는 자신을 포함하는 함수의 this를 물려받음

  • 물려받고 싶지 않을 때 function() {}을 사용

this를 쓸거면 기존함수, this를 쓰지 않아도 되면 화살표함수 사용을 권장한다. (오해 방지)




💻 클래스

프로토타입 문법을 깔끔하게 작성할 수 있는 Class 문법 도입

Constructor(생성자), Extends(상속) 등을 깔끔하게 처리할 수 있음

코드가 그룹화되어 가독성이 향상됨




💻 Promise

콜백 헬이라고 불리는 지저분한 자바스크립트 코드의 해결책

  • 프로미스? 내용이 실행은 되었지만 결과를 아직 반환하지 않은 객체

  • Then을 붙이면 결과를 반환함

  • 실행이 완료되지 않았으면 완료된 후에 Then 내부 함수가 실행됨

  • Resolve(성공리턴값) -> then으로 연결

  • Reject(실패리턴값) -> catch로 연결

Finally 부분은 무조건 실행됨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
})

Promise.all(배열): 여러개의 프로미스를 동시에 실행

  • 하나라도 실패하면 catch로 감

  • allSettled로 실패한 것만 추려낼 수 있음




💻 async/await

변수 = await 프로미스; 인 경우 프로미스가 resolve된 값이 변수에 저장

변수 await 값인 경우 그 값이 변수에 저장

1
2
3
4
5
6
7
async function findAndSaveUser(Users) {
  let user = await Users.findOne({});
  user.name = 'zero';
  user = awajit user.save();
  user = await Users.findOne({ gender: 'm' });
// 생략
}




💻 널 병합/옵셔널 체이닝

🍳 ?? (널 병합(nullish coalescing))

널 병합 연산자는 주로 ||연산자 대용으로 사용된다.

FALSY 값 (0, ‘’, false, NaN, null, undefined) 중 nullundefined만 따로 구분한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const a = 0;
const b = a || 3; // || 연산자는 falsy 값이면 뒤로 넘어감
console.log(b); // 3

const c = 0;
const d = c ?? 3; // ?? 연산자는 null과 undefined일 때만 뒤로 넘어감
console.log(d); // 0;

const e = null;
const f = e ?? 3;
console.log(f); // 3;

const g = undefined;
const h = g ?? 3;
console.log(h); // 3;


🍳 ?. (옵셔널 체이닝(optional chaining))

옵셔널 체이닝 연산자는 null이나 undefined의 속성을 조회하는 경우 에러가 발생하는 것을 막는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const a = {}
a.b; // a가 객체이므로 문제없음

const c = null;
try {
  c.d;
} catch (e) {
  console.error(e); // TypeError: Cannot read properties of null (reading 'd')
}
c?.d; // 문제없음

try {
  c.f();
} catch (e) {
  console.error(e); // TypeError: Cannot read properties of null (reading 'f')
}
c?.f(); // 문제없음

try {
  c[0];
} catch (e) {
  console.error(e); // TypeError: Cannot read properties of null (reading '0')
}
c?.[0]; // 문제없음




💻 AJAX

서버로 요청을 보내는 코드

  • 라이브러리 없이는 브라우저가 지원하는 XMLHttpRequest 객체 이용

  • AJAX 요청 시 Axios 라이브러릴 사용하는게 편함




💻 FormData

HTML form 태그에 담긴 데이터를 AJAX 요청으로 보내고 싶은 경우

  • FormData 객체 이용

FormData 메서드

  • Append 로 데이터를 하나씩 추가

  • has 로 데이터 존재 여부 확인

  • Get 으로 데이터 조회

  • GetAll 로 데이터 모두 조회

  • delete 로 데이터 삭제

  • set 으로 데이터 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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'];
1
2
3
4
5
6
7
8
9
10
11
12
(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);
  }
})();




💻 encodeURIComponent, decodeURIComponent

한글 입력을 서버가 처리하지 못하는 경우 발생

  • encodeURIComponent 로 감싸줘서 처리
1
2
3
4
5
6
7
8
9
(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);
  }
})();

받는 쪽에서는 decodeURIComponent를 사용

역시 브라우저뿐만 아니라 노드에서도 사용할 수 있다.

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




💻 데이터 속성과 dataset

HTML 태그에 데이터를 저장하는 방법

  • 서버의 데이터를 프론트엔드로 내려줄 때 사용

  • 태그 속성으로 data-속성명

  • 자바스크립트에서 태그.dataset.속성명 으로 접근 가능

    • data-user-job => dataset.userjob

    • data-id => dataset.id

  • 반대로 자바스크립트 dataset에 값을 넣으면 data-속성이 생김

    • dataset.monthSalary = 10000 => data-month-salary=”10000”
1
2
3
4
5
6
7
8
9
10
<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>




💻 함께 보면 좋은 자료

MDN 자바스크립트(저자의 추천)

ES 상세 후보군

ES2015+ 브라우저/서버 호환 여부

브라우저별 기능 지원 여부 확인

노드 버전별 ECMAScript 스펙

AJAX 설명

axios

FormData 설명

ESLint 툴

에어비앤비 코딩 스타일

저자의 블로그 ES2015+

모던 자바스크립트 튜토리얼




📚 레퍼런스

조현영. Node.js 교과서 = Node.js Textbook / 조현영 지음 (2022). Print.

[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.