일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- Spring-Framework
- intllij 내 Bean을 찾지 못해서 발생하는 오류
- intellij
- 계산맞추기 게임
- DB Browser
- googleColaboratory
- 노드에 리액트 추가하기
- node.js 설치
- node.js로 로그인하기
- react
- spring-boot
- react오류
- 거북이 대포 게임
- 웹 게임을 만들며 배우는 리액트
- JS 개념
- props
- 모두의 파이썬
- Do it 자바스크립트 + 제이쿼리 입문
- 모던자바스크립트
- 인프런
- 타자 게임 만들기
- Concurrently
- Python
- Colaboratory 글자 깨짐
- vs code 내 node
- ReactDOM.render is no longer supported in React 18. Use createRoot instead
- 리액트
- You are importing createRoot from "react-dom" which is not supported. You should instead import it from "react-dom/client"
- 따라하며 배우는 노드 리액트 기본 강의
- 자바스크립트
- Today
- Total
프로그래밍 삽질 중
Next.js와 리덕스 설명(로그인, 로그아웃) 본문
* 전통적인 웹사이트
- 브라우저가 요청 ↔ 프론트 서버 ↔ 백엔드 ↔ 데이터베이스로 동작
- 장점 : 모든 페이지가 화면에 그려짐
- 단점 : 모든 페이지가 화면에 그려지므로 로딩 속도가 느림
* 리액트, 뷰, 앵귤러 사용하는 이유
- 사용자에게 빠르게 인터렉션(단방향 서비스가 아니라 서로 교류가 잇는 양방향 통신이나 서비스)을 보여줄 때
- 로딩창만 일단 먼저 보여줌(시간 단축)
- 서버사이드 렌더링 진행
(검색 엔진을 위함, 첫 방문일 때만 전통적인 웹사이트처럼 움직이다가 페이지 전환 시 방문한 페이지 코드만 보여줌)
* Next.js
- 새로고침 시 로딩창이 없음(서버사이드 렌더링 적용)
- 페이지 이동 시 점점 빨라짐(캐싱)
- (개인적인 의견) javascript에서만 만든 프로젝트와 달리 router가 필요없다는 느낌 받음
→ pages 항목 만드니 router가 없어도 주소따라 페이지 이동 가능
* 리덕스
- 컴포넌트가 분리되어 있는 경우(state) 데이터가 따로따로 되어 있음
- 사용하려면 부모 컴포넌트 -> 자식 컴포넌트로 전달해야 함
- 중앙에서 관리할 수 없나? = contextAPI, Redux
- 아래의 예시는 id, password 데이터가 AppLayout, LoginForm, UserProfile에 사용됨
[리덕스 사용 전]
* 기존 AppLayout.js
const AppLayout = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
<Menu items={items} mode="horizontal" />
{children}
<Row gutter={8}>
<Col xs={24} md={6}>
{isLoggedIn ? ( //로그인이 true면 UserProfile false면 LoginForm
<UserProfile setIsLoggedIn={setIsLoggedIn} />
) : (
<LoginForm setIsLoggedIn={setIsLoggedIn} />
)}
</Col>
</Row>
</div>
);
};
* 기존 LoginForm.js
▶ props인 setIsLoggedIn을 받아옴
▶ setIsLoggedIn(true) 시 LoginForm에서 UserProfile로 넘어감
const LoginForm = ({ setIsLoggedIn }) => {
const [id, onChangeId] = useInput("");
const [password, onChangePassword] = useInput("");
const onSubmitForm = useCallback(() => {
console.log(id, password);
setIsLoggedIn(true);
}, [id, password]);
* 기존 UserProfile.js
▶ props인 setIsLoggedIn을 받아옴
import React, { useCallback } from "react";
const UserProfile = ({ setIsLoggedIn }) => {
const onLogOut = useCallback(() => {
setIsLoggedIn(false); //로그아웃 시 false
}, []);
return (
<>
</>
);
};
[리덕스 사용 후]
* 리덕스 적용 시 생긴 문제 해결 ([Next.js] Next.js에서 Prop `className` did not match 경고가 뜨는 이유)
npm i babel-plugin-styled-components
출처 : https://tesseractjh.tistory.com/164
▶ 그 외 추가 설치할 것들
npm i react-redux
npm i redux-devtools-extension
중앙에서 데이터 관리하는 js 파일
* 리덕스 기본 세팅
export const initialState = {
};
const reducer = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
export default reducer;
* 리덕스 세팅 - 1
- /store/configureStore.js
import { applyMiddleware, createStore, compose } from "redux";
import { createWrapper } from "next-redux-wrapper";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "../reducers";
const configureStore = (context) => {
console.log(context);
const middlewares = [];
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer);
return store;
};
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === "development",
});
export default wrapper;
* reducer 파일
- index.js
import { HYDRATE } from "next-redux-wrapper";
const initialState = {
user: {
isLoggedIn: false,
user: null,
signUpData: {},
loginData: {},
},
};
export const loginAction = (data) => {
return {
type: "LOG_IN",
data,
};
};
export const logoutAction = () => {
return {
type: "LOG_OUT",
};
};
//switch문으로 한 번에 관리
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case HYDRATE:
console.log("HYDRATE", action);
return { ...state, ...action.payload };
case "LOG_IN":
return {
...state,
user: {
...state.user,
isLoggedIn: true,
user: action.data,
},
};
case "LOG_OUT":
return {
...state,
user: {
...state.user,
isLoggedIn: false,
user: null,
},
};
default:
return state;
}
};
export default rootReducer;
* 변경된 AppLayout.js
▶ useState 대신 useSelector 사용됨
import { useSelector } from "react-redux";
const AppLayout = ({ children }) => {
const { isLoggedIn } = useSelector((state) => state.user);
console.log("isLoggedIn", isLoggedIn);
return (
<div>
<Menu items={items} mode="horizontal" />
{children}
<Row gutter={8}>
<Col xs={24} md={6}>
{isLoggedIn ? <UserProfile /> : <LoginForm />}
</Col>
</Row>
</div>
);
};
* 변경된 LoginForm.js
▶ useDispatch 사용됨
▶ reducers 폴더에서 loginAction 가져옴
import { useDispatch } from "react-redux";
import { loginAction } from "../reducers";
const LoginForm = () => {
const dispatch = useDispatch();
const [id, onChangeId] = useInput("");
const [password, onChangePassword] = useInput("");
const onSubmitForm = useCallback(() => {
console.log(id, password);
dispatch(
loginAction({
id,
password,
})
);
}, [id, password]);
return (
<FormWrapper onFinish={onSubmitForm} style={{ padding: "10px" }}>
<Button type="primary" htmlType="submit" loading={false}>
로그인
</Button>
</FormWrapper>
);
};
* 변경된 UserProfile.js
import { useDispatch } from "react-redux";
import { logoutAction } from "../reducers";
const UserProfile = () => {
const dispatch = useDispatch();
const onLogout = useCallback(() => {
dispatch(logoutAction());
}, []);
return (
<>
<Button onClick={onLogout}>로그아웃</Button>
</>
);
};
* 리덕스 세팅 - 2
▶ 만약 reducers/index.js에서 switch가 늘어날 경우
▶ 분리할 필요성이 있음
▶ 각각 user, post로 분리 후 combineReducer로 합침
reducers/index.js
import { HYDRATE } from "next-redux-wrapper";
import { combineReducers } from "redux";
import user from "./user";
import post from "./post";
const rootReducer = combineReducers({
//hydrate를 위해 indexReducer 추가(서버사이드 렌더링 위함)
index: (state = {}, action) => {
switch (action.type) {
case HYDRATE:
console.log("HYDRATE", action);
return { ...state, ...action.payload };
default:
return state;
}
},
user,
post,
});
export default rootReducer;
reducers/post, user.js
//post
export const initialState = {
mainPosts: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
export default reducer;
//user
export const initialState = {
isLoggedIn: false,
user: null,
signUpData: {},
loginData: {},
};
export const loginAction = (data) => {
return {
type: "LOG_IN",
data,
};
};
export const logoutAction = () => {
return {
type: "LOG_OUT",
};
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "LOG_IN":
return {
...state,
isLoggedIn: true,
user: action.data,
};
case "LOG_OUT":
return {
...state,
isLoggedIn: false,
user: null,
};
default:
return state;
}
};
export default reducer;
* 이후 components에서 action을 가져오는 경로 수정
- LoginForm, UserProfile.js
//기존
import { loginAction } from "../reducers;
//수정
import { loginAction } from "../reducers/user";
'과거 프로그래밍 자료들 > React' 카테고리의 다른 글
(next.js + redux toolkit 시리즈 2) next-redux-wrapper (yotube 강의) (0) | 2022.10.12 |
---|---|
(next.js + redux toolkit 시리즈 1) redux 설명(yotube 강의) (0) | 2022.10.12 |
인라인 스타일링 시 리렌더링 문제(styled-components를 사용하는 이유) (1) | 2022.10.04 |
[웹 게임을 만들며 배우는 React] - 지뢰찾기(1), contextAPI (0) | 2022.09.30 |
[웹 게임을 만들며 배우는 React] - 틱택토, useReducer, useCallback (0) | 2022.09.29 |