본문 바로가기
Development(Web, Server, Cloud)/22) React.js & Node.js

React.js & Node.js 19일차

by tonyhan18 2022. 1. 24.
728x90

이제 서버에서 받은 토큰을 가지고 사용하는 것부터해서 시작해보자

 

토큰을 localstorage에 담아서 사용하고 도메인마다 정보가 다르다. 그리고 localStorage에 있는 것은 컴퓨터가 꺼지기 전에는 계속해서 정보가 남아있다. 반드시 토큰을 이 localStorage에 저장해야만 한다. 그래야 로그인정보가 남아있기 때문이다.

 

      //로그인 성공
      // 1. localStorage에 token 저장
      // 2. axios instance의 header에 token입력
      // 3. useContext isLogin을 true로

useContext를 쓰면 컴포넌트 밖에 정보를 저장한다. 그래서 밖에서 필요한 컴포넌트만 직접 접근해서 받아올 수 있게 한다.

 

보는 바와 같이 Local Storage에 있는 토큰은 새로고침해도 사라지지 않는다.

 

해서 위와같이 작성해주면 서버쪽에서도 Authorization에 담아서 가져오기 때문에 토큰이 저기에 담긴다.

 

 

어지저찌 성공시키었다. 왜 되는지는 잘 모르겠다.

 

이렇게하면 localStorage에 토큰이 있으니 header에 token이 담겨져 있는 것을 볼 수 있을 것이다.

 

contexts라는 폴더를 따로 만들어 안에 user.js 를 놓아 Context 로 사용할 객체를 정의해야 한다. 이떄 key값과 변수값을 바꿀 함수를 함께 포함한다.

 

 

하고 위와같이 함수까지해서 함께 전달해준다.

!!token으로 token이 있으면 true이다.

 

헛점이 있는데 원래라면 token도 검증해주어야 하는데 지금은 해당 API를 짜기 힘드니 스킵

 

그리고 UserContext.Provider로 value를 전달해준다. 사실 UserContext는 다른 컴포넌트에 써도 되지만 지금은 위와같이 토큰을 전체에 뿌려주자

 

결국 Login.jsx에서 UserContext를 사용할 수 있게 된다.

 

그래서 setIsLogin을 true로 바꾸고 navigate로 보내버리자

 

이걸 useEffect에 넣어서 Login에서 처리하던것을 여기에서 처리하게끔 만들자. 그래서 여기 주소로 오게되면 자연스럽게 데이터가 삭제될 것이다.

 

logout 접속시 토큰도 함께 삭제되었다.

원래는 왼쪽 처럼 되어 있었는데 오른쪽으로 바꾸었다.

 

이제 userContext를 실재로 써보자. 예를 들어 logout 상태인데 Main페이지로 들어올려고 하면 Login 페이지로 쫓아내는 작업을 할것이다.

 

라우터까지 추가시키면 된다.

 

 

이렇게 해놓으면 `/`로 접속시 login 페이지로 쫓아내버린다. 그런데 쫓아내는 코드가 이만큼이나 되는데 앞으로 쫓아내는 코드가 늘어날 것이다. 그런데 그때마다 이걸 복붙해주는건 무리다. 그래서 이것을 따로 빼서 로그인이 필요한 페이지에서만 이 코드가 실행될 수 있도록 만들어주자.

 

우선 이 코드를 한번에 쓰기 위해서는 Router에서 써주면 될꺼같다. 왜냐하면 어찌되었든 Router은 무조건 오기 때문이다.

 

이를 위해 outlet이라는 react-router의 중첩 라우팅할때 쓰는 훅을 쓰자

 

위와같이 작성해주면 된다.

 

Main에 있던걸 쫓아내고 LoginCheck에 넣어주었다.

 

이렇게해도 동일하게 작동하는 것을 확인할 수 있다.

 

이렇게 해놓고 Outlet을 가져와 LoginCheck 내에 있는 컴포넌트를 어디에 출력할지 결정해주자

 

이런식으로 중첩라우팅을 하면서 자식라우터가 부모의 어느 부분 어디 위치에 출력될지를 결정할 수 있게 된다.

 

이제 Main 페이지를 구현하자 login 한 상태이면 항상 nav가 존재한다.

 

\

하기전에 snippets를 미리 다운 받아서 사용해보자

 

위와같이 Main을 TopNav로 감싸주어서 사용하자

 

import React from "react";
import { Link, Outlet } from "react-router-dom";
import styled from "styled-components";

function TopNav() {
  return (
    <div>
      <Header>
        <Main>
          <Link to="/">
            <ImgLogo src="https://www.instagram.com/static/images/web/mobile_nav_type_logo-2x.png/1b47f9d0e595.png" />
          </Link>
          <SearchWrapper>
            <InputSearch plceholder="검색" />
          </SearchWrapper>
          <Nav></Nav>
        </Main>
      </Header>
      <Outlet />
    </div>
  );
}

const Header = styled.header`
  position: fixed;
  top: 0%;
  width: 100%;
  background-color: white;
  border-bottom: 1px solid #dbdbdb;
`;
const Main = styled.main`
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 auto;
  height: 54px;
  max-width: 975px;
`;
const ImgLogo = styled.img`
  width: 104px;
`;
const SearchWrapper = styled.div`
  display: flex;
  align-items: center;
  padding: 3px 16px;
  box-sizing: border-box;
  height: 36px;
  min-width: 125px;
  width: 268px;
  background-color: #efefef;
  border-radius: 8px;
`;
const InputSearch = styled.input`
  background: transparent;
  border: none;
  width: 100%;
  height: 100%;
`;
const Nav = styled.nav``;

export default TopNav;

 

 

문제는 위와같이 짜고나서 svg로 되어 있는 선택할 수 있는 부분들이다. svg는 글자이기 때문에 instagram에서 직접 코드를 복사해오자 그리고 이걸 담을 폴더를 만들자.

 

이렇게 가져왔다. 이것들을 index.js로 가져오기 위해 다음과 같이 처리하자

 

이때 svg 파일은 react component파일로 가져올 수 있다.

 

나머지 파일들 총 5개를 모두 처리해주자

 

이제 만들게 dropdown, modal 등이다.

 

import React from "react";
import { Link, Outlet } from "react-router-dom";
import styled from "styled-components";
import {
  IconHome,
  IconActivity,
  IconDirect,
  IconExplore,
  IconNewPost,
} from "../../assets/images/icons/index";

function TopNav() {
  return (
    <div>
      <Header>
        <Main>
          <Link to="/">
            <ImgLogo src="https://www.instagram.com/static/images/web/mobile_nav_type_logo-2x.png/1b47f9d0e595.png" />
          </Link>
          <SearchWrapper>
            <InputSearch plceholder="검색" />
          </SearchWrapper>
          <Nav>
            <IconWrapper>
              <IconHome />
            </IconWrapper>
            <IconWrapper>
              <IconDirect />
            </IconWrapper>
            <IconWrapper>
              <IconNewPost />
            </IconWrapper>
            <IconWrapper>
              <IconExplore />
            </IconWrapper>
            <IconWrapper>
              <IconActivity />
            </IconWrapper>
          </Nav>
        </Main>
      </Header>
      <Outlet />
    </div>
  );
}

const Header = styled.header`
  position: fixed;
  top: 0%;
  width: 100%;
  background-color: white;
  border-bottom: 1px solid #dbdbdb;
`;
const Main = styled.main`
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 auto;
  height: 54px;
  max-width: 975px;
`;
const ImgLogo = styled.img`
  width: 104px;
  vertical-align: bottom;
`;
const SearchWrapper = styled.div`
  display: flex;
  align-items: center;
  padding: 3px 16px;
  box-sizing: border-box;
  height: 36px;
  min-width: 125px;
  width: 268px;
  background-color: #efefef;
  border-radius: 8px;
`;
const InputSearch = styled.input`
  background: transparent;
  border: none;
  width: 100%;
  height: 100%;
`;
const Nav = styled.nav`
  display: flex;
  justify-content: space-between;
`;
const IconWrapper = styled.div`
  cursor: pointer;
  display: flex;
  align-items: center;
  & + & {
    margin-left: 22px;
  }
`;

export default TopNav;
728x90