상세 컨텐츠

본문 제목

[노드 1팀] 2장. 알아둬야 할 JavaScript

24-25/Node.js 1

by gooroominuna 2024. 10. 11. 10:01

본문

728x90

 

2.1. ES2015+

변경 사항을 중심으로 ES2015 이상의 자바스크립트 문법에 대해 정리한다.

const / let 

  • var 함수 스코프를 갖는 반면, const와 let 블록 스코프를 갖는다.
    if (true) {var x = 3;}
    console.log(x); // 3
    
    if (true) {const y = 3;}
    console.log(y);	// ReferenceError: y is not defined
     
  • const로 선언한 변수는 재할당할 수 없고, 선언할 때 반드시 값을 할당해야 한다.
    const a = 0;
    a = 1; //TypeError: Assignment to constant variable
    
    const c; // SyntaxError: Missing initializer in const declaration

템플릿 문자열 

  • 백틱(`)으로 문자열을 감싸고, ${ } 안에 변수를 삽입해 문자열 안에 변수를 넣을 수 있다.
    const num3 = 1;
    const num4 = 2;
    const result2 = 3;
    
    console.log(`${num3} 더하기 ${num4}는 ' ${result2}'`); // 1 더하기 2는 '3'

객체 리터럴 

  • 객체의 메서드에 함수를 연결할 때 콜론(:)과 function 키워드를 생략할 수 있다.
    const newObject = {
        sayJS() {console.log('JS');}
    };
  • 속성명과 변수명이 동일한 경우에는 한 번만 작성해도 된다.
    var sayNode = function() {console.log('Node');};
    
    const newObject = {
        sayNode
    };
    
    newObject.sayJS();	// JS
  • 아래 예시의 ES6와 같은 동적 속성을 객체 리터럴 안에서 선언할 수 있다.
    var es = 'ES'
    
    const newObject = {
        [es + 6] : 'Fantastic'
    };
    
    console.log(newObject.ES6); // Fantastic

화살표 함수

  • function 대신 => 기호로 함수를 선언할 수 있고, 변수에 대입하여 재사용할 수 있다.
    function add1(x, y) {return x + y;}
     
    const add2 = (x, y) => {return x + y;};
  • 내부에 return문 밖에 없는 경우, return 키워드와 중괄호를 생략할 수 있다.
    const add3 = (x, y) => x + y;
    
    const add4 = (x, y) => (x + y);
  • 매개변수가 한 개이면 소괄호를 생략할 수 있다.
    function not1(x) {return !x;}
    
    const not2 = x => !x ;
     
  • function 선언문과 달리, 화살표 함수는 상위 스코프의 this를 그대로 물려받는다.
    var relationship1 = {
        name : 'zero', 
        friends : ['nero', 'hero', 'xero'],
        logFriends : function() {
            // this는 relationship1을 가리킨다.
            var that = this;    
            // this는 logFriends를 가리킨다.
            this.friends.forEach(function(friend){console.log(that.name, friend)});
        }
    };
    
    const relationship2 = {
        name : 'zero',
        friends : ['nero', 'hero', 'xero'],
        // 두 this 모두 relationship2를 가리킨다. 
        logFriends() {this.friends.forEach(friend => console.log(this.name, friend))}
    };
     

구조 분해 할당 

  • 구조 분해 할당을 사용하면 객체와 배열로부터 속성이나 요소를 쉽게 추출할 수 있다. 
  • 아래 예시 코드는 candyMachine 객체의 속성을 변수에 할당한다. 
    const candyMachine = {
        status : {
            name : 'node',
            count : 5
        },
        getCandy() {
            this.status.count--;
            return this.status.count;
        }
    };
    
    // candyMachine 객체의 속성을 변수와 매칭, getCandy와 count 변수 초기화 
    const {getCandy, status : {count}} = candyMachine;
    
    
    // bind를 사용해 this를 고정
    const boundGetCandy = getCandy.bind(candyMachine);
     
  • 배열에 대한 구조 분해 할당도 존재한다.
    const array = ['nodejs', {}, 10, true];
    const [node, obj, , bool] = array;
     

클래스 

  • 클래스 문법이 추가되었지만, 자바스크립트는 여전히 프로토타입 기반으로 동작한다.
    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
     

프로미스 

① 프로미스의 개념 

  • 프로미스는 실행은 바로 하되 결과값은 나중에 받는 객체이다. 
    아래 예시에서 new Promise는 바로 실행되지만, 결과값은 then이나 catch 메서드를 통해 받는다. 
  • new Promise로 프로미스를 생성한다.
    const condition = true; // true이면 resolve, false이면 reject
    
    const promise = new Promise((resolve, reject) => { 
        if (condition) {resolve('성공');}
        else {reject('실패');}
    });
     
  • 프로미스 내부에서 resolve가 호출되면 then, reject가 호출되면 catch 메서드가 실행된다.
    resolve와 reject의 인수는 각각 then과 catch의 매개변수로 전달된다. 
    promise
        .then((message) => {	// 성공(resolve)한 경우 실행
            console.log(message);   // 성공
        })
        .catch((error) => {		// 실패(reject)한 경우 실행
            console.error(error);   // 실패
        })
        .finally(() => {
            console.log('무조건'); // 성공, 실패에 관계 없이 실행
        });

② then이나 catch에 다른 then이나 catch를 연결할 수 있다. 

  • 이때 이전 then의 return 값은 다음 then의 매개변수로 전달되고,
    then에서 프로미스를 return하면 프로미스가 수행된 후 다음 then이나 catch가 호출된다. 
    promise
        .then((message) => {
            return new Promise((resolve, reject) => {resolve(message);});
        })      
        // 처음 then에서 message를 resolve하면 다음 then에서 message2로 받을 수 있다.
              
        .then((message2) => {
            console.log(message2);
            return new Promise((resolve, reject) => {resolve(message2);});
        })
              
        // 다시 message2를 resolve한 것을 다음 then에서 message3으로 받았다.
        // then에서 new Promise를 return해야 다음 then에서 받을 수 있다 
              
        .then((message3) => {
            console.log(message3);
        })
            
        .catch((error) => {
            console.error(error);
        });
     

③ 프로미스는 콜백이 여러 번 중첩되는 문제를 해결할 수 있다. 

  • 단, 메서드가 내부적으로 프로미스 객체를 가지고 있는 경우에만 사용할 수 있다.
    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);
            });
        }

④ Promise.all, Promise.resolve, Promise.reject, Promise.allSettled

  • Promise.all : 프로미스 여러 개를 한 번에 실행할 수 있다. 프로미스 중 하나라도 reject 되면 catch로 넘어간다. 
    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.resolve : 즉시 resolve하는 프로미스를 만든다. 
  • Promise.reject : 즉시 reject  하는 프로미스를 만든다. 
  • Promise.allSettled : status를 통해 어떤 프로미스가 reject 되었는지 알 수 있다.
    const promise1 = Promise.resolve('성공1');
    const promise2 = Promise.reject('실패2');
    const promise3 = Promise.resolve('성공3');
    
    Promise.allSettled([promise1, promise2, promise3])
    
        .then((result) => {
            console.log(result);
        /* [
        *    { status: 'fulfilled', value: '성공1' },
        *    { status: 'rejected', reason: '실패2' },
        *    { status: 'fulfilled', value: '성공3' }
        *  ]
        */
          })
          .catch((error) => {
            console.error(error);
          });
  • reject Promise catch를 달지 않으면 에러가 발생한다. 
     try {
          Promise.reject('에러');
        } catch (e) {
          console.error(e); // UnhandledPromiseRejection: This error originated either by throwing inside...
        }
        
        Promise.reject('에러').catch(() => {
          // catch 메서드를 붙이면 에러가 발생하지 않음
        })
     

async / await  

  • async/await을 이용하면 프로미스를 사용한 코드를 더 간결하게 정리할 수 있다.
    // (1) async funtion으로 함수 선언
    async function findAndSaveUser(Users) {
        try {
    // (2) 프로미스 앞에 await -> 프로미스가 resolve될 때까지 기다린 후 user 변수 초기화
            let user = await Users.findOne({});
            user.name = 'zero';
            user = await user.save();
            user = await Users.findOne({gender : 'm'});
            // 생략
        }
        catch (error) {
            console.error(error);
        }
    }
    
    // 화살표 함수로 작성 
    
    const findAndSaveUser = async (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);
      }
    };
  • for await of 문을 사용해서 프로미스 배열을 순회한다.  
    const promise1 = Promise.resolve('성공1');
    const promise2 = Promise.resolve('성공2');
    (async () => {
      for await (promise of [promise1, promise2]) {
        console.log(promise);
      }
    })();
     

Map / Set  

  Map : 객체와 유사한 자료구조로, 속성들 간의 순서를 보장한다. 

  • 생성 
    const m = new Map();
     
  • set(키, 값) : Map에 속성을 추가한다. 
    m.set('a', 'b');
    m.set(3, 'c');
    const d = {};
    m.set(d, 'e');          // 객체도 가능
  • get(키) : 속성값을 조회한다.
    console.log(m.get(d));  // e
     
  • size : 속성 개수를 조회한다. 
    console.log(m.size);    // 3
     
  • 반복문, forEach 사용 가능하다. 
    for (const [k, v] of m) {console.log(k, v);}
    m.forEach((v, k) => {console.log(k, v);});
     
  • has(키) : 속성 존재 여부를 확인한다. 
    console.log(m.has(d));
     
  • delete(키), clear() : 속성을 삭제한다. 
    m.delete(d);
    m.clear();             // 전부 제거 
    console.log(m.size);   // 0
     

 Set : 배열과 유사한 자료구조로, 중복을 허용하지 않는다. 

  • 생성
    const s = new Set();
     
  • add() : Set에 요소를 추가한다.
    s.add(1);
    s.add(2);
    s.add('1');
    s.add(1);   // 중복이므로 무시된다.
     
  • size : 요소 개수를 조회한다.
    has() : 존재 여부를 확인한다.
    console.log(s.size);     // size : 3
    console.log(s.has(1));   // 존재 여부 : true
     
  • 반복문, forEach 사용 가능하다.
    for (const a of s) {console.log(a);}    // 1 2 '1'
    s.forEach((a) => {console.log(a);})     // 1 2 '1'
     
  • delete(키), clear() : 속성을 삭제한다.
    s.delete(2);    // delete()로 요소 2 제거
    s.clear();      // clear()로 모든 요소 제거
     
  • 배열에서 중복을 허용하고 싶지 않을 때, 또는 기존 배열에서 중복을 제거하고 싶을 때 사용한다.
    const arr = [1, 3, 2, 7, 2, 6, 3, 5];
    const s = new Set(arr);    // 배열의 중복 요소 제거
    const result = Array.from(s);   // set을 배열로 되돌리기
    console.log(result);    // 1, 3, 2, 5, 7
      

널 병합 / 옵셔널 체이닝 

   널 병합 (nullish coalescing) ?? 연산자  

  • || 연산자는 falsy 값(0, '', false, NaN, null, undefined)이면 뒤로 넘어가는 것과 달리,   
  • ?? 연산자는 falsy 값 중 null과 undefined를 따로 구분한다. 
    const a = 0;
    const b = a || 3; 
    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의 속성을 조회하는 경우 에러가 발생하는 것을 막는다. 
    const a = {};
    a.b;    // a가 객체이므로 문제 없다. 
    
    const c = null;
    try {
        c.d;
    } catch (e) {
        console.error(e);   // TypeError: Cannot read properties of null
    }
    
    c?.d;   // 문제 없음 
    
    try {
        c.f();
    } catch (e) {
        console.error(e);   //  TypeError: Cannot read properties of null
    }
    
    c?.f(); // 문제 없음. 객체 뿐만 아니라 함수도 
    
    try {
        c[0];
    } catch (e) {
        console.error(e);   // TypeError: Cannot read properties of nul
    }


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

AJAX (Asynchronous Javascript And XML)

  • AJAX는 비동기적 웹 서비스를 개발할 때 사용하는 기법으로, 
    페이지 이동 없이 서버에 요청을 보내고 응답을 받을 수 있게 한다. 
  • 보통 AJAX 요청은 jQuery나 axios 같은 라이브러리를 이용해서 보낸다.
  • ex) GET 요청 보내기 : axios.get 함수의 인수로 요청을 보낼 주소를 넣는다.
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    
    <script>
    	(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);
      		}
    	})();
    </script>
  • ex) POST 요청 보내기 : axios.post 함수의 두 번째 인수로 데이터를 넣어 보낸다. 
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
      (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>

FormData 

  • HTML form 태그의 데이터를 동적으로 제어할 수 있는 기능이다. 
  • FormData 생성자로 formData 객체를 만든다.
    const formData = new FormData();

  • FormData 메서드 
    append : 키-값 형식의 데이터를 저장한다. 
    has : 키에 해당하는 값이 있는지 알 수 있다. 
    formData.append('name', 'zerocho');
    formData.append('item', 'orange');
    formData.append('item', 'melon');
    
    formData.has('item'); // true
    formData.has('money'); // false;

    get :  키에 해당하는 값 하나를 가져온다.
    getAll : 키에 해당하는 모든 값을 가져온다.
    formData.get('item');	// orange
    formData.getAll('item'); // ['orange', 'melon'];
    
    formData.append('test', ['hi', 'zero']);
    formData.get('test'); // hi, zero

    delete : 현재 키를 제거한다. 
    set : 현재 키를 수정한다. 
    formData.delete('test');
    
    formData.get('test'); // null
    
    formData.set('item', 'apple');
    
    formData.getAll('item'); // ['apple'];
     
  •  ex) 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);
      }
    })();
    

encodeURIComponent, decodeURIComponent  

  •  window 객체의 메서드로 주소에 한글이 들어가는 경우 사용한다.
  • 한글 주소 부분만 encodeURIComponent 메서드로 감싸 안전한 문자열로 변환하고, 
    decodeURIComponent를 사용하여 다시 한글로 복구할 수 있다. 
    (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('%EB%85%B8%EB%93%9C'); // 노드

     

데이터 속성과 dataset  

  • 데이터 속성은 HTML과 관련된 데이터를 저장하는 공식적인 방법이다.
  • 아래 코드에서 data-로 시작하는 HTML 태그의 속성이 데이터 속성이다.
  • 데이터 속성을 사용하면 자바스크립트로 데이터에 쉽게 접근할 수 있다. 
    <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>
    	 // li 태그의 data-id와 data-user-job에 접근 
         console.log(document.querySelector('li').dataset);
         // { id: '1', userJob: 'programmer' }
    </script>

Quiz

  1. 아래 코드에서 첫 번째 this는 함수 (       )을, 두 번째 this는 함수 (       )을 가리킨다. 
    const relationship = {
        name : 'zero',
        friends : ['nero', 'hero', 'xero'],
        logFriends() {this.friends.forEach(friend => console.log(this.name, friend))}
    };
  2. (       )을 이용하면 프로미스 여러 개를 한 번에 실행할 수 있고,
    (       )를 이용하면 어떤 프로미스가 reject 되었는지 알 수 있다.

  3. async 함수 내에서 프로미스가 resolve될 때까지 기다리기 위해 사용하는 키워드는 (       ) 이다.

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

  5. 널 병합 연산자(??)를 사용한 코드 1의 실행 결과는 (       ), 코드 2의 실행 결과는 (       )이다.
    const c = 0;
    const d = c ?? 3; 
    console.log(d);		// 코드 1
    
    const e = null;
    const f = e ?? 3;
    console.log(f); 	// 코드 2
  6. (     ) 는 비동기적 웹 서비스를 개발할 때 사용하는 기법으로,
    페이지 이동 없이 서버에 요청을 보내고 응답을 받을 수 있게 한다.
     

Programming Quiz

  1. 구조 분해 할당 문법으로,
    chocoMachine 객체의 getChoco와 count를 같은 이름의 변수에 할당하는 코드를 작성하시오. 
    const chocoMachine = {
        status : {
            name : 'node',
            count : 5
        },
        getChoco() {
            this.status.count--;
            return this.status.count;
        }
    };


  2. arr 배열의 중복 요소를 제거하여 변수 s에 저장하고, 
    s를 다시 배열로 되돌려 변수 result에 저장하는 코드를 작성하시오.
    const arr = [1, 3, 2, 7, 2, 6, 3, 5];
    
    // 배열의 중복 요소 제거
    const s = ______   
    
    // set을 배열로 되돌리기
    const result = _______

 

정답 

 

1. relationship, relationship 

2. Promise.all, Promise.allSettled
3. await
4. null, undefined

5. 0, 3 
6. AJAX 
 

1번

const {getChoco, status : {count}} = chocoMachine;

 

2번 

const arr = [1, 3, 2, 7, 2, 6, 3, 5];

// 배열의 중복 요소 제거
const s = new Set(arr);    

// set을 배열로 되돌리기
const result = Array.from(s);

 


출처 : 조현영,  Node.js 교과서 개정 3판, 길벗(2022)
 

Corner Node.js 1
Editor : Snoopy

 
728x90

관련글 더보기