재우니의 블로그

ReactJS : REST API 를 통해 로그인 LOGIN 및 프로파일 PROFILE 만들어보기

 

이는 react 를 처음 접근하거나 처음 배우고자 하시는 분들에게 도움이 되는 내용입니다.  여기서는 로그인 화면과 프로파일 화면을 만들 예정이며, api 는 MeCallAPI.com 사이트를 통해 데이터를 가지고 적용할 것입니다.

 

 

 

API

 

무료  login api 를 MeCallAPI.com 사이트를 통해 사용할 예정입니다.

{
    "username": "karn.yong@mecallapi.com",
    "password": "mecallapi"
}

 

  • Response:
{
    "status": "ok",
    "message": "Logged in",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
        "id": 1,
        "fname": "Karn",
        "lname": "Yong",
        "username": "karn.yong@mecallapi.com",
        "email": "karn.yong@mecallapi.com",
        "avatar": "https://www.mecallapi.com/users/1.png"
    }
}

 

 

api 중에 fake 데이터를 제공해 주는 MeCallAPI.com 사이트가 존재합니다. 여기는 jwt 를 통해 사용자 프로파일 정보 및 사용자 리스트 등을 제공해 줍니다.

 

Me Call API - Free REST-API, Ready to be Called.

Copyright © Karn Yongsiriwit 2021

www.mecallapi.com

 

 

 

Software 설치

npm install --global yarn

 

 

React App 만들기

create-react-app 을 실행하여 react application 을 생성합니다.

npx create-react-app react-basic-login

생성한 폴더 내부로 접근합니다.

cd react-basic-login

 

 

packages 추가하기

아래의 패키지를 추가합니다.

  • Material UI
  • react-router-dom
  • Sweetalert
yarn add @material-ui/core @material-ui/icons react-router-dom sweetalert

 

 

Sign-in 과 Profile 화면 생성하기

2개의 화면을 만들 예정입니다.

  • Sigin.js : login API 를 사용한 로그인 페이지
  • Profile.js : 사용자 프로필 화면(해당 화면은 인증 후 볼 수 있음)

 

Sigin.js 생성하기

 

import React, { useState } from 'react';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import swal from 'sweetalert';

const useStyles = makeStyles((theme) => ({
  root: {
    height: '100vh',
  },
  image: {
    backgroundImage: 'url(https://source.unsplash.com/random)',
    backgroundSize: 'cover',
  },
  paper: {
    margin: theme.spacing(8, 4),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%',
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));

async function loginUser(credentials) {
  return fetch('https://www.mecallapi.com/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(credentials)
  })
    .then(data => data.json())
 }

export default function Signin() {
  const classes = useStyles();
  const [username, setUserName] = useState();
  const [password, setPassword] = useState();

  const handleSubmit = async e => {
    e.preventDefault();
    const response = await loginUser({
      username,
      password
    });
    if ('accessToken' in response) {
      swal("Success", response.message, "success", {
        buttons: false,
        timer: 2000,
      })
      .then((value) => {
        localStorage.setItem('accessToken', response['accessToken']);
        localStorage.setItem('user', JSON.stringify(response['user']));
        window.location.href = "/profile";
      });
    } else {
      swal("Failed", response.message, "error");
    }
  }

  return (
    <Grid container className={classes.root}>
      <CssBaseline />
      <Grid item xs={false} md={7} className={classes.image} />
      <Grid item xs={12} md={5} component={Paper} elevation={6} square>
        <div className={classes.paper}>
          <Avatar className={classes.avatar}>
            <LockOutlinedIcon />
          </Avatar>
          <Typography component="h1" variant="h5">
            Sign in
          </Typography>
          <form className={classes.form} noValidate onSubmit={handleSubmit}>
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              name="email"
              label="Email Address"
              onChange={e => setUserName(e.target.value)}
            />
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="password"
              name="password"
              label="Password"
              type="password"
              onChange={e => setPassword(e.target.value)}
            />
            <Button
              type="submit"
              fullWidth
              variant="contained"
              color="primary"
              className={classes.submit}
            >
              Sign In
            </Button>
          </form>
        </div>
      </Grid>
    </Grid>
  );
}

 

 

 

Profile.js 구현하기

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import Avatar from '@material-ui/core/Avatar';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
  },
  menuButton: {
    marginRight: theme.spacing(2),
  },
  title: {
    flexGrow: 1,
  },
  large: {
    width: theme.spacing(20),
    height: theme.spacing(20),
  },
}));

export default function Profile() {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);
  const open = Boolean(anchorEl);
  const user = JSON.parse(localStorage.getItem('user'));

  const handleMenu = (event) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleLogout = () => {
    localStorage.removeItem("accessToken");
    localStorage.removeItem("user");
    window.location.href = "/";
  };

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <Typography variant="h6" className={classes.title}>
            Profile
          </Typography>
            <div>
            <IconButton onClick={handleMenu} color="inherit">
              <Avatar src={user.avatar} />
            </IconButton>
            <Menu id="menu-appbar" 
              anchorEl={anchorEl} 
              open={open}
              onClose={handleClose}
            >
              <MenuItem onClick={handleLogout}>Logout</MenuItem>
            </Menu>
          </div>
        </Toolbar>
      </AppBar>
      <Card className={classes.root} variant="outlined">
        <CardContent>
          <Avatar src={user.avatar} className={classes.large} />
          <Typography variant="h5">
          Welcome {user.fname} {user.lname}
          </Typography>
        </CardContent>
      </Card>
    </div>
  );
}

 

 

이제, App.js 를 수정할 차례인데요. Sign-in 과 Profile 의 화면 간의 routing 라우팅을 기술합니다. 만약에 login  api 를 호출해서 token 값이 없으면 Sign-in 화면으로 이동하게 됩니다.

 

import React from 'react';
import './App.css';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Signin from './Signin';
import Profile from './Profile';

function App() {
  const token = localStorage.getItem('accessToken');

  if(!token) {
    return <Signin />
  }

  return (
    <div className="wrapper">
      <BrowserRouter>
        <Switch>
          <Route path="/profile">
            <Profile />
          </Route>
          <Route path="/">
            <Profile />
          </Route>
        </Switch>
      </BrowserRouter>
    </div>
  );
}

export default App;

 

 

실행

React 실행하기:

yarn start

 

 

아래 사용자 계정인 Email Address 과 Password 입니다.

  • karn.yong@mecallapi.com / mecallapi
  • ivy.cal@mecallapi.com / mecallapi

 

 

참고 사이트

 

https://javascript.plainenglish.io/basic-react-login-using-external-api-e33322e480cd

 

Basic React Login using External API

This article is suitable for beginners. We will create sign-in and User Profile screens. Moreover, we will incorporate an API from…

javascript.plainenglish.io