책 읽다가 코딩하다 죽을래

리액트 심화반 - 2주차 개발일지(비동기, callback, Promise, OAuth, 웹 저장소) 본문

GD프로젝트/개발일지

리액트 심화반 - 2주차 개발일지(비동기, callback, Promise, OAuth, 웹 저장소)

ABlue 2021. 7. 26. 15:23

 

 

GD프로젝트 리액트 심화반

2주 차가 시작되었다.

이번에도 새롭게 배운 내용과 결과물들을 설명해보겠다.

 

 

 

목차

1. 새롭게 배운 내용(비동기, callback, Promise, OAuth, 웹 저장소)

2. 2주 차 과제

3. 배운 내용 프로젝트에 적용

 

 


1. 새롭게 배운 내용


 

 

 

1 - 1 비동기

 

 

 

더보기

Promise를 언급하기 전에

동기와 비동기부터 설명하겠다.

 

 

자바스크립트는 동기적인 언어이다.

동기적인 것이 무엇이냐 하면은

 

 

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

 

말 그대로 동시에 일어나는 뜻인데

프로그래밍적인 개념으로 말하자면 요청과 그에 대한 결과가 동시에 일어나는 것이다.

 

 

function add(a,b){
	return a + b;
}

let a = 3;
let b = 6;
let c = add(a,b);		// add함수의 호출(요청)과 결괏값을 반환(결과)전까지 다음코드로 진행되지 않는다.
console.log(c);

 

이러한 특성때문에 대부분의 프로그래밍 언어는 절차지향처럼 코드가 위에서 아래로 작동한다.

아래에 있는 코드는 위에 있는 코드보다 먼저 실행될 수 없다.

 

 

여기서 비동기라는 것은 무엇이냐하면 동기가 아닌 것들이다.

 

 

 

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

 

비동기는 요청과 그에 대한 결과가 동시에 일어나지 않습니다.

예로 들면

 

console.log('1');       //실행 순서 1
//비동기는 언제 실행되지 모르는 것들이다 예로 들면 setTimeout()
setTimeout(() => console.log('2'),1000);       //실행 순서 4        //이 함수가 실행될려면 1초가 필요한데 그 시간을 기다리지 않고 바로 다음 코드를 실행한다. 이것이 비동기이다.
console.log('3');       //실행 순서 2

//동기 콜백
function printImmediately(print){
    print();
}
printImmediately(() => console.log('hello'));       //실행 순서 3

//비동기 콜백
function printWitdhDelay(print, timeout){
    setTimeout(print, timeout);
}
printWitdhDelay(() => console.log('async callback.'),2000);       //실행 순서 5

 

이렇게 시간이 지연되는 setTimeout()함수를 들 수 있다.

setTimeout() 함수는 두 번째 인자만큼 시간이 지나면.

첫 번째 인자의 콜백함수가 실행되는 것이다.

 

비동기에 특성에 의해

setTimeout()에 지연되는 시간만큼 프로그램이 멈춰있는 것이 아니라

 setTimeout()이 실행되기 전에 기다리지 말고 바로 다음 코드를 실행된다.

 

 

이러한 특성이 생긴 이유는

우리가 웹이나 앱이든 무엇을 구현하든

시간이 걸리는 작업이 존재한다.

서버에서 데이터 받아오기, 네트워크 접속 대기 등

언제 끝날지 모르는 작업들이 있다면

끝날때까지 기다리지 말고 다른 것부터 해야

기다리기 싫은 사용자들 입장에서 불편하지 않게 된다.

 

 

 

 

1 - 2 callback

 

 

 

더보기

콜백함수는 쉽게 말하자면 일을 다른 객체에게 시키고, 그 일이 끝날 때까지 기다리는 것이 아니라 그 객체가 나를 다시 부를 때까지 다른 할 일을 하고 있는 것이고,

 

정확히 말하자면 제어권을 객체에게 넘겨주는 것이다. 사용자가 계속 실행하기 귀찮으니 실행제어권을 객체에게 넘겨주는 것이다.

 

이 콜백함수는 비동기 처리를 하기 위한 패턴 중 하나이다.

 

 

setInterval(() => {
    console.log('1초마다 실행될 겁니다.');
}, 1000);

 

콜백함수의 예시는 setInterval이 있다.

setTimeout이 지정한 시간이 지나면 지정한 함수가 1회 실행된다면

setInterval은 지정한 시간이 지날 때마다 함수가 1회 실행된다.

 

그렇다고 setInterval만 무한루프를 도는 것이 아니라 앞에서 말했듯이 콜백함수는 비동기 처리 패턴 중 하나이다.

setInterval 아래에 코드가 있었다면 아래의 코드부터 실행된다.

 

위의 코드블럭은 1초마다 콘솔에 '1초마다 실행될 겁니다.' 가 찍힌다.

이러한 함수가 콜백함수이다.

1초마다 사용자가 함수를 

실행하기는 너무 번거로우니 setInterval 객체에게 지정한 함수를 제어할 수 있는 권한을 준 것이다.

 

 

콜백함수의 큰 특징은 다음 4가지가 있다.

 

 

  1. 다른 함수 A(setInterval)의 매개변수로 콜백함수 B(console.log)를 전달하면 A가 B의 제어권을 갖게 된다.
  2. 특별한 요청(bind, apply)이 없는 한 A에 미리 정해진 방식에 따라 B를 호출한다.
  3. 미리 정해진 방식이란, this에 무엇을 바인딩할지, 매개변수에는 어떤 값들을 지정할지, 어떤 타이밍에 콜백을 호출할지 등이다.
  4. 콜백은 함수이다.(메소드가 될 수 없다.)

 

 

 

 

 

 

1 - 3 Promise

 

 

 

더보기

Promise는 콜백함수 대신에 비동기적인 처리를 할 수 있는 것이다.

 

Promise의 중요한 요소는 State와 Producer, Consumer가 있다.

state는 자신의 일이 다 처리되었는지 아닌지의 현재 상태를 나타내 주는 것이고

Producer는 데이터를 제공하는 사람이며

Consumer는 데이터를 소비하는 사람을 일컫는데

개념만 보면 뭔 소리하는지 못 알아듣겠으니 코드를 보자

 

 //1. Producer

 const promise = new Promise((resolve, reject) => {
     //여러가지 로직(네트워크 통신 및 파일 처리 등등)
    console.log('doing something...');
    setTimeout(() => {          
        resolve('ablue');               //일이 정상적으로 끝마치게 되면 resolve라는 콜백함수를 호출하면 된다.
        reject(new Error('no network'));  //일이 비정상적으로 끝마치게 되면 reject라는 콜백함수를 호출하면 된다. 이 코드 내에선 무조건 resolve로 가게 되니 이 코드는 실행되지 않는다.
    }, 2000);
 });

//2. Consumers: then, catch, finally로 받아올 수 있음
 
promise.then((value) => {      //promise가 정상적으로 수행하게되면 호출된다. 여기서 value는 promise에서 정상적으로 받아온 값 'ablue'이다.
   console.log(value);
})
promise.then((value) => console.log(value))
 .catch(error => {              //promise가 비정상적으로 수행하게 되면 then은 넘어가고 catch가 호출된다. 여기서 error는 promise에서 받아온 값(Error('no network'))이다.
     console.log(error);
 })
 .finally(() => {               //정상적으로 수행하든 비정상적으로 수행하든 호출되는 것.
     console.log('finally');
 });

 

 

Producer의 코드를 보면 작업을 하면 어떤 경우에 성공 케이스라고 정의하고

어떤 경우엔 실패 케이스라고 정의한다.

 

Consumers의 코드는 성공했을 때 어떤 코드를 실행하며

실패했을 때 어떤 코드를 실행하는지를 결정한다.

 

즉 Producer는 Promise가 호출됐을 때 어떤 작업을 하는지를 설계하는 부분이고

Consumers는 그 작업의 결과에 따른 어떤 행동을 대처하는지 설계하는 부분이다.

 

Producer가 resolve와 reject를 결정하며

그 결과에 따라 consumer(then, try, catch)의 행동이 달라지는 것이다.

 

Promise의 state 값은 4가지가 있다.

 

 

  1. pending : 비동기 처리 수행 전 (resolve, reject가 아직 호출되지 않음)
  2. fulfilled : 수행 성공 (resolve가 호출된 상태)
  3. rejected : 수행 실패 (reject가 호출된 상태)
  4. settled : 성공 or 실패 (resolve나 reject가 호출된 상태, 수행이 이루어지고 결과가 나온 상태) 

 

Promise는 앞에서 말했듯이 비동기적인 처리를 하는 것이며

producer와 consumer가 동시에 일어나지 않는다

보통 producer에 네트워크 접속, 서버에서 데이터 송수신 로직 등 

시간이 많이 걸리는 작업을 만들어서 사용하는 경우일 것이다.

 

 

 

1 - 4 OAuth 2.0

 

 

 

더보기

외부 서비스의 인증 및 권한 부여를 관리하는 프레임워크이다.

Open Authentication, Open Authorization라고 한다.(인증과 허가를 포함한다.)

 

 

 

 

요즘 웬만한 웹사이트에선 자체 회원가입이 아닌

구글이나 sns 계정으로 연동해서 회원가입을 하는 추세이다.

왜냐하면 사용자는 계정을 여러 개 관리하기 어렵기 때문에

거대한 회사(구글, 페이스북, 네이버)의 계정으로 여러 사이트의 이용 권한을 얻는 것이다.

 

유저가 구글로 로그인하는 상황이라고 가정하겠다

여기서 구글(자원 서버 + 권한 서버)은 유저(자원 소유자)의 정보(자원)를 갖고 있으며, 로그인 검증도 해준다.

 

 

유저가 로그인을 시도하면

내부에서 진행되는 절차는 다음과 같다.

 

 


1.server(사용자가 접속한 웹사이트)는 google에게 구글 로그인 페이지 요청
2. google이 server에게 구글 로그인 페이지 응답
3. server가 user에게 로그인 페이지 응답
4. user가 정보 입력 후 google에게 정보전달
5. google에게 user에게 결과 전달(바로 액세스 토큰을 주는 게 아니라 권한 증서를 준다.)
6. user가 server에게 권한 증서를 주고 server는 google에게 액세스 토큰 요청
7. google이 엑세스 토큰 응답
8. 엑세스 토큰을 받은 server는 user에게 전달
9. user는 토큰을 잘 보관할 때마다 sever에 작업을 할때마다 확인
토큰은 만료가 되는데 유저가 토큰이 만료가 되면 refresh 토큰으로 server에게 주고 server는  google에게 새 액세스 토큰을 요청을 한다. 이 refresh 토큰까지 만료가 됐더라면 다시 user는 로그인을 해야 한다.

 

 

 이러한 원리로 우리는 구글, 페이스북, 깃허브, 네이버 등등의 계정을 이용해 다른 웹사이트에서도 회원가입을 할 수 있다.

 

 

 

 

1 - 5 웹 저장소(쿠키, 세션, 로컬 저장소)

 

 

 

더보기

 웹에서 사용되는 데이터는 모두 서버에서만 관리되는 것이 아니다.

당신의 브라우저 내에서도 관리가 되는데

대표적인 종류가 쿠키, 세션, 로컬 저장소다.

 

 

 

ctrl + shift + j 를 눌러 개발자 도구를 켠 다음 Application 탭을 가면

 

 

 

 

저장소 종류가 나타날 것이다.

여기서 각 저장소별로 어떤 데이터들이 담겨져있는지 확인할 수 있다.

 

- cookie - 

 

쿠키의 크기는 4KB이며 데이터가 key : value 형태로 저장되어있다.

브라우저가 종료하면 제거된다.

그래서 비회원 장바구니 기능을 구현할 때 쿠키가 사용된다.

 

// 쿠키 추가하기
// key는 MY_COOKIE, value는 here
document.cookie = "MY_COOKIE=here;";

// 쿠키 가져오기
console.log(document.cookie);

// 쿠키 삭제하기
// 쿠키를 삭제할 때는 만료일을 이전으로 돌려서 지우는 방법을 사용한다.
document.cookie = "MY_COOKIE=here;expires=new Date('1970-01-01').toUTCString()";

 

 

- Session Storage -

세션 스토리지는 서버에 저장되어 있는 것이 표시된 것이며

브라우저가 꺼지면 제거가 된다.

 

 

// 세션 추가하기
// key는 MY_SESSION, value는 here인 세션을 만든다
sessionStorage.setItem("MY_SESSION", "here");

// 세션 가져오기
// key값으로 쉽게 가져올 수 있다 
sessionStorage.getItem("MY_SESSION");

// 세션 삭제하기
// 하나만 삭제하고 싶다면, 이렇게 키를 통해 삭제합니다.
sessionStorage.removeItem("MY_SESSION");
// 몽땅 지우고 싶을 땐 clear()를 쓰면 됩니다.
sessionStorage.clear();

 

 

- Local Storage -

로컬 저장소의 크기는 5MB이며 데이터가 key : value로 저장이 된다.

브라우저가 종료해도 제거되지 않고 남는다.

유저의 아이디, 비밀번호 같은 중요한 정보를 넣어두면 위험하다.

 

 

// 로컬 스토리지 추가하기
// key는 MY_LOCAL, value는 here인 데이터를 저장한다.
localStorage.setItem("MY_LOCAL", "here");

// 로컬 스토리지 가져오기
// key값으로 쉽게 가져올 수 있다.
localStorage.getItem("MY_LOCAL");

// 로컬 스토리지 삭제하기
// 하나만 삭제하고 싶다면, 이렇게 키를 통해 삭제한다.
localStorage.removeItem("MY_LOCAL");
// 몽땅 지우고 싶을 땐 clear()를 쓰면 된다.
localStorage.clear();

 

 

앞서 말한 OAuth의 사용되는 토큰은 로컬 저장소의 저장한다.

그 이유는 쿠키보다 더 많은 정보를 저장할 수 있고,

로컬 저장소는 쿠키처럼 모든 http 통신에 포함되지 않기 때문이다.

쿠키는 http 통신 데이터에 자동으로 포함이 된다.

 

그렇다고 쿠키보단 로컬 저장소를 선호하는 것이 맞진 않다.

앞에서 말한 대로 브라우저가 종료해도 데이터가 남아있으니 보안에 취약하다.

 

 

 


2. 2주 차 과제


더보기
2주 차 결과물

 

소스 코드 보기[클릭]

 

2주 차는 firebase의 Authentication 기능을 활용하여 로그인, 로그아웃, 회원가입을 구현하였다.

사용된 기능 : cookie, Firebase Authentication, redux, redux-thunk, immer, redux-logger, redux-actions

 

DB를 이용해 인증을 구현하는 것은 어렵다고 생각했는데 firebase docs에 한국어로 자세하게 설명되어있어서 그리 어렵지는 않았다!! 요즘은 정말 코딩하기에 편한 세상 같다.

 

 

firebase에 저장된 내가 입력한 유저정보들

 

 

 


3. 배운 내용 프로젝트에 적용


 

 

 

더보기

메인 페이지에서 심리테스트 상세페이지로 넘어갈 때 심리테스트의 정보를

props로 넘겨주었다.

 

문제점 : 상세페이지에서 새로고침을 하면 props의 심리테스트 정보가 초기화되어 오류가 일어난다.

 

해결방법 : 심리테스트의 정보를 세션에 저장하여 상세페이지에서 새로고침을 해도 더 이상 오류가 일어나지 않는다.

 

 

옆부분을 잘보시면 MBTI를 클릭할때마다 값이 변경되는 것을 볼 수 있다.

 

 쿠키, 로컬스토리지, 세션 중 세션을 선택한 이유는? 브라우저가 종료되어 제거되어도 상관없는 데이터이기 때문이다.