Commit 00e7f876 authored by 우지원's avatar 우지원
Browse files

01_06

parent f6928d0a
import { Button, Navbar, Nav, Form, FormControl } from 'react-bootstrap'; import { Button, Navbar, Nav } from 'react-bootstrap';
import React from 'react' import React from 'react'
const userName = "정연우"; const userName = "정연우";
......
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Button, Form, Container, Navbar } from 'react-bootstrap'; import axios from 'axios'
import { Link } from 'react-router-dom'; import { Button, Form, Container, Navbar, Spinner, Alert } from 'react-bootstrap';
import catchErrors from '../utils/catchErrors'
import { Link, Redirect } from 'react-router-dom'
import { handleLogin } from '../utils/auth'
const INIT_USER = {
email: '',
password: '',
}
function LogIn() { function LogIn() {
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const [user, setUser] = useState(INIT_USER)
//로딩, 에러, diserved 상태 넣어야됨.
const [disabled, setDisabled] = useState(true)
const [error, setError] = useState('')
const [success, setSucces] = useState(false)
const [loading, setLoading] = useState(false)
useEffect(() => {
const isUser = Object.values(user).every(el => Boolean(el))
//Boolean : 참거짓 판별
//every : every뒤에 함수값이 return하는 값이 모두 참일때만 true출력 -> element가 하나도 빈 문자열이 존재하지 않을때
//empty string때만 false로 나옴.
isUser ? setDisabled(false) : setDisabled(true)
}, [user])
function handleChange(event) {
const { name, value } = event.target
// console.log(name, value)
setUser({ ...user, [name]: value })
}
async function handleSubmit(event) {
event.preventDefault()
const handleSubmit = (event) => {
const form = event.currentTarget; const form = event.currentTarget;
if (form.checkValidity() === false) { if (form.checkValidity() === false) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }
setValidated(true); setValidated(true);
};
try {
setLoading(true)
setError('')
await axios.post('/auth/login', user)
// 알아서 stringify하기 때문에 따로 해줄 필요 없음.
handleLogin()
setSucces(true)
} catch (error) {
catchErrors(error, setError)
//setError(error.response.data)
//error객체가 들어감.
} finally {
setLoading(false)
}
//server쪽에서 json형식으로 보낼것임
}
//success시 링크이동
if (success) {
console.log('success', success)
return <Redirect to='/' />
}
return ( return (
<> <>
<Navbar bg="dark" variant="dark"> {/* <Navbar bg="dark" variant="dark">
<Navbar.Brand>YDK Messenger</Navbar.Brand> <Navbar.Brand>YDK Messenger</Navbar.Brand>
</Navbar> </Navbar> */}
<Form noValidate validated={validated} onSubmit={handleSubmit}> <Form noValidate validated={validated} onSubmit={handleSubmit} className='vh-100 flex-column align-items-center justify-content-center mt-2'>
<Container className="d-flex justify-content-center"> <Container className="d-flex justify-content-center">
<div className="mt-5 p-5 shadow w-75"> <div className="mt-5 p-5 shadow w-75">
...@@ -32,6 +83,9 @@ function LogIn() { ...@@ -32,6 +83,9 @@ function LogIn() {
<Form.Control <Form.Control
required required
type="email" type="email"
name="email"
onChange={handleChange}
value={user.email}
placeholder="이메일을 입력해주세요" /> placeholder="이메일을 입력해주세요" />
<Form.Control.Feedback type="invalid"> <Form.Control.Feedback type="invalid">
필수 정보입니다! 이메일을 입력해주세요! 필수 정보입니다! 이메일을 입력해주세요!
...@@ -43,19 +97,31 @@ function LogIn() { ...@@ -43,19 +97,31 @@ function LogIn() {
<Form.Control <Form.Control
required required
type="password" type="password"
name="password"
onChange={handleChange}
value={user.password}
placeholder="비밀번호를 입력해주세요" /> placeholder="비밀번호를 입력해주세요" />
<Form.Control.Feedback type="invalid"> <Form.Control.Feedback type="invalid">
필수 정보입니다! 비밀번호를 입력해주세요! 필수 정보입니다! 비밀번호를 입력해주세요!
</Form.Control.Feedback> </Form.Control.Feedback>
</Form.Group> </Form.Group>
<Link to="./home"> <Button
<Button type="submit" variant="outline-success" size="lg" className="mr-4" block>로그인</Button> disabled={disabled || loading}
</Link> type="submit"
variant="outline-success"
size="lg"
className="mr-4"
block>
{loading && <Spinner as='span' animation='border' size='sm' role='status' aria-hidden='true' />} {' '} 로그인
</Button>
<Link to="./signup"> <Link to="./signup">
<h6 type="button" className="text-right mt-2" style={{ cursor: 'pointer' }}>회원가입</h6> <h6 type="button" className="text-right mt-2" style={{ cursor: 'pointer' }}>회원가입</h6>
</Link> </Link>
{error && <Alert variant='danger'>
{error}
</Alert>}
</div> </div>
</Container> </Container>
</Form> </Form>
......
import React, { useState } from 'react'; import React, { useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Menu from '../Components/Menu'; import Menu from '../Components/Menu';
import { Image, Button, Container, Form, FormControl, Navbar, Nav } from 'react-bootstrap'; import { Image, Button, Container, Form } from 'react-bootstrap';
import { BrowserRouter as Router, Route, Redirect, Switch, Link } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import userdefault from './KakaoTalk_20201230_153151693.png' import userdefault from './KakaoTalk_20201230_153151693.png'
......
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import axios from 'axios'
import { Button, Form, Container, Navbar, Alert } from 'react-bootstrap'; import { Button, Form, Container, Navbar, Alert } from 'react-bootstrap';
import { Link } from 'react-router-dom'; import catchErrors from '../utils/catchErrors';
//import { Redirect } from 'react-router-dom';
const INIT_USER = { const INIT_USER = {
username: '', username: '',
...@@ -32,6 +34,7 @@ function SingUp() { ...@@ -32,6 +34,7 @@ function SingUp() {
async function handleSubmit(event) { async function handleSubmit(event) {
event.preventDefault(); event.preventDefault();
//빈문자열 입력 시 오류 문자 출력
const form = event.currentTarget; const form = event.currentTarget;
if (form.checkValidity() === false) { if (form.checkValidity() === false) {
event.preventDefault(); event.preventDefault();
...@@ -42,31 +45,40 @@ function SingUp() { ...@@ -42,31 +45,40 @@ function SingUp() {
try { try {
setError('') setError('')
const response = await fetch('/api/users/signup', { // const response = await fetch('/api/users/signup', {
//post, get같은게 주어지지 않으면 기본적으로 fetch에 get요청함 // //post, get같은게 주어지지 않으면 기본적으로 fetch에 get요청함
method: 'POST', // method: 'POST',
headers: { // headers: {
'Content-Type': 'application/json' // 'Content-Type': 'application/json'
}, // },
body: JSON.stringify(user) // body: JSON.stringify(user)
}) // })
const data = await response.json() // const data = await response.json()
console.log(data)
await axios.post('/users/signup', user)
// 알아서 stringify하기 때문에 따로 해줄 필요 없음.
// console.log(response.data)
console.log(user)
// setUser(INIT_USER) // setUser(INIT_USER)
} catch (error) { } catch (error) {
console.log(error) catchErrors(error, setError)
setError('오류가 발생했습니다!')
} }
} }
// if (success) {
// console.log('success', success)
// return <Redirect to='/login'/>
// }
return ( return (
<> <>
<Navbar bg="dark" variant="dark"> {/* <Navbar bg="dark" variant="dark">
<Navbar.Brand>YDK Messenger</Navbar.Brand> <Navbar.Brand>YDK Messenger</Navbar.Brand>
</Navbar> </Navbar> */}
<div> <Form noValidate validated={validated} onSubmit={handleSubmit} className='vh-100 flex-column align-items-center justify-content-center mt-2'>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Container className="d-flex justify-content-center"> <Container className="d-flex justify-content-center">
<div className="mt-5 p-5 shadow w-75"> <div className="mt-5 p-5 shadow w-75">
...@@ -128,7 +140,6 @@ function SingUp() { ...@@ -128,7 +140,6 @@ function SingUp() {
</Form.Control.Feedback> </Form.Control.Feedback>
</Form.Group> </Form.Group>
{/* <Link to="./login"> */}
<Button <Button
disabled={disabled} disabled={disabled}
type='submit' type='submit'
...@@ -136,14 +147,12 @@ function SingUp() { ...@@ -136,14 +147,12 @@ function SingUp() {
size="lg" size="lg"
className="mr-4" className="mr-4"
block>가입</Button> block>가입</Button>
{/* </Link> */}
{error && <Alert variant='danger'> {error && <Alert variant='danger'>
{error} {error}
</Alert>} </Alert>}
</div> </div>
</Container> </Container>
</Form> </Form>
</div >
</> </>
) )
} }
......
import axios from "axios"
//자동으로 localstorage에 login이 생성됨
export function handleLogin() {
localStorage.setItem('loginStatus', 'true')
}
export async function handleLogout() {
localStorage.removeItem('loginStatus')
await axios.get('/api/auth/logout')
}
\ No newline at end of file
function catchErrors(error, displayError) {
let errorMsg
if (error.response) {
errorMsg = error.response.data
console.log(errorMsg)
} else if (error.request) {
errorMsg = error.request
console.log(errorMsg)
} else {
errorMsg = error.message
console.log(errorMsg)
}
displayError(errorMsg)
}
export default catchErrors
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev" : "nodemon server/server.js" "dev": "nodemon server/server.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^0.21.1",
"bcryptjs": "^2.4.3",
"express": "^4.17.1", "express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.11.9", "mongoose": "^5.11.9",
"nodemon": "^2.0.6", "nodemon": "^2.0.6",
"validator": "^13.5.2" "validator": "^13.5.2"
......
...@@ -2,7 +2,9 @@ const config = { ...@@ -2,7 +2,9 @@ const config = {
env: process.env.NODE_ENV || 'development', env: process.env.NODE_ENV || 'development',
port: process.env.PORT || 3030, port: process.env.PORT || 3030,
jwtSecret: process.env.JWT_SECRET || 'My_Secret_Key', jwtSecret: process.env.JWT_SECRET || 'My_Secret_Key',
mongoDbUri: process.env.MONGODB_URI || 'mongodb://localhost/messenger' mongoDbUri: process.env.MONGODB_URI || 'mongodb://localhost/messenger',
cookieMaxAge: 60 * 60 * 24 * 7 * 1000
//1000이 1초 이므로 7일
} }
export default config export default config
\ No newline at end of file
import User from "../models/User.js"
import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
import config from "../config.js"
//꼭 js붙여주기!!
//isEmail
//sign validation해야됨
const login = async (req, res) => {
const { email, password } = req.body
//req.body를 구조분해하여 각각 보이게함 -> 모든정보들이 한줄에 보임
console.log(email, password)
try {
// 1) 사용자 확인
const user = await User.findOne({ email }).select('+password')
// 2) 이메일 사용자가 없으면 에러 반환
if (!user) {
return res.status(404).send(`${email}이 없습니다`)
}
// 3) 비밀번호 일치 확인
const passwordMatch = await bcrypt.compare(password, user.password)
// 4) 비밀번호가 맞으면 토큰 생성 후 쿠키에 저장
if (passwordMatch) {
//토큰 생성
const token = jwt.sign({ userId: user._id }, config.jwtSecret, {
//jwtSecret : 노출되면 안됨. 문자열
expiresIn: '7d'
//만기날짜 : 만든 7일후 만기
})
//쿠키에 저장
//res : client로 넘어가는 객체 cookie('이름', value)
res.cookie('token', token, {
maxAge: config.cookieMaxAge,
//생성일로부터 어느정도까지 살아있을 것인가
httpOnly: true,
//client에서 javascript로 접근할 수 없음
secure: config.env === 'production'
//secure가 true이면 http로 접근하면 cookie가 들어가지 않음.
})
res.send('Login Successful')
} else {
// 5) 비밀번호가 틀리면 에러 반환
res.status(401).send('비밀번호가 일치하지 않습니다')
}
} catch (error) {
//알수없는 모든 에러발생시 처리
console.log(error)
res.status(500).send('로그인 에러')
}
}
const logout = (req, res) => {
res.clearCookie('token')
res.send('Logout Successful')
}
export default { login, logout }
// {} : 객체로 return함
import User from "../models/User.js" import User from "../models/User.js"
import isLength from 'validator/lib/isLength.js' import isLength from 'validator/lib/isLength.js'
//import isEmail from 'validator/lib/isEmail.js' import isEmail from 'validator/lib/isEmail.js'
import bcrypt from 'bcryptjs'
//꼭 js붙여주기!! //꼭 js붙여주기!!
//sign validation해야됨 //sign validation해야됨
...@@ -11,23 +12,38 @@ const signup = async (req, res) => { ...@@ -11,23 +12,38 @@ const signup = async (req, res) => {
console.log(username, nickname, email, password) console.log(username, nickname, email, password)
try { try {
if (!isLength(username, { min: 3, max: 10 })) { if (!isLength(username, {min: 3, max: 10})){
//이범위를 벗어나면 error발생 //이범위를 벗어나면 error발생
return res.status(422).json({message: 'Name must be 3-10 characters'}) return res.status(422).send('이름은 3-10자 사이입니다')
//422 : 형식이 잘못되었다는 error발생 //422 : 형식이 잘못되었다는 error발생
} else if (!isLength(nickname, {min: 2, max: 10})) {
return res.status(422).send('별명은 2-10자 사이입니다.')
} else if (!isLength(password, {min: 6})) {
return res.status(422).send('비밀번호는 6자 이상입니다.')
} else if (!isEmail(email)) {
return res.status(422).send('유효하지 않은 이메일 형식입니다')
} }
else if (!isLength(nickname, { min: 3, max: 10 })) { // else if (!isLength(nickname, { min: 3, max: 10 })) {
return res.status(422).send('Nickname must be 3-10 characters') // return res.status(422).send('Nickname must be 3-10 characters')
} else if (!isEmail(email, { // } else if (!isEmail(email, {
allow_display_name: true, // allow_display_name: true,
require_display_name: true, // require_display_name: true,
allow_utf8_local_part: false, // allow_utf8_local_part: false,
})) { // })) {
return res.status(422).send('Email does not fit the format') // return res.status(422).send('Email does not fit the format')
} else if (!isLength(password, { min: 6, max: 25 })) { // } else if (!isLength(password, { min: 6, max: 25 })) {
return res.status(422).send('Nickname must be 6-25 characters') // return res.status(422).send('Nickname must be 6-25 characters')
// }
// 기존의 email이 있으면 나오는 error (unique)
const user = await User.findOne({email})
if(user) {
return res.status(422).send(`${email}이 이미 사용중입니다.`)
} }
const hash = await bcrypt.hash(password, 10)
//promise이므로 await사용함
const newUser = await new User({ const newUser = await new User({
username, username,
nickname, nickname,
......
import express from 'express'
import authCtrl from '../controllers/auth.controller.js'
const router = express.Router()
//router의 역할 : './주소'부분을 처리하는 역할함.
router.route('/auth/login')
.post(authCtrl.login)
router.route('/auth/logout')
.get(authCtrl.logout)
// /api/users/signup로 들어오는 것을 post (method) 를 통해 useCtrl.signup 이것이 처리함
//browser에서 주소창에 치고 들어가면 get (method) 을 타고 들어간것임
//post를 띄우고 싶으면 앱에서 ARC실행해서 post를 실행하게 만들면됨.
//객체에 접근할때는 .을 찍고 접근함/ ex) .hello
//express middleware : (req, res) => {}
//node(req(client의 정보), res)를 넘겨줌.
export default router
\ No newline at end of file
...@@ -4,7 +4,7 @@ import userCtrl from '../controllers/user.controller.js' ...@@ -4,7 +4,7 @@ import userCtrl from '../controllers/user.controller.js'
const router = express.Router() const router = express.Router()
//router의 역할 : './주소'부분을 처리하는 역할함. //router의 역할 : './주소'부분을 처리하는 역할함.
router.route('/api/users/signup') router.route('/users/signup')
.post(userCtrl.signup) .post(userCtrl.signup)
.get(userCtrl.hello) .get(userCtrl.hello)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment