박민정

Merge branch 'client' into develop

...@@ -2312,6 +2312,11 @@ ...@@ -2312,6 +2312,11 @@
2312 "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 2312 "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
2313 "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" 2313 "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
2314 }, 2314 },
2315 + "attr-accept": {
2316 + "version": "2.2.2",
2317 + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
2318 + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
2319 + },
2315 "autoprefixer": { 2320 "autoprefixer": {
2316 "version": "9.7.5", 2321 "version": "9.7.5",
2317 "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.5.tgz", 2322 "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.5.tgz",
...@@ -5530,6 +5535,21 @@ ...@@ -5530,6 +5535,21 @@
5530 "schema-utils": "^2.5.0" 5535 "schema-utils": "^2.5.0"
5531 } 5536 }
5532 }, 5537 },
5538 + "file-selector": {
5539 + "version": "0.2.4",
5540 + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
5541 + "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
5542 + "requires": {
5543 + "tslib": "^2.0.3"
5544 + },
5545 + "dependencies": {
5546 + "tslib": {
5547 + "version": "2.2.0",
5548 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
5549 + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
5550 + }
5551 + }
5552 + },
5533 "file-uri-to-path": { 5553 "file-uri-to-path": {
5534 "version": "1.0.0", 5554 "version": "1.0.0",
5535 "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 5555 "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
...@@ -11692,6 +11712,16 @@ ...@@ -11692,6 +11712,16 @@
11692 "scheduler": "^0.19.1" 11712 "scheduler": "^0.19.1"
11693 } 11713 }
11694 }, 11714 },
11715 + "react-dropzone": {
11716 + "version": "11.3.2",
11717 + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.3.2.tgz",
11718 + "integrity": "sha512-Z0l/YHcrNK1r85o6RT77Z5XgTARmlZZGfEKBl3tqTXL9fZNQDuIdRx/J0QjvR60X+yYu26dnHeaG2pWU+1HHvw==",
11719 + "requires": {
11720 + "attr-accept": "^2.2.1",
11721 + "file-selector": "^0.2.2",
11722 + "prop-types": "^15.7.2"
11723 + }
11724 + },
11695 "react-error-overlay": { 11725 "react-error-overlay": {
11696 "version": "6.0.7", 11726 "version": "6.0.7",
11697 "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", 11727 "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
5 "dependencies": { 5 "dependencies": {
6 "antd": "^3.24.1", 6 "antd": "^3.24.1",
7 "axios": "^0.19.2", 7 "axios": "^0.19.2",
8 + "core-js": "^3.6.4",
8 "formik": "^1.5.8", 9 "formik": "^1.5.8",
9 "moment": "^2.24.0", 10 "moment": "^2.24.0",
10 "react": "^16.8.6", 11 "react": "^16.8.6",
12 + "react-app-polyfill": "^1.0.6",
11 "react-dom": "^16.8.6", 13 "react-dom": "^16.8.6",
14 + "react-dropzone": "^11.3.2",
12 "react-icons": "^3.7.0", 15 "react-icons": "^3.7.0",
13 "react-redux": "^7.1.0-rc.1", 16 "react-redux": "^7.1.0-rc.1",
14 "react-router-dom": "^5.0.1", 17 "react-router-dom": "^5.0.1",
...@@ -16,9 +19,7 @@ ...@@ -16,9 +19,7 @@
16 "redux": "^4.0.0", 19 "redux": "^4.0.0",
17 "redux-promise": "^0.6.0", 20 "redux-promise": "^0.6.0",
18 "redux-thunk": "^2.3.0", 21 "redux-thunk": "^2.3.0",
19 - "yup": "^0.27.0", 22 + "yup": "^0.27.0"
20 - "core-js": "^3.6.4",
21 - "react-app-polyfill": "^1.0.6"
22 }, 23 },
23 "scripts": { 24 "scripts": {
24 "start": "react-scripts start", 25 "start": "react-scripts start",
......
1 -# https://www.robotstxt.org/robotstxt.html
2 -User-agent: *
3 -Disallow:
1 -.App {
2 - text-align: center;
3 -}
4 -
5 -.App-logo {
6 - height: 40vmin;
7 - pointer-events: none;
8 -}
9 -
10 -@media (prefers-reduced-motion: no-preference) {
11 - .App-logo {
12 - animation: App-logo-spin infinite 20s linear;
13 - }
14 -}
15 -
16 -.App-header {
17 - background-color: #282c34;
18 - min-height: 100vh;
19 - display: flex;
20 - flex-direction: column;
21 - align-items: center;
22 - justify-content: center;
23 - font-size: calc(10px + 2vmin);
24 - color: white;
25 -}
26 -
27 -.App-link {
28 - color: #61dafb;
29 -}
30 -
31 -@keyframes App-logo-spin {
32 - from {
33 - transform: rotate(0deg);
34 - }
35 - to {
36 - transform: rotate(360deg);
37 - }
38 -}
1 -import axios from 'axios';
2 -import {
3 - LOGIN_USER,
4 - REGISTER_USER,
5 - AUTH_USER
6 -} from './types';
7 -export function loginUser(logInfo) {
8 - const request = axios.post('/api/users/login', logInfo) // logInfo를 post로 전달
9 - .then(response => response.data); // 서버에서 받은 데이터를 request에 저장
10 -
11 - return { // return을 통해 Reducer로 보냄
12 - // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
13 - // request를 reducer로 보내는 작업
14 -
15 - // action은 type과 response을 넣어줘야 함
16 - type: "LOGIN_USER",
17 - payload: request // payroad == response
18 - }
19 -}
20 -
21 -export function RegisterUser(regInfo) {
22 - const request = axios.post('/api/users/register', regInfo) // logInfo를 post로 전달
23 - .then(response => response.data); // 서버에서 받은 데이터를 request에 저장
24 -
25 - return { // return을 통해 Reducer로 보냄.
26 - // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
27 - // request를 reducer로 보내는 작업
28 -
29 - // action은 type과 response을 넣어줘야 함
30 - type: "REGISTER_USER",
31 - payload: request // payroad == response
32 - }
33 -}
34 -
35 -export function auth() {
36 - const request = axios.get('/api/users/auth') // logInfo를 post로 전달
37 - .then(response => response.data); // 서버에서 받은 데이터를 request에 저장
38 -
39 - return {
40 -
41 - type: "AUTH_USER",
42 - payload: request // payroad == response
43 - }
44 -}
...\ No newline at end of file ...\ No newline at end of file
...@@ -5,6 +5,7 @@ import Auth from "../hoc/auth"; ...@@ -5,6 +5,7 @@ import Auth from "../hoc/auth";
5 import LandingPage from "./views/LandingPage/LandingPage.js"; 5 import LandingPage from "./views/LandingPage/LandingPage.js";
6 import LoginPage from "./views/LoginPage/LoginPage.js"; 6 import LoginPage from "./views/LoginPage/LoginPage.js";
7 import RegisterPage from "./views/RegisterPage/RegisterPage.js"; 7 import RegisterPage from "./views/RegisterPage/RegisterPage.js";
8 +import UploadPage from './views/UploadPage/UploadPage';
8 import NavBar from "./views/NavBar/NavBar"; 9 import NavBar from "./views/NavBar/NavBar";
9 import Footer from "./views/Footer/Footer" 10 import Footer from "./views/Footer/Footer"
10 11
...@@ -21,6 +22,7 @@ function App() { ...@@ -21,6 +22,7 @@ function App() {
21 <Route exact path="/" component={Auth(LandingPage, null)} /> 22 <Route exact path="/" component={Auth(LandingPage, null)} />
22 <Route exact path="/login" component={Auth(LoginPage, false)} /> 23 <Route exact path="/login" component={Auth(LoginPage, false)} />
23 <Route exact path="/register" component={Auth(RegisterPage, false)} /> 24 <Route exact path="/register" component={Auth(RegisterPage, false)} />
25 + <Route exact path="/upload" component={Auth(UploadPage, true)} />
24 </Switch> 26 </Switch>
25 </div> 27 </div>
26 <Footer /> 28 <Footer />
......
1 +import React from 'react'
2 +import Dropzone from 'react-dropzone'
3 +import axios from 'axios';
4 +
5 +function ImageUpload() {
6 +
7 + const imageDropEvent = (files) => {
8 + let imageData = new FormData();
9 +
10 + const config = {
11 + header: {'content-type': 'multipart/image-data'}
12 + }
13 + imageData.append("file", files[0])
14 +
15 + // 이미지 전달
16 + axios.post('/api/product/image', imageData, config)
17 + .then(response => {
18 + if (response.data.success) {
19 + console.log(response.data)
20 + }
21 + else {
22 + alert('파일 저장을 실패했습니다.')
23 + }
24 + })
25 +
26 + }
27 + return (
28 + <div style={ {display:'flex', justifyContent:'space-between'}}>
29 + <Dropzone onDrop={imageDropEvent}>
30 + {({getRootProps, getInputProps}) => (
31 + <section>
32 + <div style={{
33 + width: 300, height: 200, border: '1px solid lightgray', borderRadius: '1em', display: 'flex',
34 + alignItems: 'center', textAlign: 'center', justifyContent: 'center'
35 + }}
36 + {...getRootProps()}>
37 + <input {...getInputProps()} />
38 + <p>이곳을 클릭하여 <br/> 상품 사진을 업로드 해주세요.</p>
39 + </div>
40 + </section>
41 + )}
42 +</Dropzone>
43 + </div>
44 + )
45 +}
46 +
47 +export default ImageUpload
...@@ -36,6 +36,9 @@ function RightMenu(props) { ...@@ -36,6 +36,9 @@ function RightMenu(props) {
36 } else { 36 } else {
37 return ( 37 return (
38 <Menu mode={props.mode}> 38 <Menu mode={props.mode}>
39 + <Menu.Item key="upload">
40 + <a href="/upload">업로드</a>
41 + </Menu.Item>
39 <Menu.Item key="logout"> 42 <Menu.Item key="logout">
40 <a onClick={logoutHandler}>로그아웃</a> 43 <a onClick={logoutHandler}>로그아웃</a>
41 </Menu.Item> 44 </Menu.Item>
......
1 -import React from 'react';
2 -import { Menu } from 'antd';
3 -const SubMenu = Menu.SubMenu;
4 -const MenuItemGroup = Menu.ItemGroup;
5 -
6 -function LeftMenu(props) {
7 - return (
8 - <Menu mode={props.mode}>
9 - <Menu.Item key="mail">
10 - <a href="/">Home</a>
11 - </Menu.Item>
12 - <SubMenu title={<span>Blogs</span>}>
13 - <MenuItemGroup title="Item 1">
14 - <Menu.Item key="setting:1">Option 1</Menu.Item>
15 - <Menu.Item key="setting:2">Option 2</Menu.Item>
16 - </MenuItemGroup>
17 - <MenuItemGroup title="Item 2">
18 - <Menu.Item key="setting:3">Option 3</Menu.Item>
19 - <Menu.Item key="setting:4">Option 4</Menu.Item>
20 - </MenuItemGroup>
21 - </SubMenu>
22 - </Menu>
23 - )
24 -}
25 -
26 -export default LeftMenu
...\ No newline at end of file ...\ No newline at end of file
1 -/* eslint-disable jsx-a11y/anchor-is-valid */
2 -import React from 'react';
3 -import { Menu } from 'antd';
4 -import axios from 'axios';
5 -//import { USER_SERVER } from '../../../Config';
6 -import { withRouter } from 'react-router-dom';
7 -import { useSelector } from "react-redux";
8 -
9 -function RightMenu(props) {
10 - const user = useSelector(state => state.user)
11 -
12 - const logoutHandler = () => {
13 - axios.get('/api/users/logout').then(response => {
14 - if (response.status === 200) {
15 - props.history.push("/login");
16 - } else {
17 - alert('Log Out Failed')
18 - }
19 - });
20 - };
21 -
22 - //console.log(user.userData)
23 - //console.log(!user.userData.isAuth)
24 - if (user.userData && !user.userData.isAuth) {
25 - return (
26 - <Menu mode={props.mode}>
27 - <Menu.Item key="mail">
28 - <a href="/login">Signin</a>
29 - </Menu.Item>
30 - <Menu.Item key="app">
31 - <a href="/register">Signup</a>
32 - </Menu.Item>
33 - </Menu>
34 - )
35 - } else {
36 - return (
37 - <Menu mode={props.mode}>
38 - <Menu.Item key="logout">
39 - <a onClick={logoutHandler}>Logout</a>
40 - </Menu.Item>
41 - </Menu>
42 - )
43 - }
44 -}
45 -
46 -export default withRouter(RightMenu);
...\ No newline at end of file ...\ No newline at end of file
1 -import React from 'react' 1 +import React from 'react';
2 +import { useState } from 'react';
3 +import { Typography, Button, Form, Input } from 'antd'; // css
4 +import ImageUpload from '../../utils/ImageUpload'
5 +
6 +const { TextArea } = Input; // 박스크기 조절을 사용자가 임의로 가능하게 함.
7 +
8 +// Select Options
9 +const options = [{ key: 1, value: "a" },
10 + { key: 2, value: "b" },
11 + {key: 3, value : "c"}
12 +]
2 13
3 function UploadPage() { 14 function UploadPage() {
15 +
16 + // OnChange Function
17 +
18 + const [Image, setImage] = useState("")
19 + const [Title, setTitle] = useState("");
20 + const [Info, setInfo] = useState("");
21 + const [Cost, setCost] = useState("");
22 + const [Option, setOption] = useState(1);
23 +
24 + const titleEvent = (event) => {
25 + setTitle(event.currentTarget.value);
26 + }
27 +
28 + const infoEvent = (event) => {
29 + setInfo(event.currentTarget.value);
30 + }
31 +
32 + const costEvent = (event) => {
33 + setCost(event.currentTarget.value);
34 + }
35 +
36 +
37 + const optionEvent = (event) => {
38 + setOption(event.currentTarget.value);
39 + }
40 +
41 +
42 + const imageEvent = (event) => {
43 + setImage(event.currentTarget.value);
44 + }
45 +
46 +
4 return ( 47 return (
5 - <div> 48 + <div style={{ maxWidth: '700px', margin: '2rem auto' }}>
6 - uploadpage 49 +
50 + <div style={{ textAlign: 'center', marginBottom:'2rem'}}>
51 + <h2> 업로드 </h2>
52 +
53 + </div>
54 +
55 + <Form>
56 + {/* 파일업로드 부분은 코드가 길어서 따로 컴포넌트로 만들었습니다. */}
57 + <ImageUpload />
58 + <br />
59 + <br />
60 + <label>이름</label>
61 + <Input onChange={ titleEvent} value={Title} />
62 + {/* ㄴ ant design에서 가져온 Input */}
63 + <br />
64 + <br />
65 + <label>설명</label>
66 + <TextArea onChange={ infoEvent} value={Info} />
67 + <br />
68 + <br />
69 + <label>가격</label>
70 + <Input onChange={ costEvent} value={Cost} type="number"/>
71 + <br />
72 + <br />
73 + <select onChange={optionEvent} value={ Option}>
74 + {options.map(item => (
75 + <option key={item.key} value={item.key}>{ item.value}</option>
76 + ))}
77 + <option></option>
78 + </select>
79 + <br />
80 + <br />
81 + <Button>확인</Button>
82 +
83 + </Form>
84 +
7 </div> 85 </div>
8 ) 86 )
9 -} 87 +}
10 88
11 export default UploadPage 89 export default UploadPage
......
1 -// 사용자의 상태를 보고
2 -// 해당 페이지에 들어갈 수 있게 할지 안할지 결정해 줌. hoc 이용.
3 -import axios from 'axios';
4 -import React, {useEffect} from 'react';
5 -//import {useEffect} from 'react';
6 -import {useDispatch} from 'react-redux';
7 -import {auth} from '../_actions/user_action'
8 -
9 -
10 -export default function (SpecificComponent, option, adminRoute = null){
11 - // ㄴ option 종류
12 - // - null 아무나 출입 가능한 페이지
13 - // - true 로그인한 유저만 출입 가능
14 - // - false 로그인한 유저 출입 불가능
15 -
16 - function AuthenticationCheck(props) {
17 - //1. backend에 request를 날려서 사용자의 상태를 확인
18 - const dispatch = useDispatch(); // 1-1. dispatch 사용
19 - useEffect(() => {
20 - dispatch(auth()) // 페이지가 이동할 때마다 dispatch가 작동해서 backend에 request를 줌
21 - .then(response => { // 받은 response
22 - console.log(response);
23 -
24 -
25 - // 로그인 안했다면
26 - if(!response.payload.isAuth) {
27 - if(option) { // 만약 위 option이 true이면 (로그인한 유저만 출입 가능한 페이지로 가게 하려면)
28 - props.history.push('/login'); // 로그인 하게 함
29 - }
30 - }
31 -
32 - // 로그인 했다면
33 - else {
34 - if(adminRoute && !response.payload.isAdmin) { // admin만 갈 수 있는 페이지를 admin이 false 사람이 들어가려 한다면
35 - props.history.push('/'); // 홈페이지로 돌아가게 함
36 - }
37 - else {
38 - if(option===false) {// 로그인한 유저가 출입 불가능한 곳을 가려고 한다면
39 - props.history.push('/'); // 홈페이지로 돌아가게 함
40 -
41 - }
42 - }
43 - }
44 - })
45 - },[])
46 -
47 - return (
48 - <SpecificComponent />
49 - )
50 -
51 - }
52 -
53 - return AuthenticationCheck
54 -}
...\ No newline at end of file ...\ No newline at end of file
1 body { 1 body {
2 margin: 0; 2 margin: 0;
3 - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", 3 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 - "Droid Sans", "Helvetica Neue", sans-serif; 4 + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 + sans-serif;
5 -webkit-font-smoothing: antialiased; 6 -webkit-font-smoothing: antialiased;
6 -moz-osx-font-smoothing: grayscale; 7 -moz-osx-font-smoothing: grayscale;
7 } 8 }
8 9
9 code { 10 code {
10 - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; 11 + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 + monospace;
11 } 13 }
12 14
13 body { 15 body {
14 - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", 16 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
15 - "Segoe UI Emoji", "Segoe UI Symbol"; 17 + sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
16 font-size: 14px; 18 font-size: 14px;
17 line-height: 1.5; 19 line-height: 1.5;
18 color: #24292e; 20 color: #24292e;
...@@ -31,7 +33,6 @@ input.error { ...@@ -31,7 +33,6 @@ input.error {
31 border-color: red; 33 border-color: red;
32 } 34 }
33 35
34 -
35 .input-feedback { 36 .input-feedback {
36 color: red; 37 color: red;
37 height: 5px; 38 height: 5px;
......
1 -const reportWebVitals = onPerfEntry => {
2 - if (onPerfEntry && onPerfEntry instanceof Function) {
3 - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 - getCLS(onPerfEntry);
5 - getFID(onPerfEntry);
6 - getFCP(onPerfEntry);
7 - getLCP(onPerfEntry);
8 - getTTFB(onPerfEntry);
9 - });
10 - }
11 -};
12 -
13 -export default reportWebVitals;
1 -// jest-dom adds custom jest matchers for asserting on DOM nodes.
2 -// allows you to do things like:
3 -// expect(element).toHaveTextContent(/react/i)
4 -// learn more: https://github.com/testing-library/jest-dom
5 -import '@testing-library/jest-dom';
...@@ -103,6 +103,11 @@ ...@@ -103,6 +103,11 @@
103 } 103 }
104 } 104 }
105 }, 105 },
106 + "append-field": {
107 + "version": "1.0.0",
108 + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
109 + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
110 + },
106 "aproba": { 111 "aproba": {
107 "version": "1.2.0", 112 "version": "1.2.0",
108 "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 113 "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
...@@ -397,6 +402,43 @@ ...@@ -397,6 +402,43 @@
397 "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 402 "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
398 "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 403 "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
399 }, 404 },
405 + "buffer-from": {
406 + "version": "1.1.1",
407 + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
408 + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
409 + },
410 + "busboy": {
411 + "version": "0.2.14",
412 + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
413 + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
414 + "requires": {
415 + "dicer": "0.2.5",
416 + "readable-stream": "1.1.x"
417 + },
418 + "dependencies": {
419 + "isarray": {
420 + "version": "0.0.1",
421 + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
422 + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
423 + },
424 + "readable-stream": {
425 + "version": "1.1.14",
426 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
427 + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
428 + "requires": {
429 + "core-util-is": "~1.0.0",
430 + "inherits": "~2.0.1",
431 + "isarray": "0.0.1",
432 + "string_decoder": "~0.10.x"
433 + }
434 + },
435 + "string_decoder": {
436 + "version": "0.10.31",
437 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
438 + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
439 + }
440 + }
441 + },
400 "bytes": { 442 "bytes": {
401 "version": "3.1.0", 443 "version": "3.1.0",
402 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 444 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
...@@ -604,6 +646,17 @@ ...@@ -604,6 +646,17 @@
604 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 646 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
605 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 647 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
606 }, 648 },
649 + "concat-stream": {
650 + "version": "1.6.2",
651 + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
652 + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
653 + "requires": {
654 + "buffer-from": "^1.0.0",
655 + "inherits": "^2.0.3",
656 + "readable-stream": "^2.2.2",
657 + "typedarray": "^0.0.6"
658 + }
659 + },
607 "concurrently": { 660 "concurrently": {
608 "version": "4.1.2", 661 "version": "4.1.2",
609 "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.2.tgz", 662 "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.2.tgz",
...@@ -834,6 +887,38 @@ ...@@ -834,6 +887,38 @@
834 "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 887 "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
835 "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 888 "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
836 }, 889 },
890 + "dicer": {
891 + "version": "0.2.5",
892 + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
893 + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
894 + "requires": {
895 + "readable-stream": "1.1.x",
896 + "streamsearch": "0.1.2"
897 + },
898 + "dependencies": {
899 + "isarray": {
900 + "version": "0.0.1",
901 + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
902 + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
903 + },
904 + "readable-stream": {
905 + "version": "1.1.14",
906 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
907 + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
908 + "requires": {
909 + "core-util-is": "~1.0.0",
910 + "inherits": "~2.0.1",
911 + "isarray": "0.0.1",
912 + "string_decoder": "~0.10.x"
913 + }
914 + },
915 + "string_decoder": {
916 + "version": "0.10.31",
917 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
918 + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
919 + }
920 + }
921 + },
837 "dot-prop": { 922 "dot-prop": {
838 "version": "4.2.0", 923 "version": "4.2.0",
839 "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", 924 "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
...@@ -2644,6 +2729,21 @@ ...@@ -2644,6 +2729,21 @@
2644 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2729 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
2645 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 2730 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
2646 }, 2731 },
2732 + "multer": {
2733 + "version": "1.4.2",
2734 + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz",
2735 + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==",
2736 + "requires": {
2737 + "append-field": "^1.0.0",
2738 + "busboy": "^0.2.11",
2739 + "concat-stream": "^1.5.2",
2740 + "mkdirp": "^0.5.1",
2741 + "object-assign": "^4.1.1",
2742 + "on-finished": "^2.3.0",
2743 + "type-is": "^1.6.4",
2744 + "xtend": "^4.0.0"
2745 + }
2746 + },
2647 "nan": { 2747 "nan": {
2648 "version": "2.14.0", 2748 "version": "2.14.0",
2649 "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 2749 "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
...@@ -3717,6 +3817,11 @@ ...@@ -3717,6 +3817,11 @@
3717 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 3817 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
3718 "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 3818 "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
3719 }, 3819 },
3820 + "streamsearch": {
3821 + "version": "0.1.2",
3822 + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
3823 + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
3824 + },
3720 "string-width": { 3825 "string-width": {
3721 "version": "1.0.2", 3826 "version": "1.0.2",
3722 "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 3827 "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
...@@ -3913,6 +4018,11 @@ ...@@ -3913,6 +4018,11 @@
3913 "mime-types": "~2.1.24" 4018 "mime-types": "~2.1.24"
3914 } 4019 }
3915 }, 4020 },
4021 + "typedarray": {
4022 + "version": "0.0.6",
4023 + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
4024 + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
4025 + },
3916 "undefsafe": { 4026 "undefsafe": {
3917 "version": "2.0.3", 4027 "version": "2.0.3",
3918 "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", 4028 "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
...@@ -4178,6 +4288,11 @@ ...@@ -4178,6 +4288,11 @@
4178 "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", 4288 "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
4179 "dev": true 4289 "dev": true
4180 }, 4290 },
4291 + "xtend": {
4292 + "version": "4.0.2",
4293 + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
4294 + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
4295 + },
4181 "y18n": { 4296 "y18n": {
4182 "version": "4.0.0", 4297 "version": "4.0.0",
4183 "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 4298 "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
25 "jsonwebtoken": "^8.5.1", 25 "jsonwebtoken": "^8.5.1",
26 "moment": "^2.24.0", 26 "moment": "^2.24.0",
27 "mongoose": "^5.4.20", 27 "mongoose": "^5.4.20",
28 + "multer": "^1.4.2",
28 "react-redux": "^5.0.7", 29 "react-redux": "^5.0.7",
29 "saslprep": "^1.0.3", 30 "saslprep": "^1.0.3",
30 "supports-color": "^7.1.0" 31 "supports-color": "^7.1.0"
......
...@@ -30,6 +30,7 @@ app.use(cors()) ...@@ -30,6 +30,7 @@ app.use(cors())
30 30
31 31
32 app.use('/api/users', require('./routes/users')); 32 app.use('/api/users', require('./routes/users'));
33 +app.use('/api/product', require('./routes/product'));
33 34
34 35
35 // 이미지 가져오려고 36 // 이미지 가져오려고
......
1 +const express = require('express');
2 +const { User } = require("../models/User");
3 +const { auth } = require("../middleware/auth");
4 +const router = express.Router();
5 +const multer = require('multer');
6 +
7 +
8 +var storage = multer.diskStorage({
9 + destination: function (req, file, cb) {
10 + cb(null, 'uploads/') // 어느 폴더에 저장할건지
11 + },
12 + filename: function (req, file, cb) {
13 + cb(null, Date.now() + '_' + file.originalname) // 이미지 이름
14 + }
15 + })
16 +
17 +var upload = multer({ storage: storage }).single("file");
18 +
19 +router.post('/image', (req, res) => {
20 +
21 + // 클라이언트로부터 받은 이미지 저장
22 + upload(req, res, (err) => {
23 + if (err) return req.json({ success: false, err })
24 + return res.json({success: true, filePath: res.req.file.path, fileName: res.req.file.filename})
25 + }
26 + )
27 +
28 +})
29 +
30 +module.exports = router;