박민정

[docs] Change the document structure

node_modules
dev.js
\ No newline at end of file
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
This diff could not be displayed because it is too large.
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
No preview for this file type
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import LandingPage from './components/views/LandingPage/LandingPage'
import LoginPage from './components/views/LoginPage/LoginPage'
import RegisterPage from './components/views/RegisterPage/RegisterPage'
function App() {
return (
<Router>
<div>
{/*
A <Switch> looks through all its children <Route>
elements and renders the first one whose path
matches the current URL. Use a <Switch> any time
you have multiple routes, but you want only one
of them to render at a time
*/}
<Switch>
<Route exact path="/">
<LandingPage />
</Route> {/*
<Route exact path="/" component={LandingPage} /> 로 해도 똑같은 결과가 나옴!
*/}
<Route path="/login">
<LoginPage />
</Route>
<Route path="/register">
<RegisterPage />
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
import React from 'react'
function Footer() {
return (
<div>
Footer
</div>
)
}
export default Footer
Footer
\ No newline at end of file
import React, {useEffect} from 'react'
import axios from 'axios'
function LandingPage() {
// 랜딩페이지에 들어오자마자
useEffect(() => {
axios.get('http://localhost:5000/api/hello') // get request를 서버로 보냄 (endpoint는 /api/hello)
.then(response => console.log(response.data)) // 서버로부터 응답 받은 내용을 콘솔에 출력
}, [])
return (
<div>
LandingPage 랜딩페이지
</div>
)
}
export default LandingPage
import React from 'react'
function LoginPage() {
return (
<div>
LoginPage
</div>
)
}
export default LoginPage
import React from 'react'
function NavBar() {
return (
<div>
NavBar
</div>
)
}
export default NavBar
import React from 'react'
function RegisterPage() {
return (
<div>
RegisterPage
</div>
)
}
export default RegisterPage
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
// development모드이면 process.env.NODE_ENV는 development라고 나오고
// production모드이면 production이라고 나옴
if(process.env.NODE_ENV === 'production') // === 는 형변환을 하지 않음.
// ex) true == 1 -> return true
// true === 1 -> return false
{
module.exports = require('./prod')
}
else
{
module.exports = require('./dev')
}
\ No newline at end of file
module.exports ={
mongoURI: process.env.mongo_URI
}
\ No newline at end of file
const express = require('express')
const app = express()
const port = 5000
// User.js에서 만든 model을 가져옴
const { User } = require('./models/User')
//const { User } = require('./')
// body-parser 가져옴
const bodyParser = require('body-parser')
// bodyParser option
app.use(bodyParser.urlencoded({extended: true})) //application/x-www-form-urlencoded로 된 데이터를 분석해서 가져옴
app.use(bodyParser.json()) // application/json 타입으로 된 데이터를 분석해서 가져옴
const config = require('./config/key')
const cookieParser = require('cookie-parser')
app.use(cookieParser())
const mongoose = require('mongoose')
const { auth } = require('./middleware/auth')
//이 정보는 비밀임..! 몽고DB아이디랑 비밀번호를 감춰야해..!
mongoose.connect(config.mongoURI, {
useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, useFindAndModify: false
}).then(()=>console.log('MongoDB Connected...'))
.catch(err => console.log(err))
app.get('/', (req, res) => {
res.send('민정이짱짱맨최고최고!')
})
// 회원가입 구현
// route의 endpoint는 register
app.post('/api/users/register', (req, res) => {
// 회원가입할 때 필요한 정보들을 client에서 가져오면
// 그것들을 데이터베이스에 넣어준다.
const user = new User(req.body) // req.body에 User의 정보를 저장
// 비밀번호 암호화
// mongoDB에서 오는 메서드. 정보들이 user model에 저장
user.save((err, userInfo) => {
// 만약 에러가 나면, json형식으로 success:false를 보내주고, 에러메시지를 보내줌
if(err) return res.json({success: false, err})
// 성공하면 status(200) (status(200)은 성공했다는 뜻)
return res.status(200).json({
success: true
})
})
})
// 로그인 구현 -> 로그인 하면 토큰 생성
app.post('/api/users/login', (req, res) => {
// 1. 요청된 이메일이 데이터베이스에 있는지 찾기
User.findOne({ email: req.body.email }, (err, user) => {
if(!user)
{
return res.json({
loginSuccess: false,
message: "There is no user with that email."
})
}
// 2. email과 비밀번호가 맞는지 확인 (User.js에 comparePassword 함수 정의되어 있음)
user.comparePassword(req.body.password, (err, isMatch) => {
if(!isMatch)
return res.json({loginSuccess: false, message: "Password is not match."})
// 3. 비밀번호까지 맞다면 유저를 위한 토큰 생성 (User.js에 generateToken 함수 정의)
user.generateToken((err, user) => { // err가 없으면 user에 정보 받아옴
if(err)
return res.status(400).send(err);
// 4. 생성한 토큰을 저장함 -> 쿠키나 로컬 스토리지 등에 저장할 수 있는데 여기선 쿠키에 저장
res.cookie("loginCookie", user.token)
.status(200) //성공했다는 표시
.json({loginSuccess: true, userId: user._id})
})
})
})
})
// 인증 구현 (이 사람이 일반유저인지 관리자인지)
app.get('/api/users/auth', auth ,(req,res) => {
// 여기까지 미들웨어(auth) 통과했으면 authentication == true 라는 뜻
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true, // role이 0이면 일반 유저, role이 0이 아니면 관리자.
isAuth: true,
email: req.user.email,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image
})
})
// 로그아웃 구현 (로그인 때 만든 토큰을 지워버림)
app.get('/api/users/logout', auth, (req, res) => {
User.findOneAndUpdate({_id: req.user._id}, // id로 User를 찾아서 업데이터 시킴
{ token: "" }, (err, user) => {
if(err) return res.json({success: false, err});
return res.status(200).send({
success: true
})
})
})
// test
app.get('/api/hello', (req, res) => {
res.send("hello");
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
\ No newline at end of file
const { User } = require("../models/User");
let auth = (req, res, next) => {
// 인증 처리
// 1. client 쿠키에서 토큰을 가져옴.
let token = req.cookies.loginCookie;
// 2. 토큰을 복호화한 후 유저를 찾는다. (User.js에 findByToken(); 있음)
User.findByToken(token, (err, user)=>{
// 에러가 있으면
if(err) throw err;
// 유저가 없으면
if(!user) return res.json({ isAuth:false, error: true})
// 에러도 없고 유저도 있으면
req.token = token; // token과 user를 request에 넣어줌으로써 index.js에서 request 사용할 수 있음
req.user = user;
next();
});
// 3. 유저가 있으면 인증OK, 유저가 없으면 인증No!
}
// 이 auth를 다른 파일에서도 쓸 수 있도록
module.exports = { auth };
\ No newline at end of file
// monggoDB Model and Schema
const mongoose = require('mongoose');
// bcrypt 가져옴
const bcrypt = require('bcrypt')
// bcrypt 사용하기 위해 salt를 생성하고 그걸 이용해 암호화 시킴
const saltRounds = 10 // salt를 몇글자 할 건지
//
const jwt = require('jsonwebtoken')
const userSchema = mongoose.Schema({
name:{
type: String,
maxlength: 50
},
email:{
type: String,
trim: true, // 'minjeong park'처럼 space가 있는 문자에 space가 없어지도록 함
unique: 1 // 똑같은 이메일은 쓸 수 없도록
},
password: {
type: String,
minlength: 5
},
lastname: {
type: String,
maxlength: 50
},
role: { // Number==1 이면 관리자, number==0 이면 일반 유저
type: Number,
default: 0 // default는 0
},
token : {
type : String
},
tokenExp: { //토큰의 유효기간
type: Number
}
})
// index.js의 app.post('/register', (req, res)에 있는
// user model에 user 정보를 저장하기 전에 무엇을 한다는 것
// function( next )를 해서 얘네가 끝난 다음에 다음걸 실행해라~
userSchema.pre('save', function( next ){
var user = this
if(user.isModified('password')) // password를 변경할 때만 적용되도록..
{
// 비밀번호 암호화 (https://www.npmjs.com/package/bcrypt 에서 가져옴)
bcrypt.genSalt(saltRounds, (err, salt) => // salt를 만드는 함수
{
if(err) return next(err) // 에러 나면 return err
bcrypt.hash(user.password, salt, (err, hash) => { // bcrypt.hash(암호화되지 않은 pw, salt, function(err, 암호화된 비밀번호))
if(err) return next(err) // 에러 나면 return err
user.password = hash // 성공하면 user.password를 hash로 교체
next()
});
});
}
else
{
next()
}
})
userSchema.methods.comparePassword = function(plainPassword, cb){
// 1. plainPassword가 1234567 암호화된 비밀번호 가 같은지 체크해야함
// 그러면 plainPassword도 암호화해서 비교해야함. (복호화 할 수 없기 때문에)
bcrypt.compare(plainPassword, this.password, function(err, isMatch)
{ // 에러가 나면 err callback, 아니면 isMatch
if(err) return cb(err);
cb(null, isMatch);
})
}
userSchema.methods.generateToken = function(cb)
{
var user = this;
// jsonwebtoken을 이용해서 token 생성
var token = jwt.sign(user._id.toHexString(), 'secretToken') //database에 있는 id라서 _id
user.token = token
user.save(function(err, user){
if(err)
return cb(err) // 에러가 있다면 callback으로 에러 전달
cb(null, user) // 에러가 없다면 err는 없고 user정보만 전달
})
}
userSchema.statics.findByToken = function(token, cb)
{
var user = this;
// 1. 토큰을 decoding
jwt.verify(token, 'secretToken', function(err, decoded) {
// 2. 유저 아이디를 이용해서 유저를 찾은 다음에 클라이언트에서 가져온 토큰과 DB에 보관된 토큰이 일치하는지 확인.
user.findOne({"_id": decoded, "token": token}, function(err, user){ // findOne :: mongoDB에 이미 있는 method
// 에러가 나면
if(err) return cb(err);
// 에러가 안나면
cb(null, user)
})
})
}
// 만든 스키마를 모델로 감싸줌
const User = mongoose.model('User', userSchema)
// 이 모델을 다른 파일에서도 쓰고싶으면 아래와 같이 해주면 됨
module.exports = {User}
\ No newline at end of file
This diff is collapsed. Click to expand it.
{
"name": "boiler-plate",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"backend": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "mindyeoi",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.0.1",
"body-parser": "^1.19.0",
"cookie-parser": "^1.4.5",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.12.12"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}