로그인/회원가입 구현

ujin·2022년 11월 20일
0

개발

목록 보기
4/5

로그인/회원가입

로직 순서

로그인/회원가입

input, button (…props, children 사용하기)

Login, SignUp 페이지 단위의 컴포넌트를 만들고 input과, button을 component로 만들어서 가져오고 싶었다.

input.jsx

function Input({ ...props }) {

  return (
    <Wrapper>
      <InputText {...props}></InputText>
    </Wrapper>
  )
}

export default Input;
  • 스프레드 연산자를 이용해서 inputText에 필요한 값들을 객체로 …props로 만들어준다.

Login.jsx


function Login() {

  return (
    <Wrapper>
      <Container>
        <Title>Welcome</Title>
        <form onSubmit={onSubmit}>
          <Input name="email" placeholder="이메일" />
          <Input name="password" type="password" placeholder="비밀번호" />
          <Button type="submit">Sign In</Button>
        </form>
      </Container>
      <Link to='/signup'>
      <Footer>계정이 없으신가요?</Footer>
      </Link>
    </Wrapper>
  )
}

export default Login;
  • type이랑 placeholder를 Input 컴포넌트에 작성만 해주면 된다

button은 children을 이용하였는데

Button.jsx

function Button({children, ...props}) {
  return (
    <ButtonStyled {...props}>{children}</ButtonStyled>
  )
}

export default Button;
  • props와 children을 객체로 넘겨준다.
  • 위의 Login 코드에 똑같이 type을 넘겨주면 된다

event 사용하기


function Login() {
  const navigate = useNavigate();
  const onSubmit = (e) => {
    e.preventDefault();

  return (
    <Wrapper>
      <Container>
        <Title>Welcome</Title>
        <form onSubmit={onSubmit}>
          <Input name="email" placeholder="이메일" />
          <Input name="password" type="password" placeholder="비밀번호" />
          <Button type="submit">Sign In</Button>
        </form>
      </Container>
      <Link to='/signup'>
      <Footer>계정이 없으신가요?</Footer>
      </Link>
    </Wrapper>
  )
}

export default Login;
  • form 태그로 감싸서 이벤트를 사용하였다. state를 이용해 볼 수도 있음 !!

유효성 검사

function Login() {
  const navigate = useNavigate();
  const onSubmit = (e) => {
    e.preventDefault();

    if (!e.target.email.value) {
      alert("이메일을 입력하세요");
      e.target.email.focus()
      return
    }
    if (!e.target.password.value) {
      alert("비밀번호를 입력하세요");
      e.target.password.focus()
      return
    }
    console.log(e.target.email.value);
  }

  return (
    <Wrapper>
      <Container>
        <Title>Welcome</Title>
        <form onSubmit={onSubmit}>
          <Input name="email" placeholder="이메일" />
          <Input name="password" type="password" placeholder="비밀번호" />
          <Button type="submit">Sign In</Button>
        </form>
      </Container>
      <Link to='/signup'>
      <Footer>계정이 없으신가요?</Footer>
      </Link>
    </Wrapper>
  )
}

export default Login;
  • 이메일을 입력하지 않고 버튼을 클릭 했을 때 alert창 띄워줬다. 이부분은 ux 영역인데 프론트엔드라면 여기까지도 신경을 써줘야 한다.

axios 요청 (회원가입)

function SignUp() {

signUp({
      email: e.target.email.value,
      password: e.target.password.value,
      name: e.target.name.value,
      gender: e.target.gender.value
    })

  const signUp = async({email, password, name, gender}) => {
    try{
      await axios.post('/user/signup', {
        email,
        password,
        name,
        gender
      })
      alert('회원가입이 완료되었습니다');
    } catch (err) {
      console.log(err)
    }
  }

  return (
    <Wrapper>
      <Container>
        <Title>Welcome</Title>
        <form onSubmit={onSubmit}>
          <Input name="email" placeholder="이메일" />
          <Input name="password" type="password" placeholder="비밀번호" />
          <Input name="name"placeholder="이름" />
          <Input name="gender" placeholder="성별" />
          <Button type="submit">Sign Up</Button>
        </form>
      </Container>
      <Link to='/'>
      <Footer>로그인으로</Footer>
      </Link>
    </Wrapper>
  )
}

export default SignUp;
  • 회원 가입에서는 데이터를 응답 받을 필요가 없으니 response는 없다.
  • signUp 함수를 만들고 이벤트 함수 안에서 실행 해주면 끝.
  • 여기서 더 나아가 버튼을 클릭했을 때 비활성화 시켜주고 싶어서 disabled를 toggle로 구현 해주었다
function SignUp() {
  const [toggle, setToggle] = useState(false);

  const onSubmit = (e) => {
    e.preventDefault();
    // 유효성 검사
    if (!e.target.email.value) {
      alert("이메일을 입력하세요");
      e.target.email.focus()
      return
    }
    if (!e.target.password.value) {
      alert("비밀번호를 입력하세요");
      e.target.password.focus()
      return
    }
    if (!e.target.name.value) {
      alert("이름을 입력하세요");
      e.target.name.focus()
      return
    }
    if (!e.target.gender.value) {
      alert("성별을 입력하세요");
      e.target.gender.focus()
      return
    }
    // console.log(e.target.email.value)
    signUp({
      email: e.target.email.value,
      password: e.target.password.value,
      name: e.target.name.value,
      gender: e.target.gender.value
    })
  }

  const signUp = async({email, password, name, gender}) => {
    try{
      await axios.post('/user/signup', {
        email,
        password,
        name,
        gender
      })
      alert('회원가입이 완료되었습니다');
      setToggle(true)
    } catch (err) {
      console.log(err)
    }
  }

  return (
    <Wrapper>
      <Container>
        <Title>Welcome</Title>
        <form onSubmit={onSubmit}>
          <Input name="email" placeholder="이메일" />
          <Input name="password" type="password" placeholder="비밀번호" />
          <Input name="name" placeholder="이름" />
          <Input name="gender" placeholder="성별" />
          <Button type="submit" disabled={toggle}>Sign Up</Button>
        </form>
      </Container>
      <Link to='/'>
      <Footer>로그인으로</Footer>
      </Link>
    </Wrapper>
  )
}

export default SignUp;
  • 비활성화 즉, true로 기본으로 값을 주어진다. 문자열이 아닌 boolen 타입으로
  • 그럼 우리는 값이 변할 때를 버튼이 비활성화 되어야 하니까 useState를 사용한다. const [toggle, setToggle] = useState(false); → boolen 타입이니까 초기값을 우리는 true가 아닌 false로 준다. → disabled={toggle} : toggle을 객체로 담아주고 → setToggle(true) : 회원가입이 성공 했을 때 버튼이 비활성화가 되어야 하니까 alert창 띄어주는 코드 다음에 코드를 작성해준다.

axios 요청 (로그인)

로그인도 회원 가입처럼 유효성 검사까지 한 후 axios 요청을 보내야 한다.

function Login() {
  const navigate = useNavigate();
  const onSubmit = (e) => {
    e.preventDefault();

    if (!e.target.email.value) {
      alert("이메일을 입력하세요");
      e.target.email.focus()
      return
    }
    if (!e.target.password.value) {
      alert("비밀번호를 입력하세요");
      e.target.password.focus()
      return
    }
    console.log(e.target.email.value);
    signUp({
      email: e.target.email.value,
      password: e.target.password.value,
    })
  }

  const signUp = async({email, password}) => {
    try{
      const response = await axios.post('/user/signin', {
        email,
        password,
      })
      alert('로그인이 완료되었습니다');
      console.log(response.data.data.token)
      localStorage.setItem('token', response.data.data.token)
      navigate("/main");
    } catch (err) {
      console.log(err)
      alert('로그인에 실패했습니다');
    }
  }

export default Login;
  • 회원 가입과 같이 값을 요청 보낸다.
  • 회원 가입과 다른 점은 로그인에서는 서버가 보내준 토큰을 저장시켜야 하는데 우리는 토큰이 response → data → data 안에 있는 것을 확인 하고 setItem으로 localStorage에 저장한다.
  • 그리고 성공 했을 시 navigate를 이용해서 main 페이지로 리다이렉트 해주면 된다.

localStorage 토큰 유무 검사

App.js에서 우리는 토큰이 있을 때와 없을 때 페이지 리다이렉트를 구현 해야한다.

useEffect(function () {
    // localstorage에 저장되어 있는 토큰 값이 있는지 없는지 검사
    const token = localStorage.getItem("token");
    console.log(location);
    const isCurrentRootPath = location.pathname === "/"; // isCurrentRootPath 로그인 페이지

    if (!token) {
      // token이 없을 경우
      if (!isCurrentRootPath) {
        // 로그인 페이지가 아닐 때
        navigate("/"); // 로그인 페이지로 이동
      }
    } else {
      // token이 있을 경우 (로그인 기록이 있을 때) -> useProperty 가져와야함 -> 저장 방법 (recoil)
      // 토큰이 만료되었을 때 오류 처리
      if (isCurrentRootPath) {
        // 로그인 페이지일 때
        navigate("/main"); // main 페이지로 이동
      }
    }
  }, []);
  • useEffect를 사용해서 토큰 유무 검사해준다.
  • token이 없을 경우 (로그인 된 적이 없다) → 그대로 즉, 로그인 페이지에 머물고
  • token이 있을 경우 (로그인 기록이 있을 경우) → main 페이지로 이동한다

미들웨어 axios 인스턴스화

에러를 확인할 때 console에서 하나하나 찾기 귀찮아서 middleware을 만들어서 요청을 보냈다.

import axios from "axios";

const middleware = axios.create({
  baseURL: "http://",
});

middleware.interceptors.request.use(
  function (request) {
    return request;
  },
  function (error) {
    return error;
  }
);

middleware.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    if (!error.response.data) {
      throw new Error("알 수 없는 오류");
    }

    throw error.response.data;
  }
);

export default middleware;

axios header에 token 저장

import axios from "axios";

const middleware = axios.create({
  baseURL: "http://",
});

export const setHeader = token => {
  // 1. 헤더에 token을 넣어줌
  middleware.defaults.headers.common["Authorization"] = `Bearer ${token}`;
};

middleware.interceptors.request.use(
  function (request) {
    return request;
  },
  function (error) {
    return error;
  }
);

middleware.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    if (!error.response.data) {
      throw new Error("알 수 없는 오류");
    }

    throw error.response.data;
  }
);

export default middleware;

token이 있을 경우 setHeader을 실행

axios를 사용할 때마다 자동으로 header에 Authorization이 적용될 수 있도록 한다

useEffect(function () {
    // localstorage에 저장되어 있는 토큰 값이 있는지 없는지 검사
    const token = localStorage.getItem("token");
    console.log(location);
    const isCurrentRootPath = location.pathname === "/"; // isCurrentRootPath 로그인 페이지

    if (!token) {
      // token이 없을 경우
      if (!isCurrentRootPath) {
        // 로그인 페이지가 아닐 때
        navigate("/"); // 로그인 페이지로 이동
      }
    } else {
      // token이 있을 경우 (로그인 기록이 있을 때) -> useProperty 가져와야함 -> 저장 방법 (recoil)
      // 토큰이 만료되었을 때 오류 처리
      if (isCurrentRootPath) {
				setHeader(token);
        // 로그인 페이지일 때
        navigate("/main"); // main 페이지로 이동
      }
    }
  }, []);

token이 있을 경우 getProperty

async function getProperty(isCurrentRootPath) {
    try {
      const response = await axios.get("/user/property");
      setUser(response.data.data.user);
      if (isCurrentRootPath) {
        // 로그인 페이지일 때
        navigate("/main"); // main 페이지로 이동
      }
    } catch (err) {
      console.log(err);
			navigate("/"); // 로그인 페이지로 넘어감 -> 없을 때
    }
  }
useEffect(function () {
    // localstorage에 저장되어 있는 토큰 값이 있는지 없는지 검사
    const token = localStorage.getItem("token");
    console.log(location);
    const isCurrentRootPath = location.pathname === "/"; // isCurrentRootPath 로그인 페이지

    if (!token) {
      // token이 없을 경우
      if (!isCurrentRootPath) {
        // 로그인 페이지가 아닐 때
        navigate("/"); // 로그인 페이지로 이동
      }
    } else {
      // token이 있을 경우 (로그인 기록이 있을 때) -> useProperty 가져와야함 -> 저장 방법 (recoil)
      // 토큰이 만료되었을 때 오류 처리
      if (isCurrentRootPath) {
				setHeader(token);
				getProperty(isCurrentRootPath);
      }
    }
  }, []);

userPropertyState를 중앙 관리해준다

→ store 폴더에 user.js 파일을 만들고 npm recoil → atom 코드 작성 → index.js에 RecoilRoot 해주기(?)

import { atom } from "recoil";

export const usePropertyState = atom({
  // userProperty를 중앙 관리함
  key: "usePropertyState",
  default: null,
});

main 페이지에 board를 불러옴

import React, { useEffect, useState } from 'react';
import axios from '../../api/config';
import { usePropertyState } from '../../store/user';
import { useRecoilState } from 'recoil';

function Main() {
  const [board, setBoard] = useState([]);
  const [userProperty, setUserProperty] = useRecoilState(usePropertyState)

  useEffect(() => {
    async function getData(){ // board를 가져오는 함수
      try {
        const response = await axios.get('/board', { params: { page:1, size:30 }}) // object에서 params 값을 추가해서 요청, page1
        console.log(response)
      } catch (err) {
          console.log(err);
      }
    }
    if(userProperty) { // userProperty가 있을 경우
      getData(); // getData 실행
    }
  }, [userProperty]) 
 
  return (
    <div>Main</div>
  )
}

export default Main;

token 만료시 오류 처리

import axios from "axios";

const middleware = axios.create({
  baseURL: "http://",
});

export const setHeader = token => {
  // 1. 헤더에 token을 넣어줌
  middleware.defaults.headers.common["Authorization"] = `Bearer ${token}`;
};

export const removeHeader = () => {
  delete middleware.defaults.headers.common["Authorization"];
};

middleware.interceptors.request.use(
  function (request) {
    return request;
  },
  function (error) {
    return error;
  }
);

middleware.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    if (!error.response.data) {
      throw new Error("알 수 없는 오류");
    }

    throw error.response.data;
  }
);

export default middleware;
async function getProperty(isCurrentRootPath) {
    try {
      const response = await axios.get("/user/property");
      setUser(response.data.data.user);
      if (isCurrentRootPath) {
        // 로그인 페이지일 때
        navigate("/main"); // main 페이지로 이동
      }
    } catch (err) {
      console.log(err);
			alert("로그인 정보가 만료되었습니다"); // 토큰이 만료되면 alert창 띄워주고
      navigate("/"); // 로그인 페이지로 넘어감
      localStorage.removeItem("token"); // token를 localstorage에서 지워줌
      removeHeader(); // axios header에서 지워줌
    }
  }
profile
개발공부일기

0개의 댓글