2주차 - 로그인하기
본문 바로가기
항해 중/5주차 리액트 심화반

2주차 - 로그인하기

by 은돌1113 2021. 11. 30.

1. 헤더 분기하고 리덕스 설치하기

더보기

👉 클라이언트의 입장에서 로그인 하기는 사실 별 게 없어요.

우리 서버에 로그인 요청을 보내고, 응답을 받아서 토큰을 저장하면 끝입니다.

 

- 로그인 하고 나면 뭐가 바뀔까??

: 로그인을 하면 가장 먼저 생각 나는 건 헤더 컴포넌트 바뀌는 겁니다.

 

(헤더 컴포넌트 분기하기)

import React from "react";
import {Grid, Text, Button} from "../elements";

const Header = (props) => {

    const [is_login, setIsLogin] = React.useState(false);
    if(is_login){
        return (
          <React.Fragment>
            <Grid is_flex padding="4px 16px">
              <Grid>
                <Text margin="0px" size="24px" bold>
                  헬로
                </Text>
              </Grid>

              <Grid is_flex>
                <Button text="내정보"></Button>
                <Button text="알림"></Button>
                <Button text="로그아웃"></Button>
              </Grid>
            </Grid>
          </React.Fragment>
        );
    }
    return (
        <React.Fragment>
            <Grid is_flex padding="4px 16px">
                <Grid>
                    <Text margin="0px" size="24px" bold>헬로</Text>
                </Grid>
                
                <Grid is_flex>
                    <Button text="로그인"></Button>
                    <Button text="회원가입"></Button>
                </Grid>
            </Grid>
        </React.Fragment>
    )
}

Header.defaultProps = {}

export default Header;

 

- 리덕스 설치하기

더보기

👉 지금 우리는 로그인 상태를 알기 위해 매번 뭘하고 있나요? 그렇죠. 매번 쿠키를 체크하고 있습니다.

이걸 공용함수로 빼놨으니 비교적 편하게 하고는 있는데, 그래도 아직 좀 불편하죠.

로그인 상태를 리덕스에 저장하고 어떤 컴포넌트에서든 편히 볼 수 있게 해봐요. 🙂

# 이건 리덕스와 리덕스 모듈 내에서 경로 이동까지 하게 해줄 히스토리, 라우터와 히스토리를 엮어줄 모듈까지 한번에 설치해보는 거예요.
yarn add redux react-redux redux-thunk redux-logger history@4.10.1 connected-react-router@6.8.0
# 프론트엔드의 꽃 리액트 강의에서는 리듀서에서 뭔가를 바꿀 때 불변성 관리를 우리가 신경썼죠.
# 이번엔 우리가 신경쓰지 말고, 임머라는 패키지 사용해서 해볼거예요.
# 그리고 액션도 매번 맨 위에서 user/CREATE처럼 리듀서 모듈 명에 어떤 타입 넣어서 만들고, 
# 그 아래에는 액션 생성 함수 만들어서 export 다 해주고 좀 귀찮았죠.
# 이걸 redux-actions라는 패키지로 좀 더 편하게 쓸거예요.
yarn add immer redux-actions

2. 유저 모듈 만들기

더보기

👉 설치한 redux-actions와 immer를 사용해서 액션과 액션 생성 함수, 리듀서를 만드는 방법, 알아봅시다!

이 immer는 불변성 관리를 , redux-actions는 좀 더 편한 액션 관리를 위해 사용해요.

 

→ Q. 왜 불변성 관리하는데 패키지까지 써야하나요?

A. 객체는 const로 선언해도 내용이 수정될 수 있죠! 그래서 스프레드 문법 ({...어떤 것} 이렇게 쓰는 거!) 등을 이용해서 수정되지 않게 주의해서 코드를 작성합니다. 그런데, 객체 구조가 복잡해지면 코드를 짜기가 번거로워요. 그래서 불변성을 신경 쓰지 않는 것처럼 써도 알아서 불변성을 유지해주는 immer를 사용하는거예요.

 

[시작하기 전에 잠깐!]

항상 폴더부터 만들고 시작해야해요!

src 폴더 아래에 redux폴더, 그리고 그 아래에 modules 폴더를 만들어주세요.

 

1) import 하기

// user.js

port { createAction, handleActions } from "redux-actions";
import { produce } from "immer";

import { setCookie, getCookie, deleteCookie } from "../../shared/Coo

2) 액션 타입 만들기

// user.js

const LOG_IN = "LOG_IN";
const LOG_OUT = "LOG_OUT";
const GET_USER = "GET_USER";

3) 액션 생성 함수 만들기

// user.js

const logIn = createAction(LOG_IN, (user) => ({ user }));
const logOut = createAction(LOG_OUT, (user) => ({ user }));
const getUser = createAction(GET_USER, (user) => ({ user }));

4) initalState 만들기

// user.js

const initialState = {
  user: null,
  is_login: false,
};

5) 리듀서 만들기 (feat.immer)

더보기

👉 immer 사용 예는 공식 사이트에서 볼 수 있어요! (링크)

// user.js

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
);

6) actionCreators 내보내기

// user.js

const actionCreators = {
  logIn,
  getUser,
  logOut,
};

export { actionCreators };

3. 리덕스 스토어 만들기

더보기

👉 스토어 만들 때는 어떻게 하나요?

- combineReducers()를 사용해서 export한 reducer를 모아 root reducer를 만들고,

- 미들웨어를 적용해주고,

- createStore()를 사용해서 root reducer와 미들웨어를 엮어 스토어를 만든다!

// configureStore.js

// ** import 하기 **
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();

// ** rootreducer 만들기 **
const rootReducer = combineReducers({
  user: User,
  router : connectRouter(history), // 라우터랑 history 연결
});

// ** rootreducer 만들기 **
const middlewares = [thunk.withExtraArgument({history:history})];

// 지금이 어느 환경인 지 알려줘요. (개발환경, 프로덕션(배포)환경 ...)
const env = process.env.NODE_ENV;

// ** 미들웨어 준비 ** 
// 개발환경에서는 로거라는 걸 하나만 더 써볼게요.
if (env === "development") {
  const { logger } = require("redux-logger");
  middlewares.push(logger);
}

// ** reduxdevTools 설정 **
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();

4. 스토어에 주입하기

 

1) 리덕스와 컴포넌트 연결하기

더보기

👉 스토어를 주입할 때 Provider라는 걸 써요. Provider를 index.js에서 주입 할 거예요. 그 다음에는 App.js에서 원래 BrowserRouter와 Route를 써서 컴포넌트에 주입하던 history를 ConnectedRouter를 써서 리덕스랑 같은 history를 사용하도록 해줄게요. (그래야 히스토리를 공유하겠죠!)

- index.js에서 스토어 주입하기

import store from "./redux/configureStore";
import { Provider } from "react-redux";

ReactDOM.render(
  <Provider store={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}>
          <Route path="/" exact component={PostList} />
          <Route path="/login" exact component={Login} />
          <Route path="/signup" exact component={Signup}/>
        </ConnectedRouter>
      </Grid>
    </React.Fragment>
  );
}

2) 로그인 액션 실행하기 (리덕스 훅을 사용해서 간단하게 해봅니다)

 

- 로그인 페이지에서 리덕스 훅 사용하기

import React from "react";
import { getCookie, setCookie, deleteCookie } from "../shared/Cookie";
import { Text, Input, Grid, Button } from "../elements";
import { useDispatch } from "react-redux";
import {actionCreators as userActions} from '../redux/modules/user'

const Login = (props) => {

  const dispatch = useDispatch();
  let id = React.useRef();
  let pw = React.useRef();

  const login = () => {
    dispatch(userActions.logInAction({user_name : "saebom"}));
  };

  return (
    <React.Fragment>
      <Grid padding="16px">
        <Text size="32px" bold>
          로그인
        </Text>

        <Grid padding="16px 0px">
          <Input
            label="아이디"
            placeholder="아이디를 입력해주세요."
            _onChange={() => {
              console.log("아이디 입력했어!");
            }}
            refInfo={id}
          />
        </Grid>

        <Grid padding="16px 0px">
          <Input
            label="패스워드"
            placeholder="패스워드 입력해주세요."
            _onChange={() => {
              console.log("패스워드 입력했어!");
            }}
            refInfo={pw}
          />
        </Grid>

        <Button
          text="로그인하기"
          _onClick={() => {
            login();
          }}
        ></Button>
      </Grid>
    </React.Fragment>
  );
};

export default Login;

- 로그인 하면 메인 페이지로 이동하기

// middleware actions
const logInAction = (user)=>{
    return function (dispatch, getState, {history}) {
        console.log(history);
        dispatch(logIn(user));
        history.push("/");
    }
}
...

// action creator export
const actionCreators = {
  logIn,
  getUser,
  logOut,
  loginAction,
};

 

3) 헤더에서 스토어 데이터 보기 (리덕스 훅을 사용해서 간단하게 해봅니다)

 

- Header.js에서 리덕스 훅 사용하기

import { useSelector, useDispatch } from "react-redux";
import { actionCreators as userActions } from "../redux/modules/user";
...
const Header = (props) => {
const dispatch = useDispatch();
const is_login = useSelector((state) => state.user.is_login);

    if(is_login){
        return (
          <React.Fragment>
            <Grid is_flex padding="4px 16px">
              <Grid>
                <Text margin="0px" size="24px" bold>
                  헬로
                </Text>
              </Grid>

              <Grid is_flex>
                <Button text="내정보"></Button>
                <Button text="알림"></Button>
                <Button text="로그아웃" _onClick={() => {dispatch(userActions.logOut({}));}}></Button>
              </Grid>
            </Grid>
          </React.Fragment>
        );
    }
    return (
        <React.Fragment>
            <Grid is_flex padding="4px 16px">
                <Grid>
                    <Text margin="0px" size="24px" bold>헬로</Text>
                </Grid>
                
                <Grid is_flex>
                    <Button text="로그인"></Button>
                    <Button text="회원가입"></Button>
                </Grid>
            </Grid>
        </React.Fragment>
    )
}

Header.defaultProps = {}

export default Header;

댓글