ReactJS : REST API 를 통해 로그인 LOGIN 및 프로파일 PROFILE 만들어보기
이는 react 를 처음 접근하거나 처음 배우고자 하시는 분들에게 도움이 되는 내용입니다. 여기서는 로그인 화면과 프로파일 화면을 만들 예정이며, api 는 MeCallAPI.com 사이트를 통해 데이터를 가지고 적용할 것입니다.
API
무료 login api 를 MeCallAPI.com 사이트를 통해 사용할 예정입니다.
{
"username": "karn.yong@mecallapi.com",
"password": "mecallapi"
}
{
"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 를 통해 사용자 프로파일 정보 및 사용자 리스트 등을 제공해 줍니다.
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