리액트 심화반 2주차 - 1
22.04.17(일) ~ 18(월)
스파르타코딩클럽 리액트 심화반 - 2주차 과정 - 1
◎ Promise
- 콜백 헬(멸망의 피라미드)
- 꼬리에 꼬리를 무는 비동기 처리가 늘어나면 호출이 계속 중첩되고, 코드가 깊어지고, 관리는 어려워짐
- 이런 콜백 헬이 발생하는 이유?
- 비동기 처리 시에는 실행 완료를 기다리지 않고 바로 다음 작업을 실행
- 비동기 처리 함수 내에서 처리 결과를 반환하는 걸로는 원하는 동작을 하지 않으니, 콜백 함수를 사용해 원하는 동작을 하게 하려고 콜백 함수 사용
- 이 콜백 함수 내에서 또 다른 비동기 작업이 필요할 경우 중첩이 생기면서 콜백 헬이 생김
function async1('a', function (err, async2){
if(err){
errHandler(err);
}else{
...
async2('b', function (err, async3){
...
}){
...
}
}
});
-
Promise
- 비동기 연산이 종료된 이후 결과를 알기 위해 사용하는 객체
- 비동기 메서드를 마치 동기 메서드처럼 값을 반환할 수 있음 ( 비동기 처리 시점을 좀 더 명확하게 표현할 수 있음)
-
Promise 객체 만들기
const promise = new Promise((resolve, reject) => { if(...){ ... resolve("성공!"); }else{ ... reject("실패!"); } });
- 인자로는 (resolve, reject) => {} 이런 excutor 실행자(실행 함수)를 받는다.
- 비동기 작업이 끝나고, 콜백 함수를 실행
-
Promise의 상태값
- pending: 비동기 처리 수행 전(resolve, reject가 아직 호출되지 않음)
- fulfilled: 수행 성공(resolve가 호출된 상태)
- rejected: 수행 실패(reject가 호출된 상태)
- settled: 성공 or 실패(resolve나 reject가 호출된 상태)
-
Promise의 후속처리 Method
- Promise로 구현된 비동기 함수는 Promise 객체를 반환
- {} 안에서는 비동기 처리 결과(성공 결과나 에러메시지)를 받아서 처리해야 한다.
- .then : 첫 인자는 성공시 실행, 두번째 인자는 실패시 실행,
let promise_success = new Promise((resolve, reject) => { setTimeout(() => resolve("완료!"), 1000); let promise_fail = new Promise((resolve, reject) => { setTimeout(() => reject(new Error("오류!")), 1000); }); promise_success.then(result => { console.log(result); // 완료! }, error => { console.log(error); // 실행X }); promise_fail.then(result => { console.log(result); // 실행X }, error => { console.log(error); // Error: 오류! });
-
Promise Chaining
- Promise는 후속 처리 Method를 Chaining해서 여러개의 Promise를 연결 할 수 있다.
new Promise((resolve, reject) => { setTimeout(() => resolve("promise 1"), 1000); }).then((result) => { // 후속 처리 메서드 하나를 쓰고, console.log(result); // promise 1 return "promise 2"; }).then((result) => { // 이렇게 연달아 then을 써서 이어주는 거예요. console.log(result); return "promise 3";
-
async
- 함수 앞에 async를 붙여서 사용, 항상 Promise를 반환
async function myFunc() { return "프라미스를 반환해요!"; // 프라미스가 아닌 걸 반환시 } myFunc().then(result => {console.log(result)}); // 프라미스를 반환해요! // Promise {<fulfilled>: undefined}
-
await
- async 함수 안에서만 동작, Promise가 다 처리될 때까지 기다렸다가 결과 반환
async function myFunc(){ let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("완료!"), 1000); }); console.log(promise); // Promise {<pending>} let result = await promise; console.log(promise); // 1초 후, Promise {<fulfilled>: '완료!'} console.log(result); // 1초 후, 완료! }
◎ 토큰 기반 인증
-
옛날 방법
- 사용자의 로그인 상태를 서버에서 전부 가지고 있었음
- 사용자가 많아지면 서버에 부하가 걸림 / 서버를 여러개 놓으면 로그인 상태 관리가 까다로움
- 따라서 토큰 기반 인증 방법을 많이 사용한다.
-
OAuth2.0: 외부서비스의 인증 및 권한부여를 관리하는 프레임워크
- 동작방식
1.클라이언트
와서버
사이에 인증(로그인)을 하면 서버가access_token
을 줍니다.
2.클라이언트
는access_token
을 이용해서 API 요청을 할 수 있어요.
3.서버
는 API 요청을 받고,access_token
을 가지고 권한이 있나 없나 확인해서 결과를클라이언트
에 보내줍니다. - 외부 서비스 엮어서 외부 서비스에서 접근 권한, 유저 정보들을 가져오게 할 수도 있다.
- 유저가
구글
로그인을 합니다.- 자원 소유자가 자원서버에 권한 요청을 한거죠!
구글
은 로그인할 때 유저가 입력한 정보(아이디, 비밀번호 등)을 보고클라이언트(우리 웹사이트!)
에 접근 권한을 줍니다.클라이언트
는 이 권한을 가지고Authorization server(권한 서버)
에access_token
을 요청합니다.클라이언트
는 이access_token
을 가지고 구글에서유저 정보
를 가져올 수 있어요.구글
은클라이언트
가 보낸access_token
을 가지고 권한이 있나 없나 확인해서결과
를클라이언트
에 보내줍니다.
- 유저가
- 동작방식
-
JWT: 데이터가 JSON 형태로 이루어져 있는 토큰
- 생김새 : [header].[payload(내용)].[signature(서명)]
- header: 토큰 타입과 암호화 방식 정보가 들어갑니다.
- payload: 토큰에 담을 정보가 name: value 쌍으로 들어갑니다.
- signature: 서명 정보입니다. secret key를 포함해서 header와 payload 정보가 암호화 되어 들어갑니다.
- 동작 방식 : 토큰 기반 동작 방식대로 움직여요!
- 유저가 로그인을 시도하면, 서버가 요청을 확인하고 secret key를 가지고 access_token을 발급합니다.
- 클라이언트에 JWT를 전달하고
- 클라이언트는 API 요청을 할 때 Authorization header에 JWT를 담아서 보냅니다.
- 서버는 JWT의 서명을 확인하고 payload에서 정보를 확인해서 API 응답을 보냅니다.
- 생김새 : [header].[payload(내용)].[signature(서명)]
◎ 웹 저장소
- 크롬 개발자도구 → Application 탭 → 좌측 Storage 에서 확인가능
- 쿠키(Cookie)
- 클라이언트 로컬에 저장되는 key:value 형태의 저장소 (약 4KB)
- 쿠키 만들기
document.cookie = "MY_COOKIE=here;";
- 쿠키 가져오기
console.log(document.cookie); // 하나의 String 형태로 가져옴 // 개별 쿠키 나누기 document.cookie.split(";")
- 쿠키 삭제 : 만료일을 이전으로 설정
dateString = Date('2020-12-12').toUTCString()" document.cookie = "MY_COOKIE=here; expires=${dateString}";
- 세션 스토리지(Session Stroage)
- key:value 형태의 저장소, 세션 스토리지에 저장된 데이터는 브라우저를 닫으면 제거
sessionStorage.setItem("MY_SESSION", "here"); // 설정 sessionStorage.getItem("MY_SESSION"); // 값 가져오기 sessionStorage.removeItem("MY_SESSION"); // 해당 값 삭제 sessionStorage.clear(); // 세션 스토리지 전부 지우기
- 로컬 스토리지(Local Stroage)
- key:value 형태의 저장소, 로컬 스토리지는 따로 지워주지 않으면 계속 브라우저에 남아 있음 (중요한 정보를 넣어두면 매우 위험)
localStorage.setItem("MY_LOCAL", "here"); // 설정 localStorage.getItem("MY_LOCAL"); // 값 가져오기 localStorage.removeItem("MY_LOCAL"); // 해당 값 삭제 localStorage.clear(); // 로컬 스토리지 전부 지우기
◎ 리덕스(redux) 이용하기
-
리덕스: 클라이언트에서 전역 데이터 관리를 용이하게 해줌
-
리덕스(redux) 설치
# 리덕스와 리덕스 모듈 내에서 경로 이동까지 하게 해줄 히스토리, 라우터와 히스토리를 엮어줄 모듈 포함
yarn add redux react-redux redux-thunk redux-logger [email protected] [email protected]
# redux-actions : 액션 만들기 등을 자동화
# immer : 불변성 유지를 편하게 해줌
yarn add immer redux-actions
- 모듈 만들기
// src > redux > modules > user.js
import { createAction, handleActions } from "redux-actions";
import { produce } from "immer";
import { setCookie, getCookie, deleteCookie } from "../../shared/Cookie";
// Action Type
const LOG_IN = "LOG_IN";
const LOG_OUT = "LOG_OUT";
const GET_USER = "GET_USER";
// action creators / Using redux-actions
const logIn = createAction(LOG_IN, (user) => ({ user }));
const logOut = createAction(LOG_OUT, (user) => ({ user }));
const getUser = createAction(GET_USER, (user) => ({ user }));
// initialState
const initialState = {
user: null,
is_login: false,
};
// Reducer / Using redux-actions immer
export default handleActions(
{
[LOG_IN]: (state, action) =>
produce(state, (draft) => {
setCookie("is_login", "success");
draft.user = action.payload.user;
draft.is_login = true;
}),
[LOG_OUT]: (state, action) =>
produce(state, (draft) => {
deleteCookie("is_login");
draft.user = null;
draft.is_login = false;
}),
[GET_USER]: (state, action) => produce(state, (draft) => {}),
},
initialState
);
- Redux Store 만들기 (추후 기능 추가)
- import 하기
- rootReducer 준비
- middleware 준비 (redux-logger, redux devTools)
- middleware 묶기
- rootReducer + middleware로 스토어 만들기
+history 전역에서 사용할 수 있게 주입
// src > redux > configureSrore.js
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import { createBrowserHistory } from "history";
import { connectRouter } from "connected-react-router";
import User from "./modules/user";
// History 만들기
export const history = createBrowserHistory();
// Reducer 묶기 + 만든 history를 Store와 묶기
const rootReducer = combineReducers({
user: User,
router: connectRouter(history),
});
// thunk 비동기 작업 처리
// withExtraArgument , thunk 에 다른 인수를 넘겨줌
const middlewares = [thunk.withExtraArgument({history:history})];
// 지금이 어느 환경인 지 알려줘요. (개발환경, 프로덕션(배포)환경 ...)
const env = process.env.NODE_ENV;
// 개발환경에서는 로거라는 걸 하나만 더 써볼게요.
// console에 스토어 데이터에 뭐가 담기는지, 액션으로 인해 변한게 찍힘
if (env === "development") {
const { logger } = require("redux-logger");
middlewares.push(logger);
}
// redux devTools 설정
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
})
: compose;
// Middleware 묶기
const enhancer = composeEnhancers(
applyMiddleware(...middlewares)
);
// 스토어 만들기
let store = (initialStore) => createStore(rootReducer, enhancer);
export default store();
- Store 주입하기
// index.js
import store from "./redux/configureStore";
import { Provider } from "react-redux";
ReactDOM.render(
<Provider store={store}> // Provider 세팅, store 주입
<App />
</Provider>,
document.getElementById("root")
);
// App.js
import { ConnectedRouter } from "connected-react-router";
import { history } from "../redux/configureStore";
...
function App() {
return (
<React.Fragment>
<Grid>
<Header></Header>
<ConnectedRouter history={history}> // ConnectedRouter로 변경, history 주입
<Route path="/" exact component={PostList} />
<Route path="/login" exact component={Login} />
<Route path="/signup" exact component={Signup}/>
</ConnectedRouter>
</Grid>
</React.Fragment>
);
}
- 액션 실행하기, state 값 가져오기
...
import {actionCreators as userActions} from "../redux/modules/user";
import { useDispatch, useSelector } from "react-redux";
const Login = (props) => {
const dispatch = useDispatch();
const login = () => {
dispatch(userActions.login({user_name: "perl"})); // Action 실행
}
const is_login = useSelector((state) => state.user.is_login) // 값 가져오기
...
}
Author And Source
이 문제에 관하여(리액트 심화반 2주차 - 1), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gwichanlee/리액트-심화반-2주차-1저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)