Advanced Course 주특기 2강 - React
심화 주특기 2강!
- 토큰에 대해 알아본다.
- 파이어베이스를 사용해서 로그인 기능을 구현해본다.
- 로그인을 유지하는 방법에 대해 알아본다.
- 로그인 권한을 체크하는 컴포넌트를 만들어본다.
Promise
// 프라미스 객체를 만듭니다.
// 인자로는 (resolve, reject) => {} 이런 excutor 실행자(혹은 실행 함수라고 불러요.)를 받아요.
// 이 실행자는 비동기 작업이 끝나면 바로 두 가지 콜백 중 하나를 실행합니다.
// resolve: 작업이 성공한 경우 호출할 콜백
// reject: 작업이 실패한 경우 호출할 콜백
const promise = new Promise((resolve, reject) => {
if(...){
...
resolve("성공!");
}else{
...
reject("실패!");
}
});
- promise의 상태값
1. pending: 비동기 처리 수행 전(resolve, reject가 아직 호출되지 않음)
2. fulfilled: 수행 성공(resolve가 호출된 상태)
3. rejected: 수행 실패(reject가 호출된 상태)
4. settled: 성공 or 실패(resolve나 reject가 호출된 상태)
- promise 후속 처리 메서드
1. then(성공 시, 실패 시)
then의 첫 인자는 성공 시 실행, 두번째 인자는 실패 시 실행됩니다. (첫 번째 인자만 넘겨도 됩니다!)
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000);
});
// resolve
promise.then(result => {
console.log(result); // 완료!가 콘솔에 찍힐거예요.
}, error => {
console.log(error); // 실행되지 않습니다.
}).finally(() => {
console.log('finally'); // promise 마지막에 무조건 실행.
});
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("오류!")), 1000);
});
// reject
promise.then(result => {
console.log(result); // 실행되지 않습니다.
}, error => {
console.log(error); // Error: 오류!가 찍힐거예요.
});
then을 연달아서 이어주는 방법 (promise chaining)
new Promise((resolve, reject) => {
setTimeout(() => resolve("promise 1"), 1000);
}).then((result) => { // 후속 처리 메서드 하나를 쓰고,
console.log(result); // promise 1
return "promise 2";
}).then((result) => { // 위 then의 return 값이었던 promise 2가 result로 들어감
console.log(result);
return "promise 3";
}).then(...);
- .catch(실패 시)
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("오류!"), 1000);
});
promise.catch((error) => {console.log(error};);
async & await & generator
async
함수 앞에 async를 붙여서 사용한다. promise로 감싸서 반환한다.
async function fetchUser() {
return '성공'; // promise와 달리 별다른 선언 없이도 간편하게 비동기 구문을 쓸 수 있음.
}
const user = fetchUser();
user.then(console.log);
// then(result => console.log(result))
// 위와 같이 result를 생략할 수 있음.
console.log(user);
await
async 없이는 사용 못한다. async 함수 안에서만 동작한다.
await은 promise가 처리될 때가지 기다렸다가 그 이후에 결과를 반환한다.
// 첫번째 예제
async function myFunc(){
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000);
});
console.log(promise); // pending 상태
let result = await promise; // 여기서 기다리자!하고 신호를 줍니다.
console.log(promise);
console.log(result); // then(후처리 함수)를 쓰지 않았는데도, 1초 후에 완료!가 콘솔에 찍힐거예요.
}
// 두번째 예제
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(2000); // 2초간 여기서 기다림
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
async function pickFruits() {
// 아래처럼 get 함수를 먼저 호출하면 return 값은 미리 할당해둘 수 있음.
// 즉, 병렬적으로 처리를 할 수가 있는데, 이는 총 2초(apple)가 걸림.
// 이렇게 하지 않으면? -> 아래 주석 참고
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
// 아래처럼 get 함수를 먼저 호출해서 할당해두지 않으면, apple에 2초 걸린 후 banana에 1초 걸림. (await 때문에 기다려야하므로...)
// async function pickFruits() {
// const apple = await getApple();
// const banana = await getBanana();
// return `${apple} + ${banana}`;
// }
pickFruits().then(console.log);
병렬적으로 처리하는 비동기 구문은 위처럼(pickFruits() 함수처럼) 쓰지 않고 더 예쁘게 쓸 수 있다...!!!
Promise.all 메서드를 이용!function pickAllFruits() { return Promise.all([getApple(), getBanana()]).then(fruits => fruits.join(' + ') ); } pickAllFruits().then(console.log);
가장 빨리 전달된 값 하나만 뽑고싶다면??
Promise.race 메서드를 이용!function pickOnlyOne() { return Promise.race([getApple(), getBanana()]); } pickOnlyOne().then(console.log);
generator
이터러블(이터레이션을 돌릴수 있는)을 생성하는 함수이다. 배열도 이터러블 객체이고, 문자열도 이터러블 객체이다.
제너레이터 함수는 비동기 처리에 유용하게 사용된다.
function* counter() { // 제너레이터 함수의 특이한 정의 형태. *을 붙여줌.
console.log('첫번째 호출');
yield 1; // 첫번째 호출 시에 이 지점까지 실행된다.
console.log('두번째 호출');
yield 2; // 두번째 호출 시에 이 지점까지 실행된다.
console.log('세번째 호출'); // 세번째 호출 시에 이 지점까지 실행된다.
return 3;
}
const generatorObj = counter();
console.log(generatorObj.next()); // 첫번째 호출 {value: 1, done: false}
console.log(generatorObj.next()); // 두번째 호출 {value: 2, done: false}
console.log(generatorObj.next()); // 세번째 호출 {value: 3, done: true}
for(let value of generatorObj) {
alert(value); // 1, 2가 출력됨. return 값은 무시함.
}
제너레이터 함수는 생성을 해두고 호출하면 객체를 반환하는 특이한 함수이다.
그 객체를 이용해 사용하고 싶을때 사용한다면 비동기 처리하듯 이용할 수 있을것이다.
yarn 설치 패키지들...
redux
yarn add redux react-redux redux-thunk redux-logger [email protected] [email protected]
immer
yarn add immer redux-actions
firebase
yarn add firebase
기록해두면 재활용하기 좋은 파일들...
image-community 앱 참고!
configureStore.js
// configureStore.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";
export const history = createBrowserHistory();
const rootReducer = combineReducers({
user: User,
router: connectRouter(history),
});
const middlewares = [thunk.withExtraArgument({ history: history })];
// 지금이 어느 환경인 지 알려줘요. (개발환경, 프로덕션(배포)환경 ...)
const env = process.env.NODE_ENV;
// 개발환경에서는 로거라는 걸 하나만 더 써볼게요.
if (env === "development") {
const { logger } = require("redux-logger"); // 배포환경에서는 import 안되도록 개발환경에서만 require로 불러옴.
middlewares.push(logger);
}
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;
const enhancer = composeEnhancers(applyMiddleware(...middlewares));
let store = (initialStore) => createStore(rootReducer, enhancer);
export default store();
firebase.js
//firebase.js
import firebase from "firebase/app";
import "firebase/auth";
const firebaseConfig = {
apiKey: "AIzaSyB7jyeMM1j2DcPl02FK8_mIq-Hn8MXL9Oo",
authDomain: "sparta-react-f05ca.firebaseapp.com",
projectId: "sparta-react-f05ca",
storageBucket: "sparta-react-f05ca.appspot.com",
messagingSenderId: "387824718666",
appId: "1:387824718666:web:1588254195b4dce37d27b3",
measurementId: "G-YJF8QHWTWF",
};
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const apiKey = firebaseConfig.apiKey;
export { auth, apiKey };
index.js
//index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./shared/App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "./redux/configureStore";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
쿠키 저장하기
// Cookie.js
const getCookie = (name) => {
let value = "; " + document.cookie;
let parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(";").shift();
}
};
const setCookie = (name, value, exp = 5) => {
let date = new Date();
date.setTime(date.getTime() + exp * 24 * 60 * 60 * 1000);
document.cookie = `${name}=${value}; expires=${date.toUTCString}`;
};
const deleteCookie = (name) => {
let date = new Date("2020-01-01").toUTCString();
document.cookie = name + "=; expires=" + date;
};
export { getCookie, setCookie, deleteCookie };
모듈 만들기
// user.js
import { createAction, handleActions } from "redux-actions";
import { produce } from "immer";
import { setCookie, getCookie, deleteCookie } from "../../shared/Cookie";
import { auth } from "../../shared/firebase";
import firebase from "firebase/app";
// initial state
const user_initial = {
user_name: "bbakyong",
};
const initialState = {
user: user_initial,
is_login: false,
};
// actions
const LOG_OUT = "LOG_OUT";
const GET_USER = "GET_USER";
const SET_USER = "SET_USER";
// action creators
const logOut = createAction(LOG_OUT, (user) => ({ user }));
const getUser = createAction(GET_USER, (user) => ({ user }));
const setUser = createAction(SET_USER, (user) => ({ user }));
// middleware actions
const loginFB = (id, pwd) => {
return (dispatch, getState, { history }) => {
auth.setPersistence(firebase.auth.Auth.Persistence.SESSION).then(() => {
auth
.signInWithEmailAndPassword(id, pwd)
.then((user) => {
dispatch(
setUser({
id: id,
user_name: user.user.displayName,
user_profile: "",
uid: user.user.user_uid,
})
);
history.push("/");
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage);
});
});
};
};
const signupFB = (id, pwd, user_name) => {
return function (dispatch, getState, { history }) {
auth
.createUserWithEmailAndPassword(id, pwd)
.then((user) => {
console.log(user);
auth.currentUser
.updateProfile({
displayName: user_name,
})
.then(() => {
dispatch(
setUser({
id: id,
user_name: user_name,
user_profile: "",
uid: user.user.user_uid,
})
);
history.push("/");
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage);
// ..
});
};
};
const loginCheckFB = () => {
// 세션에 로그인 정보 값이 있을때 사용하는 함수.
// 세션에 값이 있다면 이 함수로 리덕스에 값을 넣어준다.
// 세션에 값이 없어졌다면 리덕스에서도 logOut을 이용해 값을 없애준다.
return (dispatch, getState, { history }) => {
auth.onAuthStateChanged((user) => {
if (user) {
dispatch(
setUser({
user_name: user.displayName,
user_profile: "",
id: user.email,
uid: user.uid,
})
);
} else {
dispatch(logOut());
}
});
};
};
const logoutFB = () => {
return (dispatch, getState, { history }) => {
auth.signOut().then(() => {
dispatch(logOut());
history.replace("/"); // replace: 지금 페이지와 괄호 안의 페이지를 바꿔치기 한다. 뒤로가기 해도 전 페이지가 나오지 않는다.
});
};
};
// reducer using handle actions, immer
export default handleActions(
{
[SET_USER]: (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
);
const actionCreators = {
logOut,
getUser,
loginFB,
signupFB,
loginCheckFB,
logoutFB,
};
export { actionCreators };
사용한 컴포넌트들...
-
페이지 단위
login, postdetail, postlist, postwrite, signup -
중간 단위 (페이지 내부에서 영역을 나눔)
header, post, commentlist, commentwrite -
최소 단위 (거의 태그 단위)
button, grid, image, index, input, text
Author And Source
이 문제에 관하여(Advanced Course 주특기 2강 - React), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@uvula6921/Advanced-Course-주특기-2강-React저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)