[Day 34] Auth Token + Promise + Async Function + To-do List(Axios)

JWT

토큰

  • 인증 토큰(Authorization Token) 저장하기 위해 쿠키를 쓰는 경우와 인증 서버를 쓰는 경우가 있다.
    • 쿠키를 쓰는 경우
    • 직접 서버를 관리하는 경우
      • local storage 에 저장
  • 인증 토큰의 유효기간
    • 토큰을 만들어내는 주체는 서버다.
    • 토큰을 만들어내는 사람이 직접 유효기간을 설정할 수 있다.
    • 매번 인증이 발생할 때마다 토큰이 사용되는데, 유효기간이 지났을 경우 서버에서 기억하고 있는 정보와 다르기 때문에 인증이 안된다.

비동기 프로그래밍

Promise

  • Promise는 ‘언젠가 끝나는 작업’의 결과값을 담는 객체.

  • Promise 객체가 만들어지는 시점에는 그 통안에 무엇이 들어갈지 모를 수도 있다.

  • then 메소드를 통해 콜백을 등록해서 작업이 끝났을 때 결과값을 가지고 추가 작업을 할 수 있다.

  • Promise 객체는 Promise 생성자를 통해 만들 수 있다.

    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('2초가 지났습니다.');
        resolve('hello');
      }, 2000);
    });
    
    • Promise 생성자의 인수 두 가지는 모두 콜백이다.
    • 첫 번째 인수인 resolve 함수를 콜백 안에서 호출하면 resolve에 인수로 준 값이 Promise 객체의 결과값이 된다.
  • Promise 객체의 결과값을 이용해 추가 작업을 하려면 then 메소드를 호출한다. then 메소드에 콜백을 넘겨서 첫 번째 인수로 들어온 결과값을 가지고 추가 작업을 한다.

    p.then(msg => {
      console.log(msg); // hello
    });
    
  • then 메소드 자체도 Promise 객체를 반환한다. 이 때 콜백에서 반환한 값이 곧 Promise의 결과값이 된다. (then 메소드 체인 가능) (비동기 작업을 쉽게 연이어서 할 수 있다.)

    p.then(msg => {
      return msg + ' world'; // Promise의 결과값이 된다.
    }).then(msg => {
      console.log(msg);
    });
    
  • then 메소드에서 넘겨준 콜백에서 Promise 객체를 반환하면 then 메소드가 반환한 Promise 객체는 앞의 Promise 객체의 결과를 따르게 된다.

    // Promise 객체를 반환하는 함수
    function delay(ms) {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log(`${ms} 밀리초가 지났습니다.`);
          resolve();
        }, ms);
      });
    }
      
    delay(1000)
      .then(() => delay(2000))
      .then(() => Promise.resolve('끝'))
      .then(console.log);
      
    console.log('시작');
    
    function delay(ms, value) {
      return new Promise(resolve => {
        setTimeout(() => {
          // console.log(`${ms} 밀리초가 지났습니다.`);
          resolve(value);
        }, ms);
      });
    }
      
    delay(1000, 'hello')
      .then((str) => {
        return delay(2000, str + ' world')
      })
      .then((str) => {
        console.log(str)
      })
      
    
  • HTTP 통신을 할 때에도 Promise가 사용된다.

    • axios 라이브러리를 사용해서 GET 요청을 보낼 떄 axios.get() 메소드를 사용할 수 있는데, 이 때 Promise 객체가 반환된다.

      const axios = require('axios');
      const API_URL = 'https://api.github.com';
          
      axios.get(`${API_URL}/repos/facebookincubator/create-react-app/issues?per_page=10`)
        .then(res => {
          console.log('최근 10개의 이슈:');
          res.data
            .map(issue => issue.title)
            .forEach(title => console.log(title));
          console.log('출력이 끝났습니다.');
        });
      
      • axios.get() 메소드를 호출해서 반환된 Promise 객체에 담긴 결과값은 Response 객체로 HTTP 응답에 대한 내용을 담고 있다.
  • Promise 특징

    • then 메소드는 Promise 객체를 반환하므로, 콜백을 중첩하지 않고도 비동기 작업을 연이어 할 수 있다.
    • 비동기 작업이라는 동작 자체를 값으로 다룰 수 있게 된다. (조합성이 좋아진다.)
  • Promise의 정적 메소드

    • Promise.all()

      • 인수로 들어온 iterable에 들어 있는 모든 Promise 객체가 완료되었을 때, 그 자신도 완료되는 새 Promise 객체를 반환한다.
      .then(res => {
        const ps = res.data.map(user => axios.get(`${API_URL}/users/${user.login}/starred?per_page=10`));
        return Promise.all(ps);
      })
      

비동기 함수 (Async Function)

  • ES2017에서 도입된 비동기 함수를 사용하면 동기식 코드와 거의 같은 구조를 갖는 비동기식 코드를 짤 수 있다.

  • 함수 앞에 async 키워드를 붙이면 비동기 함수가 된다.

    // 비동기 함수
    async function func1() {
      // ...
    }
      
    // 비동기 화살표 함수
    const func2 = async () => {
      // ...
    }
      
    // 비동기 메소드
    class MyClass {
      async myMethod() {
        // ...
      }
    }
    
  • 특징

    • 항상 Promise 객체를 반환한다.

    • Promise의 결과값은 비동기 함수 내에서 무엇을 반환하느냐에 따라 결정되고, then 메소드와 같은 방식으로 동작한다.

      async function func1() {
        return 1;
      }
          
      async function func2() {
        return Promise.resolve(2);
      }
          
      func1().then(console.log); // 1
      func2().then(console.log); // 2
      
    • 비동기 함수 내에서 await 키워드를 쓸 수 있다.

    • await 키워드 뒤에 오는 Promise가 결과값을 가질 때까지 비동기 함수의 실행을 일시정지시킨다.

      • 일시정지는 비동기적으로 이루어지며, 브라우저는 Promise가 완료될 때까지 다른 작업을 처리할 수 있다.
    • await는 연산자이기도 하며, await 연산의 결과값은 뒤에 오는 Promise 객체의 결과값이다.

      function delay(ms, value) {
        return new Promise(resolve => {
          setTimeout(() => {
            // console.log(`${ms} 밀리초가 지났습니다.`);
            resolve(value);
          }, ms);
        });
      }
          
      async function main() {
        const str1 = await delay(1000, 'hello');
        console.log(str1)
        const str2 = await delay(2000, 'bye');
        console.log(str2)
        const result = await Promise.resolve('끝');
        console.log(result);
      }
          
      main();
      
    • await 키워드는 for, if 같은 제어구문 안에서도 쓰일 수 있기 때문에 then 메소드를 사용할 때보다 복잡한 비동기 데이터 흐름을 아주 쉽게 표현할 수 있다.

  • 비동기 함수가 나오기 전에는 Generator를 이용해서 비동기 함수처럼 사용했었다.

    • Generator를 비동기식으로 작동시킬 수 있는 co 라이브러리를 사용하면 비동기 함수와 거의 똑같이 사용할 수 있다.
    • Generator는 함수의 재개를 프로그래머가 직접 제어할 수 있다는 장점이 있기 때문에 일부러 비동기 함수 대신 Generator를 사용하는 경우도 있다.
      • React 프로그래밍에서 비동기 작업을 할 때 사용되는 redux-saga 라이브러리도 Generator를 사용하고 있다.
  • async 키워드는 바깥 함수에서 썼더라도 내부 함수에서 다시 써줘야 각각의 내부함수에도 적용이 된다!!

실습

Axios를 활용한 할 일 목록 만들기

프론트엔드 개발에 있어서 중요한 개념들

  • 상태로부터 화면을 그릴 때
    • 상태 저장소(서버)를 여러개 둘 경우 상태 불일치가 발생한다.
    • 믿을 수 있는 상태 저장소는 딱 하나만 있는게 좋다.
    • 이러한 개념을 Single Source of Truth (진리의 유일한 원천)
  • 화면을 다시 그릴 때
    • 매번 Single Source로부터 상태를 가져와서 화면을 다시 그려주는게 좋다.
    • 비효율적으로 보이지만 비효율적이지 않다.
  • Real-Time Web (실시간 웹)
    • 구현하기 위해 사용하는 대표적인 기술은 Web Socket
    • 웹 소켓을 안쓰면 요청을 보내야만 응답을 받을 수 있다.
    • 웹 소켓을 쓰면 서버와 연결이 계속 유지되어 있어서 요청을 보내지 않아도 응답을 받을 수 있다.

댓글남기기