! 오늘 할 것 !
- 로그인 로그아웃 : 로그인하면 상품명 페이지 뜨도록
- 저번에 장바구니에서 삭제 잘 안된거 수정
- 라우팅 설명
- 상세페이지 설명
1. 링크를 거는 방법
첫번째 방법) link 컴포넌트 사용 : 화면에서는 a 태그로 변환 → 그냥 a 태그처럼 사용하는 상황일 때
두번째 방법) 함수로 호출해주는 방법 : useNavigate사용 → 함수를 호출해줘야 하는 상황일 때
- routes 하는 건 app.js , index.js 둘 다 상관없음
1) main.js 에서 회사소개 페이지 누르면 그 페이지로 넘어가도록
- import { Link } from 'react-router-dom';
- <Link to='/company'>회사소개 페이지</Link>
2) company.js 에서 메인페이지로 이동 누르면 그 페이지로 넘어가도록
- import { useNavigate } from 'react-router-dom';
- <span onClick={goToMain}>메인페이지로 이동</span>
const navigate = useNavigate();
const goToMain = () => {
navigate('/')
}
2. ? : 쿼리 값
- uses서치팜 : query 값 가져올 수 있고 셋쿼리
- query 값 : url 뒤에 추가적인 정보를 전달하고 싶을 때 사용
- usestate 와 비슷하게 사용
1) app.js 에서 product.js 라우트
2) product.js
- import { useSearchParams } from 'react-router-dom'
- let [query,setQuery]=useSearchParams();
- console.log('query 값', query.get('page'))
- 여기서 get 은 어떤 걸 가져오는 메서드 page 에 해당하는 키값을 가져오겠다는 뜻
- http://localhost:3000/product?page=1&num=10 → 페이지는 1, num은 10 인데 이걸로 주소 입력했을 때 콘솔에 query 값 1 이라고 뜸
3) main.js
- import useNavigate
- const navigate = useNavigate();
const goToProduct = () =>{
navigate('/product?page=1&num=10&name=dd') // 보통은 변수로 들어감
/* navigate(`/product?page=${id}&num=${num}&name=${name}`) -> 이런 식으로 들어감 */
}
- <button onClick={goToProduct}>상품페이지 가기</button>
3. 상세페이지 만들기
1) ProductDetail.js 파일 생성
- app.js 에 ProductDetail route
- useNavigate 를 import 해주기
- const navigate = useNavigate();
- <button onClick={goToProduct}>상품페이지 가기</button>
const goToProduct = () =>{
navigate('/product?page=1&num=10&name=dd') // 보통은 변수로 들어감
/* navigate(`/product?page=${id}&num=${num}&name=${name}`) -> 이런 식으로 들어감 */
}
2) restfull routes : https://woojinger.tistory.com/32
- api 디자인 패턴
- https://learn.co/lessons/sinatra-restful-routes-readme : Routes Overview 보기, url 디자인 권고사항
- 상세페이지 표시로 :id 쓸거임, id 는 라우트의 파라미터로 변수 같은 개념, 어떤 값이 들어와도 무조건 상세페이지를 표시해줌
- /product/:id 는 이 아이디 값을 가진 아이템을 보여준다는 뜻
3) app.js 에 restfull routes 적용
- <Route path='/product:id' element={<ProductDetail />} />
- import ProductDetail from './pages/ProductDetail';
4) ProductDetail.js
- import { useSearchParams } from 'react-router-dom'
- const params = useParams();
- id 가 key 값, value 가 id
4. 로그인 페이지 구현
1) 기본틀 만들기
- User.js 컴포넌트 생성
- route > RedirectsRoute.js 생성
2) Redirects
방향을 다른 페이지로 돌려 주는 것
3) app.js
- <Route path='/login' element={<Login />} /> 추가
- <Route path='/product/:id' element={<UserRedirects />} /> 추가
4) RedirectsRoute.js
import React from 'react';
import Detail from '../components/Detail';
import { Navigate } from 'react-router-dom'
import { useSelector } from 'react-redux/es/exports';
const UserRedirects = () => {
const user = useSelector((state)=>state.user.value)
return user === true ? <Detail /> : <Navigate to='/login' />;
};
export default UserRedirects;
5. 에러 났던 장바구니 react12 에서 이어서
1) CartItem.js
const restItems = cart.filter((ele, index) => {
let indexNum;
if (ele.id === item.id) {
indexNum = index;
}
return indexNum;
})
→ 지우고 다시
- const restItems = cart.filter((ele, index) => ele.id === item.id) 로 수정
- ele.id === item.id : 일치하는 것은 하나이기 때문에 계속 index는 0 번일 것임
2) cartSlice.js
- deleteCart 에서 const num = state.findIndex (어레이의 메서드) 로 검사할 것임
- const num = state.findIndex((ele)=>ele.id===action.payload[0].id)
console.log(action.payload)
/* 0: {id: 1, product_name: '검정칼라 스웨터', price: 35000, product_img: 'https://picsum.photos/200', is_checked: 'false'}
length: 1
[[Prototype]]: Array(0) */
- state.splice(num, 1) 로 수정
- 지난 번에 왜 안 됐는지 알아야 함
장바구니에 같은 애 못 담게
- state = state.push(action.payload); 수정
- console.log(action.payload.id) : 클릭한 id 가 뜸
- const equalItem = state.findIndex(ele=>ele.id===action.payload.id);
if(equalItem>=0){
alert ('장바구니에 동일한 상품이 있습니다.')
}else{
state = state.push(action.payload);
}
→ 과제 : 장바구니 총 합산 구하기
6. redux-persist : 로컬스토리지 사용할 수 있게 만드는 라이브러리
- https://redux-toolkit.js.org/tutorials/overview 참고
- npm install redux-persist : 설치 먼저
1) store 에 index.js
import storage from 'redux-persist' //로컬
import storageSession from 'redux-persist/lib/storage/session' //세션
- import { persistReducer } from 'redux-persist' : reducer 가 실행될 때 persist 를 같이 사용하게 묶어서 사용할 수 있게 하는 것
- import { configureStore, combineReducers } from '@reduxjs/toolkit' : combineReducers 여러가지 reducer 를 묶어서 사용할 수 있게 하는 것(login reducer 도 만들 것이고 persist 도 만들 것이기 때문에 사용함)
- persistConfig 변수 만들고 key: 'root', storage, 적기
- 여기서 key 값이란 localstorage 에 저장될 때의 key 값
- const rootReducer = combineReducers({ cart : cartSlice, })
const persistedReducer = persistReducer(persistConfig, rootReducer)
// persistConfig 와 rootReducer 를 묶어서 사용할 수 있게 해줌,
redux-persist 를 사용해서 로컬스토리지에 state 저장하면 페이지가 새로고침 되어도 초기값(initialState)이 로컬스토리지에 저장된 state 값으로 대체되기 때문에 초기화되지 않는다.
- const store = configureStore({ reducer : persistedReducer, }) 적은 후
export default configureStore({
reducer: {
cart: cartSlice
}
})
→ 지우고 export default store; 로 수정
2) src 폴더의 index.js 파일
- import { PersistGate } from 'redux-persist/integration/react';
- let persistor = persistStore(store);
<PersistGate persistor={persistor}>
<App />
</PersistGate>
→ 수정
- import { persistStore } from "redux-persist";
3) store 폴더의 index.js 파일
- middelware : 사가, 텅크? 두가지가 있음. redux 에서의 비동기처리. 사가는 따로 설치해야 하고 텅크는 toolkit 에 있음
7. 로그인
1) Login.js 파일 생성
<form>
<div>
<div><label htmlFor='userId'>아이디</label></div>
<div><input type="text" id="userId" placeholder='아이디입력' /></div>
</div>
<div>
<div><label htmlFor='userPass'>비밀번호</label></div>
<div><input type="password" id="userPass" /></div>
</div>
<div>
<input type="submit" value="로그인" />
</div>
</form>
→ 기본틀 입력
- app.js 에 <Route path='/login' element={<Login />} />
- Detail.js 파일 생성, 기본틀 작성
- route 폴더 생성해서 밑에 UserRedirects.js 파일 생성
2) UserRedirects.js
import React from 'react';
import Detail from '../components/Detail';
import { Navigate } from 'react-router-dom'
import { useSelector } from 'react-redux/es/exports';
const UserRedirects = () => {
const user = useSelector((state)=>state.user.value)
return user === true ? <Detail /> : <Navigate to='/login' />;
};
export default UserRedirects;
3) app.js
- <Route path='/product/:id' element={<UserRedirects />} />
- import UserRedirects from './route/UserRedirects';
4) store > user > userSlice.js
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: false,
}
export const userSlice = createSlice({
name: 'user', // reducer 의 이름
initialState, // 데이터의 초기값
reducers: { // 상태가 변하면 어떻게 실행될지 지정
login: (state, action) => {
state.value = action.payload;
action.type='SET_USER_LOGIN'
},
logout: (state, action) => {
state.value = action.payload;
action.type='SET_USER_LOGOUT'
},
},
})
export const { login, logout } = userSlice.actions
export default userSlice.reducer
→ 초기 세팅
5) store > index.js
- user:userSlice 추가
- import { userSlice } from './user/userSlice'; 추가
6) login.js
- <form onSubmit={(e)=>loginUser(e)}> 수정
- 변수 loginUser 추가
const loginUser = (e)=>{
e.preventDefault();
dispatch(login(true))
navigate('/')
}
7) nav.js
- import useNavigate, useDispatch, logout
const user=useSelector((state) => state.user.value)
const dispatch = useDispatch()
const navigate = useNavigate()
→ 추가
{
user ?
( <span className="user" onClick={()=>{dispatch(logout(false))}}>로그아웃</span>) :
(<span className="user" onClick={() =>
navigate("/login")}>로그인</span>)
}
→ 출력되어야 하는 곳에 추가
8. 프라이빗페이지 리다이렉트
1) ProductItem
const navigate = useNavigate();
const goToDetail=()=>{
navigate(`/product/${item.id}`)
}
→ 추가
- import { useNavigate } from 'react-router-dom';
- <div className="item-name" onClick={goToDetail}>{item.product_name}</div> 으로 수정
2) Detail
- import {useParams} from 'react-router-dom'
- let params = useParams();
- console.log('params', params.id)
- <h2>제품아이디는 {params.id}</h2>