1. Node.js란?

    웹 서버의 개념이 아닌 Javascript로 서버를 구축하고 서버에서 Javascript가 작동되도록 해주는 런타임 환경(플랫폼)을 말한다.

    여기서 런타임(Runtime)이란, 프로그래밍 언어가 구동(실행)되는 환경을 말한다.

    Untitled

  2. npm

    NPM은 (Node Package Manger)의 약자로 명령어로 자바스크립트 라이브러리를 설치하고 설치된 라이브러리의 버전을 관리해주는 패키지 매니저다. 개발자는 단 몇줄의 명령어로 기존의 공개된 모듈들을 설치하고 활용할 수 있다.

    {
      "name": "mustard_seed",
      "version": "0.1.0",
      "private": true,
      "dependencies": {
        "@testing-library/jest-dom": "^5.16.5",
        "@testing-library/react": "^13.4.0",
        "@testing-library/user-event": "^13.5.0",
        "@types/jest": "^27.5.2",
        "@types/node": "^16.18.1",
        "@types/react": "^18.0.23",
        "@types/react-dom": "^18.0.7",
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "react-icons": "^4.6.0",
        "react-router-dom": "^6.4.2",
        "react-scripts": "5.0.1",
        "typescript": "^4.8.4",
        "web-vitals": "^2.1.4"
      },
      "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
      },
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      },
      "devDependencies": {
        "@typescript-eslint/eslint-plugin": "^5.41.0",
        "@typescript-eslint/parser": "^5.41.0",
        "eslint": "^8.22.0",
        "eslint-config-prettier": "^8.5.0",
        "eslint-plugin-prettier": "^4.2.1",
        "eslint-plugin-react": "^7.31.10",
        "eslint-plugin-react-hooks": "^4.6.0",
        "prettier": "^2.7.1"
      }
    }
    
  3. var, let, const 정의와 차이점

    Untitled

    변수의 선언은 varconstlet 키워드로 할 수 있으며, ES6에서 const와 let이 추가되었다.

    자바스크립트에서 변수 선언은 선언 → 초기화 단계를 거쳐 수행된다.

    1. var 키워드

      var 키워드를 이용한 변수 선언은 선언 단계와 초기화 단계가 동시에 진행되어, kmj에 암묵적으로 undefined를 할당해 초기화한다.

      var kmj
      console.log(kmj) // output: undefined
      

      그런데 반대로, console을 먼저 찍어도 반환 값이 undefined로 나온다.

      console.log(kmj) // output: undefined
      var kmj
      

      이는 변수 선언이 런타임에서 되는 것이 아니라, 그 이전 단계에서 먼저 실행되기 때문이다. 자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행하기에 앞서, 변수 선언을 포함한 모든 선언문(ex. 변수 선언문, 함수 선언문 등)을 찾아내 먼저 실행한다. 즉, 변수 선언이 어디에 있든 상관없이 다른 코드보다 먼저 실행되는 특징을 호이스팅(hoisting)이라 한다.

      • 변수 할당

        변수에 값을 할당 할 때에는 할당 연산자(=)를 사용한다.

        var kmj // 변수 선언
        kmj = 'howdy-mj' // 값의 할당
        
        var kmj = 'howdy-mj' // 변수 선언과 할당
        

        변수 선언과 할당은 하나의 문(statement)으로 단축 표현할 수 있지만, 두 개의 실행 시점이 다르다. 변수 선언이 호이스팅되어 런타임 이전에 실행되지만, 값의 할당은 소스코드가 순차적으로 실행되는 런타임에 실행된다.

        따라서 변수의 할당과 console을 실행하는 위치에 따라 반환되는 값이 다르다.

        console.log(kmj) // output: undefined
        
        var kmj = 'howdy-mj'
        console.log(kmj) // output: howdy-mj
        

        kmj라는 변수에 새로운 값을 재할당할 수도 있다.

        console.log(kmj) // output: howdy-mj
        
        kmj = 'mj'
        console.log(kmj) // output: mj
        

        재할당은 변수에 저장된 값을 다른 값으로 변경하는 것으로, 만약 변경할 수 없는 값이라면 이는 변수가 아니라 상수(constant)라 부른다.

    2. var, let, const의 차이

      Untitled

      앞에서 발견한 var 키워드의 문제점은 크게 세 가지가 존재한다.

      • 변수 중복 선언 가능하여, 예기치 못한 값을 반환할 수 있다.
      • 함수 레벨 스코프로 인해 함수 외부에서 선언한 변수는 모두 전역 변수로 된다.
      • 변수 선언문 이전에 변수를 참조하면 언제나 undefined를 반환한다.

      ES6에서 나온 let과 const 키워드는 위의 세 가지 문제점을 해결했다.

      1. 변수 중복 선언 불가
      • let

        let 키워드로는 변수 중복 선언이 불가하지만, 재할당은 가능하다.

        let name = 'kmj'
        console.log(name) // output: kmj
        
        let name = 'howdy' // output: Uncaught SyntaxError: Identifier 'name' has already been declared
        
        name = 'howdy'
        console.log(name) // output: howdy
        
      • const

        const가 let과 다른 점이 있다면, 반드시 선언과 초기화를 동시에 진행되어야 한다.

        const name; // output: Uncaught SyntaxError: Missing initializer in const declaration
        const name = 'kmj'
        

        const도 let과 마찬가지로 재선언이 불가하며, 더 나아가 재할당도 불가하다. 재할당의 경우, 원시 값은 불가능하지만, 객체는 가능하다. const 키워드는 재할당을 금지할 뿐, '불변'을 의미하지 않는다.

        // 원시값의 재할당
        const name = 'kmj'
        name = 'howdy' // output: Uncaught TypeError: Assignment to constant variable.
        
        // 객체의 재할당
        const name = {
          eng: 'kmj',
        }
        name.eng = 'howdy'
        
        console.log(name) // output: { eng: "howdy" }
        
      1. 블록 레벨 스코프

      Untitled

      let, const 키워드로 선언한 변수는 모두 코드 블록(ex. 함수, if, for, while, try/catch 문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.

      위 var 키워드로 예를 들었던 것을 그대로 가져와 바꾸면 아래와 같은 결과가 나온다.

      let a = 1
      
      if (true) {
        let a = 5
      }
      
      console.log(a) // output: 1
      

      var 키워드로 선언한 경우 5가 나왔지만, let 키워드로 선언한 경우 if 문 안에 있는 것은 지역 스코프를 가져 전역에서 console을 찍었을 경우, 전역에 있는 a가 결과 값으로 출력된다. (const 키워드도 let 키워드와 동일하게 동작한다)

  4. ‘==’와 ‘===’ 연산자의 차이는 무엇인지 설명해주실 수 있을까요?

    Untitled

  5. 비동기 통신

    Untitled

    1. 동기와 비동기의 차이점

      1. 동기(Synchronous: 동시에 일어나는)

        • 동기는 말 그대로 동시에 일어난다는 뜻이다. 요청과 그 결과가 동시에 일어난다는 약속이다.

          바로 요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 한다.

          순서에 맞춰 진행되는 장점이 있지만,  여러 가지 요청을 동시에 처리할 수 없다.

          위 그림의 (a)처럼 커피 주문을 받고 나올 때까지 기다리는 것이 동기 방식의 예시라고 할 수 있다.

      2. 비동기(Asynchronous: 동시에 일어나지 않는)

        • 비동기는 동시에 일어나지 않는다를 의미한다. 요청과 결과가 동시에 일어나지 않을 거라는 약속이다.

          하나의 요청에 따른 응답을 즉시 처리하지 않아도, 그 대기 시간동안 또 다른 요청에 대해 처리 가능한 방식이다.

          여러 개의 요청을 동시에 처리할 수 있는 장점이 있지만 동기 방식보다 속도가 떨어질 수도 있다.

          위 그림의 (b)처럼 점원 한명이 커피 주문을 받고 다른 점원이 커피를 건네주는 것이 비동기 방식의 예시다.

    2. Promise

      1. 과거, Promise가 없었던 시절에는 콜백 지옥에서 살아왔다.

        1. 콜백 지옥
        function increaseAndPrint(n, callback) {
          setTimeout(() => {
            const increased = n + 1; //클로저
            console.log(increased);
            if (callback) {
              callback(increased); //자기 자신 콜백함수
            }
          }, 1000);
        }
        
        increaseAndPrint(0, n => {
          increaseAndPrint(n, n => {
            increaseAndPrint(n, n => {
              increaseAndPrint(n, n => {
                increaseAndPrint(n, n => {
                  console.log('끝!');
                });
              });
            });
          });
        });
        
      2. Promise 를 사용하면, 비동기 작업의 개수가 많아져도 들여쓰기 코드의 깊이가 깊어지지 않게 된다. 코드를 보는데 있어서 편하게 읽을 수 있다.

      3. Promise는 비동기 작업이 완료된 이후에 다음 작업을 연결시켜 진행할 수 있다. 작업 결과 따라 성공 또는 실패를 리턴하며 결과 값을 전달 받을 수 있다. Promise 객체를 통해 미리 함수를 등록해놓고, 결과에 따라서 resolve reject가 호출받으면, 함수를 실행하는 식이다. 마치 스위치를 내려 전기를 흐르게 하거나 올려서 안흐르게 하거나 같은 원리이다.

      4. 기본 문법

        const myPromise = new Promise( (resolve, reject) => { //콜백함수
          // 구현..
          // resolve(값) or reject(값)
        })
        
        myPromise
           .then(n => {}) //성공하면 콜백함수 실행. 인자 n은 resolve의 값. 리턴값은 Promise객체 (체이닝 가능)
           .catch(n => {}); //실패하면 콜백함수 실행. 인자 n은 reject의 값. 리턴값은 Promise객체 (체이닝 가능)
        
        new Promise((resolve, reject) => { //pending
        
        	//... 비동기 처리
        	resolve();  // fulfilled
            reject(); // rejected
        });
        

        생성자를 통해 프로미스 객체를 만드는 순간 pending(대기) 상태라고 한다.

        new Promise( /*executor*/ (resolve, reject) => {...} );
        

        resolve함수를 실행하면, fulfilled(이행) 상태가 된다.

        reject함수를 실행하면, rejected(거부) 상태가 된다.

        const myPromise = new Promise( (resolve, reject) => {
          
          setTimeout(function(){
        
            let status = Math.floor(Math.random()*2);
            
            console.log(status);
            if (status){
              resolve("성공"); // status is fulfilled and then 실행
            }
            else{
              reject("실패"); // status is rejected and catch 실행
            }
          },1000)
        });
        
        myPromise
          .then(n => console.log(`${n} 하였습니다.`))
          .catch(n => console.log(`${n} 하였습니다.`));
        
        function increaseAndPrint(n) {
          return new Promise((resolve, reject)=>{
            setTimeout(() => {
              const increased = n + 1;
              console.log(increased);
              resolve(increased);
            }, 1000)
          })
        }
        
        increaseAndPrint(0)
          .then((n) => increaseAndPrint(n))
          .then((n) => increaseAndPrint(n))
          .then((n) => increaseAndPrint(n))
          .then((n) => increaseAndPrint(n)); // 체이닝 기법
        
      5. 상태 및 흐름

        Untitled

        • pending(대기) : 처리가 완료되지 않은 상태
        • fulfilled(이행) : 성공적으로 처리가 완료된 상태
        • rejected(거부) : 처리가 실패로 끝난 상태
      6. 자세한 내용은 해당 블로그 참조

        1. https://ko.javascript.info/promise-basics
    3. async/await

      1. callback과 promise의 문제점을 보완한 비동기 코드 작성을 위한 방법이다.
      2. async 와 await 는 절차적 언어에서 작성하는 코드와 같이 사용법도 간단하고 이해하기도 쉽다.
        • 문법1. - function 키워드 앞에 async만 붙여주면 되고
        • 문법2. - 비동기로 처리되는 부분 앞에 await만 붙여주면 된다.
        • 문법3. - async가 붙은 함수는 프라미스를 반환하고, 프라미스가 아닌 것은 프라미스로 감싸 반환한다.
        • 문법4. - await 키워드를 만나면 프라미스가 처리(settled)될 때까지 기다린다.
        • 문법5. - 프라미스가 처리가 완료되어 resolve(값) 되면 값만 따로 추출해서 리턴한다.
      3. await는 promise.then보다 좀 더 세련되게 프라미스의 result 값을 얻을 수 있도록 해주는 문법이다. promise.then보다 가독성 좋고 쓰기도 쉽다.
      4. https://ko.javascript.info/async-await
      async function p2(){ // async을 지정해주면 Promise를 리턴하는 함수로 만들어준다.
        return 'hello2'; //리턴값은 Promise{<resolved>: "hello2"} 자동 resolve해준다는걸 알 수있다.
        // reject는 throw 에러를 날리면 된다.
      }
      
      p2().then((n) => console.log(n));
      
  6. Arrow Function

    1. 화살표 함수 표현(arrow function expression)은 전통적인 함수표현(function)식보다 단순하고 간결한 문법으로 함수를 만들 수 있는 방법이다. 하지만, 화살표 함수는 몇 가지 제한점이 있고 모든 상황에 사용할 수는 없다.
    2. 규칙
      • this나 super에 대한 바인딩이 없고,  클래스의 method 로 사용될 수 없다.
      • 함수 구현부에서 new 키워드가 없다.
      • 일반적으로 스코프를 지정할 때 사용하는 callapplybind methods를 이용할 수없다.
      • 생성자로 사용할 수 없다.
      • yield를 화살표 함수 내부에서 사용할 수 없다.