[TIL] react 강의

JISU·2021년 11월 23일
0

TIL

목록 보기
3/27

일기

오늘은 첫 레슨!

why react
모듈화에 용이하기 때문에
UI와 기능을 컴포넌트로 조각조각 나누기 용이하기 때문에!

컴포넌트들이 모여서 또 컴포넌트를 구성하기 때문에
네이버 화면에서 구역마다 팀을 나눠 개발이 가능할 것이다.

클래스 컴포넌트에서 라이프 사이클

컴포넌트 실행
초기화 (컨스트럭터)
렌더
마운트 업데이트 언마운트

아 나중에 기능 손 볼 때 스피너 추가해야겠다.

슬슬 ES6 문법으로다가 리팩토링 시작해야하나.


오늘 배운 것

react 관련

redux middleware

redux-thunk
동기적으로만 처리하는 redux에서 비동기 처리를 도와주는 middleware이다.
액션 객체가 아니라 함수를 디스패치 할 수 있다.
사실 무슨 말인지 정확히는 모르겠음...

redux 사용해서 backend와 통신할 때 무조건 사용하는 애 같음
왜냐하면 컴포넌트는 최대한 기능이 없고 뷰만 담당하면 되는 것인데 그 안에서
통신을 하면 좋지 않다.

따라서 redux에서 백엔드와 데이터 주고 받고, redux store에도 데이터를 업데이트하면서
뷰가 그걸 구독해서 화면에 잘 띄워줄 수 있도록 해야한다.

-> 다시 찾아보고 정리한 내용

미들웨어란!
미들웨어는 액션이 디스패치되어서 리듀서에서 이를 처리하기 전에 사전에 지정된 작업들을 설정한다. 액션과 리듀서의 중간자라고 이해하면 된다고 한다.

왜 미들웨어를 사용하나?

redux는 동기적으로 작업한다. 따라서 필요에 의해 비동기 작업을 사용할 때 redux-thunk를 이용한다.

redux-thunk는
객체 대신, 함수를 생헝하는 액션 생성함수를 작성할 수 있도록 도와준다.

redux에서는 기본적으로 액션 자체를 디스패치 한다.
actionCreator에 payload를 가지고 액션 객체 { type: LOAD, payload }를 반환하는 것

그런데 이런 액션 객체만 딱 뽑으면, 아무런 조건도 추가할 수 없고, 그냥 액션만 실행된다.
그러니까, 만약에 액션이 몇초 뒤에 실행하게 하거나, 상태에 따라 액션 수행을 하지 않거나 등등..

이러한 단점을 redux-thunk가 보완해준다.

const INCREMENT_COUNTER = 'INCREMENT_COUNTER'

function increment() {
	return {
    	type: INCREMENT_COUNTER
    }
    
function incrementAsync() {
	return (dispatch, getState) => {
    	setTimeout(() => {
        	dispatch(increment())
        }, 1000)
    }

이렇게 적어주면 어느 컴포넌트에서 incrementAsync를 사용해서 dispatch하면, increment dispatch를 1초 뒤에 실행, 즉 해당 액션을 1초 뒤에 실행할 수 있는 것이다.

또한, 상태 getState()를 가지고 현재 상태 값에 따라 조건을 걸어서 dispatch를 무시하는 기능 또한 넣을 수 있게 된다.

일반 생성자의 기능을 보완시켜주는 라이브러리라는 것을 알아야 할 것 같다.

[참고 및 출처] https://jw910911.tistory.com/48

redux-saga
thunk와 같은 비동기 처리 도와주는 라이브러리.

Javascript 개념

동기 / 비동기

  1. 동기 (syncronous)

    순차적으로 코드를 진행

  2. 비동기 (asyncronous)

    꼭 순차적으로만 코드를 진행하는 것은 아님. 시간이 오래걸리는 동작을 실행함과 동시에 그 동작이 끝나기 전에 다른 동작을 계속 진행해 나가는 것.

그 중에서도 Javascript는 싱글 스레드, 동기 언어이다.

호이스팅이 된 이후로 작성한 코드가 하나하나 순서대로 실행된다는 이야기이다.

그렇다면 문제는, 만약 시간이 오래 걸리는 함수를 만났을 때
그 함수가 끝날 때까지 대기하고 있어서 다음 코드가 진행이 되지 않고,
그래서 비효율적인 프로그램이 될 것이다.

특히나 시간이 오래 걸리는 동작은 network나 file을 읽어오는 등 무거운 작업인 경우가 많으므로 동기적으로만 코드를 진행시키다가는 사용자가 속 터질 수도 있다는 얘기.

그렇다면 싱글 스레드 동기 언어인 Javascript에서 비동기를 어떻게 구현해??

먼저 3가지 방법이 있다는 것을 알아가자
1. callback 함수
2. promise
3. async await -> 다음 포스트에

callback 함수

기본적으로 callback 함수란,
매개변수로 넘겨받은 함수, 그리고 그 함수가 다시 호출(call back)되어 실행되는 함수가 콜백 함수이다.

비동기를 가능하게 하는 함수 개념이 아니다.

하지만, 여기서는 비동기를 구현할 때 사용한 콜백함수에 대해 알아보기로 한다.

일단 코드를 봐보자.

console.log("1")
// 1000ms가 지나면 앞에 있는 인자 (callback 함수)를 callback해서 실행시켜줌
setTimeout(() => {
  console.log("2")
}, 1000) // 비동기
console.log("3")

setTimeout은 브라우저 api인데 따라서 저 자바스크립트 코드가 브라우저에 요청을 보내놓고,
응답을 기다리지 않고 그 다음 코드를 실행한다.
브라우저가 1초를 다 세면, 이제 1초 지났어
너가 보내준 callback 함수 다시 보내줄 게 이거 실행해라고 하는 것.

이렇게 비동기 실행을 가능하게 할 수가 있는 것이다.
그러니까, 나중에 (여기서는 1초가 지나서 / 혹은 파일을 다 받고 나서 등등) 그거 끝나면 내가 줬던 함수(callback 함수)를 실행해줘이다.

callback 함수는 특정 일이 끝나게 되면 실행하도록 두는 함수이다.

콜백 함수를 무조건 비동기적으로 사용하는 것은 아니다.

1) Syncronous callback
동기적인 콜백 함수도 있다.

function printImmediately(print) {
 // 함수를 인자로 받아서 안에서 바로 실행.
 // 함수 안에는 print this가 들어있다.
 print()
}

printImmediately(() => console.log("print this right now"))

printImmediately 함수는 print를 인자로 받아서 바로 실행(콜백)해준다.
따라서 동기적으로 바로 print 함수, 즉 print this right now 가 실행되게 된다.

2) Asyncronous callback

function printWithDelay(print, timeout) {
 setTimeout(print, timeout)
}

printWithDelay(() => console.log("print later"), 2000)

printWithDelay라는 setTimeout 함수를 감싸는 함수가 print(함수)와 number(시간)을 인자로 받앗 setTimeout을 실행해준다. 이 때는 당연히 print 콜백 함수가 2초 뒤에 실행이 된다.

뭐야.. 콜백 좋은거 아니냐고.
는 아님.ㅠ

콜백 지옥이라는 것이 있다. 어떤 문제가 있는지 살펴보자.

3) 콜백 지옥
콜백 함수안에 콜백 함수를 넣고 계속 네스팅하면 콜백 지옥이 완성된다.

class UserStorage {
 // 사용자에게 id와 password를 입력받아서
 // login 성공했다면 onSuccess 콜백 함수를 실행
 // login 실패했다면 onError 콜백 함수를 실행
 loginUser(id, password, onSuccess, onError) {
   setTimeout(() => {
     if (id === "ellie" && password === "dream") onSuccess(id)
     else onError(new Error("not found"))
   }, 2000)
 }

 // 마찬가지로 사용자에게 user를 입력받아서
 // login 성공했다면 onSuccess 콜백 함수를 실행
 // login 실패했다면 onError 콜백 함수를 실행
 getRoles(user, onSuccess, onError) {
   setTimeout(() => {
     if (user === "ellie") onSuccess({ name: "ellie", role: "admin" })
     else onError(new Error("no access"))
   }, 1000)
 }
}

UserStorage라는 클래스 안에 loginUser와 getRoles가 있다.
각각의 함수는 onSuccess 콜백 함수 / onError 콜백 함수를 인자로 받아서 그 함수를 조건에 따라 실행해준다.

const userStorage = new UserStorage()

const id = "ellie"
const password = "dream"

userStorage 만들고, id / password를 입력해준다.

이제 콜백 함수를 이용해서 login 요청을 해서 id(user)를 받아오고, 그 받아온 id를 이용해서 roles(admin)을 다시 요청해서 받아오는 프로그램을 구현한다.
괴랄스러운 백엔드이지만, 어쨌든 요청을 하고 그 받아온 데이터를 이용해서 또 요청을 보내야 하는 상황인 것이다.

userStorage.loginUser(
 id,
 password,
 (user) => { // callback 함수(onSuccess) 실행 - loginUser
   userStorage.getRoles(
     user,
     (userWithRole) => { // callback 함수(onSuccess) 실행 - getRoles
       console.log(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`)
     },
     (error) => { // callback 함수(onError) 실행 - getRoles
       console.log(error)
     }
   )
 },
 (error) => { // callback 함수(onError) 실행 - loginUser
   console.log(error)
 }
)

자 login user의 id와 password를 검사하기 위해 loginUser 함수를 실행하면서 콜백 함수로 onSuccess, onError 있다.
일단, loginUser가 성공적으로 값을 받아오면 "ellie"를 받아올 것.
그럼 그 안에, user("ellie")를 이용해서 getRoles함수를 실행 성공한다면 name과 role을 받아올 것이다.
모두 성공적으로 받아온다면 Hello ellie, you have a admin role 이라는 스트링을 보여줄 것이다.

그러나, 이 콜백 함수 안의 콜백 함수(콜백 체인)는 가독성이 떨어질 뿐만 아니라 디버깅하는 것 또한 어려워진다.

하지만 언제나 다른 방법이 또 있겠지.

그건 바로 promise 이다.

Promise

Promise란 자바스크립트 안에 내장되어 있는 객체이다.
비동기 동작을 위해 사용한다. (콜백 함수 대신)

  1. state 상태
    pending(promise가 만들어져서 지정한 동작이 수행 중일 때)
    -> fulfilled(동작이 성공적으로 끝났을 때) or rejected(문제가 생겼을 때)

  2. Producer vs Consumer
    Producer 데이터를 생성/제공하는 곳
    Consumer 데이터를 소비하는 곳

1) Producer
Producer - promise 만들기

const promise = new Promise((resolve, reject) => {
  console.log("doing something") // 무거운 일 (network, read files)
  setTimeout(() => {
    resolve("ellie") // 성공했을 때
    // reject(new Error("no network")) // 실패했을 때
  }, 2000)
})

이것을 실행해보면 executor 콜백 함수 (resolve, reject) 모두 바로 실행이 된다. 그러니까, 프로미스가 만들어지면, 저 실행자들은 바로 실행이 된다는 것.
이 점을 유의해서 코딩을 해야한다. (사용자가 요청을 한 적이 없는데도 요청이 될 수 있으므로)

2) Consumer
Promise가 생성이 되었으니 쓰는 쪽이 있을 것이다.
Consumer에서는 then, catch, finally 등을 이용해서 데이터를 받아온다.

Promise가 담긴 promise에다가 값을 받아올 것이다.

promise
  .then((value) => {
    console.log(value)
  }) // 그냥 catch 없이 (error 핸들링) 실행시키면 uncaught error가 발생한다.
  .catch((error) => console.log(error))
  .finally(() => {
    console.log("finally")
  }) // 성공 여부 상관없이 마지막에 실행 됨

promise에 담긴 값은 받아오는 데에 성공했다면 'ellie'라는 스트링이 될 것이다.
따라서 .then 안의 value는 ellie가 되겠다.

그 then이 반환하는 것은 또 Promise이기 때문에 .catch, .finally를 계속 사용할 수 있는 것이다. (promise chaining)

[참고 및 출처] https://www.youtube.com/watch?v=s1vpVCrT8f4
[참고 및 출처] https://www.youtube.com/watch?v=JB_yU6Oe2eE

profile
블로그 이전

0개의 댓글