https://www.youtube.com/playlist?list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB
React JS 이론부터 실전까지
www.youtube.com
1강 - React JS의 개요 및 Hello World [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=s2knmog2j1U&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=1
https://github.com/facebook/react
GitHub - facebook/react: The library for web and native user interfaces
The library for web and native user interfaces. Contribute to facebook/react development by creating an account on GitHub.
github.com
https://reactjs.org/redirect-to-codepen/hello-world
js--------------------
ReactDom.render(
<h1>Hello, React!</h1>,
document.getElementById('root')
);
2강 - 코드펜(Codepen)을 이용한 React 개발환경 구축하기 [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=Hs943cOaQWg&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=2
CodePen
An online code editor, learning environment, and community for front-end web development using HTML, CSS and JavaScript code snippets, projects, and web applications.
codepen.io
create - pen - settings
JavaScript
Add External Scripts/Pens
react
react-dom
JavaScript Preprocessor
Babel
====================
HTML--------------------
<div id="root"></div>
JS--------------------
class HelloReact extends React.Component {
render() {
return (
<h1>Hello React!</h1>
)
}
}
ReactDOM.render(<HelloReact/>, document.getElementById('root'));
3강 - React에서 JSX란 [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=kjnVKNmT_xE&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=3
====================
HTML--------------------
<div id="root"></div>
CSS--------------------
.blue {
color: blue;
}
.red {
color: red;
}
JS--------------------
function formatInfo(student) {
return student.name + "[" + student.id + "]"
}
const student = {
id: "2015315"
name: "Dongbin Na"
color: "blue"
}
const element = (
<h3 class={student.color}>
{formatInfo(student)}
</h3>
);
ReactDOM.render(element, document.getElementById('root'));
react.component는 함수로 component를 정의할 수 있다 - 함수형 component
함수로 component를 작성하면 render 함수를 생략한 형태로 작성할 수 있다
{formatInfo(student)} 중괄호는 JavaScript를 쓰겠다고 표시한 거다 - JSX
{student.color} 속성에도 JSX가 들어갈 수 있다
====================
HTML--------------------
<div id="root"></div>
JS--------------------
function tick() {
const element = (
<h3>현재 시각은 [{new Date().toLocaleTimeString()}] 입니다.</h3>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
ReactDOM이 특정 함수 안에 작성하는 일은 드물다
4강 - React의 Component와 Props [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=HZlH8olDeAQ&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=4
====================
HTML--------------------
<div id="root"></div>
JS--------------------
function Show(props) {
return (
<h3>
Name is {props.name}
</h3>
);
}
const element = <Show name="동빈나"/>;
ReactDOM.render(element, document.getElementById("root"));
function Show(props) {
return (
<h3>
Name is {props.name}
</h3>
);
}
ReactDOM.render(<Show name="동빈나"/>, document.getElementById("root"));
JS--------------------default값 명시할 수 있다
function Show(props) {
return (
<h3>
Name is {props.name}
</h3>
);
}
Show.defaultProps = {
name: '홍길동'
}
ReactDOM.render(<Show/>, document.getElementById("root"));
JS--------------------부모로부터 props 물려 받을 수 있으니까 계층적으로 작성하면
function Show(props) {
return (
<h3>
Name is {props.name}
</h3>
);
}
function App() {
return (
<div>
<Show name="나동빈" />
<Show name="홍길동" />
<Show name="이순신" />
</div>
)
}
Show.defaultProps = {
name: '홍길동'
}
ReactDOM.render(<App/>, document.getElementById("root"));
App - Show 계층적으로 작성
====================
HTML--------------------
<div id="root"></div>
JS--------------------
function User(props) {
return (
<div>
<img src={props.user.imageUrl}/>
<strong>{props.user.name}</strong>
</div>
);
}
function Board(props) {
return (
<section>
<User user={props.user}/>
{props.title}
<hr/>
{props.content}
</section>
);
}
const board = {
title: '게시글 제목입니다.',
content: '게시글 내용입니다. 반갑습니다.',
user: {
name: '나동빈',
imageUrl: 'https://placeimg.com/32/32/any'
}
}
ReactDOM.render(
<Board
title={board.title}
content={board.content}
user={board.user}
/>,
document.getElementById('root')
);
게시글 작성에 유용
Board-User-board
5강 - React의 State [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=YCe3LhhOxr0&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=5
props - 부모 component에서 자식 component로 데이터 전달할 때 사용, 고정적은 데이터를 전달할 때 사용, 함수형 컴포넌트
state - 변경될 수 있는 데이터를 처리할 때 사용, 클래스형 컴포넌트
React는 계속적으로 상태변화를 감지하고 있기때문에
state값을 변경해서
화면(View)이 변경되면
render()함수가 다시 실행되어
실제로 화면에 적용을 해준다
어떤 정적인 데이터만 사용한다면 - props - 함수형 컴포넌트 사용할 수 있지만,
값이 계속해서 변경될 여지가 있다면 - state - 클래스형 컴포넌트를 사용
====================
HTML--------------------
<div id="root"></div>
JS--------------------저번에 현재 시간을 출력하는 코드
function tick() {
const element = (
<h3>현재 시각은 [{new Date().toLocaleTimeString()}] 입니다.</h3>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
JS--------------------객체지향으로 수정-함수형 컴포넌트 props 사용
function Clock(props) {
return (
<h3>현재 시각은 [{props.date.toLocaleTimeString()}] 입니다.</h3>
);
}
function tick() {
ReactDOM.render(<Clock date={new Date()}, document.getElementById('root'));
}
setInterval(tick, 1000);
JS--------------------객체지향으로 수정-클래스형 컴포넌트-props버전
class Clock extends React.Component {
render() {
return (
<h3>현재 시각은 [{this.props.date.toLocaleTimeString()}] 입니다.</h3>
);
}
}
function tick() {
ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('root'));
}
setInetval(tick, 1000);
JS--------------------클래스형 컴포넌트 state 사용
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}
render() {
return (
<h3>현재 시각은 [{this.state.date.toLocaleTimeString()}] 입니다.</h3>
);
}
}
function tick() {
ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('root'));
}
setInetval(tick, 1000);
클래스형 컴포넌트는 render함수 사용해야 된다
클래스형 컴포넌트를 사용하는 경우에는 props를 기본적으로 내장하고 있기 때문에 앞에 this를 붙이고
render(props) 안해줘도 된다.
시간이 가지 않는다---추가작업해주자
JS--------------------
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { // 초기값 설정
date: new Date()
};
}
tick() {
this.setState({ // 변경값 설정
date: new Date()
})
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount(){
clearInterval(this.timerID);
}
render() {
return (
<h3>현재 시각은 [{this.state.date.toLocaleTimeString()}] 입니다.</h3>
);
}
}
ReactDOM.render(<Clock/>, document.getElementById('root'));
라이프사이클 이용해야 된다.
값이 계속 변경되는 경우는 state를 이용하여 작성한다
6강 - React의 LifeCycle과 API 호출 [React JS 이론부터 실전까지]
https://www.youtube.com/watch?v=mhBfAibYwHQ&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=6
https://jsonplaceholder.typicode.com
JSONPlaceholder - Free Fake REST API
{JSON} Placeholder Free fake API for testing and prototyping. Powered by JSON Server + LowDB. Tested with XV. Serving ~2 billion requests each month.
jsonplaceholder.typicode.com
====================
JS--------------------
class EventHandling extends React.Component {
constructor(props) {
super(props);
}
handleClick() {
console.log("이벤트 처리");
}
render() {
return (
<button onClick={this.handleClick}>버튼</button>
);
}
}
ReactDOM.render(<EventHandling/>, document.getElementById('root'));
====================
JS--------------------
class EventHandling extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
}
}
handleClick() {
this.setState(state => ({
isToggleOn: !this.state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(<EventHandling/>, document.getElementById('root'));
처음 toggle버트이 true라고 초기값 넣어주고
현재값이 true이면 false로
false이면 true로 바꾸어준다
{this.state.isToggleOn ? 'ON' : 'OFF'}
버튼을 눌러도 변화가 없다. bind문제
자바스크립트는 바인딩 처리가 기본적으로 설정되지 않는다
특정한 변수의 값을 바꾸고자 할 때는 반드시 바인딩처리를 해주어야 된다.
가장 간단한 방법은
handleClick = () => {
JS--------------------
class EventHandling extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
}
}
handleClick = () => {
this.setState(state => ({
isToggleOn: !this.state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(<EventHandling/>, document.getElementById('root'));
이런식으로 처리해주거나
혹은 초기화하는 부분에서 해당 함수에서 바인딩처리를 해주겠다고 직접 명시하는것
JS--------------------
class EventHandling extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !this.state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(<EventHandling/>, document.getElementById('root'));
JS--------------------위의 방식을 더 선호
class EventHandling extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
}
}
handleClick = () => {
this.setState(state => ({
isToggleOn: !this.state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(<EventHandling/>, document.getElementById('root'));
자동으로 바인딩처리가 되기 때문에
1강 - Create React App으로 리액트 프로젝트 시작하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=_yEH9mczm3g&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=8
폴더만들고
react_na
npm install -g create-react-app
create-react-app management(어플이름)
dir
cd management
npm install -g yarn 얀 설치 안되어 있다면
yarn start 리액트 앱 구동
ctrl + c 서버 종료
2강 - Visual Studio Code를 이용한 소스코드 작성 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=kQ3fBeJmP8Q&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=9
3강 - 깃 허브(Git Hub)를 이용해 소스코드 관리하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=D2L_sIvJEu0&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=10
src-App.js--------------------
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="gray-background">
<img src={logo} lat="logo" />
<h2>Let's develop management system!</h2>
</div>
);
}
export default App;
src-App.css--------------------
.gray-background {
background: darkgray;
}
public-index.html--------------------
<!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" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
src-index.js--------------------
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// 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();
README.md--------------------
## Management System
본 프로젝트는 React 강의 목적으로 만들어진 고객 관리 시스템
(Management System)입니다.
github.com
git repositories
new
React-Management-Tutorial
Create repository
vscode-git
changes +
커밋메시지
Create React Project
커밋버튼
터미널 창 열기 Ctrl + Shift + `
원격지 깃 주소 등록
git remote add origin 원격지 깃 주소
git push --set-upstream origin master
github에 가보면
.gitignore
/node_modules 기본적으로 들어가 있다
4강 - 고객 컴포넌트(Component) 만들기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=4qr6cQYUORw&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=11
src-App.js--------------------클래스형으로 강의 진행
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className='gray-background'>
<img src={logo} lat="logo" />
<h2>Let' develop management system!</h2>
</div>
);
}
}
export default App;
src-components-Customer.js--------------------
import React from "react";
class Customer extends React.Component {
render() {
return (
<div>
<h2>홍길동</h2>
<p>961222</p>
<p>남자</p>
<p>대학생</p>
</div>
)
}
}
export default Customer;
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
class App extends Component {
render() {
return (
<Customer/>
);
}
}
export default App;
====================
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
const customer = {
'name': '홍길동',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
}
class App extends Component {
render() {
return (
<Customer
name={customer.name}
birthday={customer.birthday}
gender={customer.gender}
job={customer.job}
/>
);
}
}
export default App;
src-components-Customer.js--------------------
import React from "react";
class Customer extends React.Component {
render() {
return (
<div>
<h2>{this.props.name}</h2>
<p>{this.props.birthday}</p>
<p>{this.props.gender}</p>
<p>{this.props.job}</p>
</div>
)
}
}
export default Customer;
5강 - 고객 컴포넌트 구조화하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=UT9U9WALGS0&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=12
화면상에 출력할 정보가 엄청나게 많다면
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
const customer = {
'id': 1,
'image': 'https://placeimg.com/64/64/any',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
}
class App extends Component {
render() {
return (
<Customer
id={customer.id}
image={customer.image}
name={customer.name}
birthday={customer.birthday}
gender={customer.gender}
job={customer.job}
/>
);
}
}
export default App;
src-components-Customer.js--------------------
import React from "react";
class Customer extends React.Component {
render() {
return (
<div>
<CustomerProfile id={this.props.id} image={this.props.image} name={this.props.name}/>
<CustomerInfo birthday={this.props.birthday} gender={this.props.gender} job={this.props.job}/>
</div>
)
}
}
class CustomerProfile extends React.Component {
render() {
return (
<div>
<img src={this.props.image} alt="profile"/>
<h2>{this.props.name}({this.props.id})</h2>
</div>
)
}
}
class CustomerInfo extends React.Component {
render() {
return (
<div>
<p>{this.props.birthday}</p>
<p>{this.props.gender}</p>
<p>{this.props.job}</p>
</div>
)
}
}
export default Customer;
여러개 출력한다면
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
const customers = [
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]
class App extends Component {
render() {
return (
<div>
<Customer
id={customers[0].id}
image={customers[0].image}
name={customers[0].name}
birthday={customers[0].birthday}
gender={customers[0].gender}
job={customers[0].job}
/>
<Customer
id={customers[1].id}
image={customers[1].image}
name={customers[1].name}
birthday={customers[1].birthday}
gender={customers[1].gender}
job={customers[1].job}
/>
<Customer
id={customers[2].id}
image={customers[2].image}
name={customers[2].name}
birthday={customers[2].birthday}
gender={customers[2].gender}
job={customers[2].job}
/>
</div>
);
}
}
export default App;
map함수 이용
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
const customers = [
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]
class App extends Component {
render() {
return (
<div>
{
customers.map(c => {
return (
<Customer
id={c.id}
image={c.image}
name={c.name}
birthday={c.birthday}
gender={c.gender}
job={c.job}
/>
);
})
}
<Customer
id={customers[0].id}
image={customers[0].image}
name={customers[0].name}
birthday={customers[0].birthday}
gender={customers[0].gender}
job={customers[0].job}
/>
</div>
);
}
}
export default App;
map 사용하면 필히 각 원소를 구분할 수 있는 key 넣어줘야 된다
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
const customers = [
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]
class App extends Component {
render() {
return (
<div>
{
customers.map(c => {
return (
<Customer
key={c.id}
id={c.id}
image={c.image}
name={c.name}
birthday={c.birthday}
gender={c.gender}
job={c.job}
/>
);
})
}
</div>
);
}
}
export default App;
6강 - Material UI를 적용하여 고객 테이블 디자인하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=qTVmxoXZkKQ&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=13
https://mui.com/material-ui/getting-started/installation/
Installation - Material UI
Install Material UI, the world's most popular React UI framework.
mui.com
npm install @material-ui/core 에러나네
npm install @mui/material @emotion/react @emotion/styled
React UI 라이브러리 - MUI (Material-ui)
https://www.youtube.com/watch?v=or3np70c7zU
npm install @mui/material
yarn start
====================
src-components-Customer.js--------------------
import React from "react";
import { TableRow } from "@mui/material";
import { TableCell } from "@mui/material";
class Customer extends React.Component {
render() {
return (
<TableRow>
<TableCell>{this.props.id}</TableCell>
<TableCell><img src={this.props.image} alt="profile"></img></TableCell>
<TableCell>{this.props.name}</TableCell>
<TableCell>{this.props.birthday}</TableCell>
<TableCell>{this.props.gender}</TableCell>
<TableCell>{this.props.job}</TableCell>
</TableRow>
)
}
}
export default Customer;
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
const customers = [
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]
class App extends Component {
render() {
return (
<div>
<Table>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);})}
</TableBody>
</Table>
</div>
);
}
}
export default App;
css 적용해보자
src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
const styles = theme =>({
root: {
width : '100%',
// marginTop: theme.spacing(3),
marginTop: theme.spacing.unit * 3,
overflowX: "auto"
},
table:{
minWidth:1080,
}
})
const customers = [
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]
class App extends Component {
render() {
const { classes } = this.props;
return (
<Paper sx={{width : '100%', overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);})}
</TableBody>
</Table>
</Paper>
);
}
}
export default App;
https://www.youtube.com/watch?v=qk2oY7W3fuY
<Paper sx={{overflowX: "auto"}}>
<Table stickyHeader sx={{maxWidth: '1080px', minWidth: '1080px'}}>
withStyles 에러나서 적용안됨
7강 - Node.js Express 서버 개발환경 구축하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=YO9CqrnxbFU&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=14
package.json--------------------
{
"name": "management",
"version": "1.0.0",
"scripts": {
"client": "cd client && yarn start",
"server": "nodemon server.js",
"dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\""
},
"dependencies": {
"body-parser": "1.20.2",
"express": "4.18.2"
},
"devDependencies": {
"concurrently": "8.0.1"
}
}
client-.gitignore 복사해서 루트 폴더에도 넣어준다
npm install
npm install -g nodemon
server.js--------------------
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/api/hello', (req, res) => {
res.send({message: 'Hello Express!'});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
node server.js
크롬에서 접속해보자
http://localhost:5000/api/hello
// 20230413122303
// http://localhost:5000/api/hello
{
"message": "Hello Express!"
}
Ctrl + C 서버중단
vscod-git
Add Node.js Express Server Module
commit
push
8강 - Node.js Express에서 REST API 구축하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=zug4VBcZOrI&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=15
서버, 클라이언트 동시 실행
yarn dev
서버
localhost:5000/api/hello
클라이언트
localhost:3000
REST API 알아보자
server.js--------------------
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/api/customers', (req, res) => {
res.send([
{
'id': 1,
'image': 'https://placeimg.com/64/64/1',
'name': '나동빈',
'birthday': '961222',
'gender': '남자',
'job': '대학생'
},
{
'id': 2,
'image': 'https://placeimg.com/64/64/2',
'name': '홍길동',
'birthday': '960305',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '921205',
'gender': '남자',
'job': '디자이너'
}
]);
});
app.listen(port, () => console.log(`Listening on port ${port}`));
Ctrl + C
yarn dev
===
클라이언트가 서버에서 데이터를 받아와서
jsonlint.com 데이터 json 검증 사이트
일반적으로 리액트에서는 비동기통신을 이용해서
서버에 접근하여 데이터를 가져오세요
5000번 포트를 api 서버로 개발목적으로 이용하기 위해서
client-package.json 에 추가로 proxy 설정
client-package.json--------------------
{
"name": "management",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mui/material": "^5.11.16",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"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"
]
},
"proxy": "http://localhost:5000/"
}
client-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
// const styles = theme =>({
// root: {
// width : '100%',
// // marginTop: theme.spacing(3),
// marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// })
class App extends Component {
state = {
customers: ""
}
componentDidMount() {
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
render() {
const { classes } = this.props;
return (
<Paper sx={{width : '100%', overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);})}
</TableBody>
</Table>
</Paper>
);
}
}
export default App;
client-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
// const styles = theme =>({
// root: {
// width : '100%',
// // marginTop: theme.spacing(3),
// marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// })
class App extends Component {
state = {
customers: ""
}
componentDidMount() {
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
render() {
const { classes } = this.props;
return (
<Paper sx={{width : '100%', overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) : ""}
</TableBody>
</Table>
</Paper>
);
}
}
export default App;
client-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: ""
}
componentDidMount() {
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
render() {
// const { classes } = this.props;
return (
// <Paper className={classes.root}>
// <Table className={classes.table}>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) : ""}
</TableBody>
</Table>
</Paper>
);
}
}
// export default withStyles(styles)(App);
export default App;
yarn dev
서버
localhost:5000/api/customers
클라이언트
localhost:3000
9강 - React의 라이프 사이클 이해 및 API 로딩 처리 구현하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=YWwoO3_RnFM&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=16
로딩메시지
https://mui.com/material-ui/react-progress/
Circular, Linear progress React components - Material UI
Progress indicators commonly known as spinners, express an unspecified wait time or display the length of a process.
mui.com
component는 이러한 라이프사이클을 가지고 있다
1) constructor() - 처음 constructor를 불러오고
2) componentWillMount() - component가 마운트 되기전에
3) render() - 실제로 component를 화면에 그리고
4) componentDidMount() - 그 이후에 불러와 진다
component의 props 혹은 state가 변경되는 경우에는 shouldComponentUpdate() 사용되어서
다시 render()함수를 불러와서 view를 갱신해주게 된다
리액트는 상태변화를 알아서 감지해서
이러한 view 즉 화면을 다시 재구성해주게 되기때문에
프로그래머는 상태만 잘 관리해주면 된다
API를 불러와서 웹사이트 화면에 특정한 view를 출력하고자 한다면
componentDidMount()함수에서 API를 비동기적으로 호출하면 된다
API에서 응답이 돌아 왔을 때
상태가 변화되고
리액트에서 그러한 상태변화를 감지해서
알아서 view가 갱신되기 때문에
화면에 이러한 API응답 결과를 출력할 수가 있는 것이다.
다만 비동기적으로 호출이 이루어진다는 점에서
API 서버에서 응답을 하지 않으면
사용자에게 로딩화면만 출력이 되도록 설정을 해야 된다
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
// <Paper className={classes.root}>
// <Table className={classes.table}>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
<CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-App.js--------------------progress보기위해 잠시 수정
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
// this.callApi()
// .then(res => this.setState({customers: res}))
// .catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
// <Paper className={classes.root}>
// <Table className={classes.table}>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
<CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-App.js--------------------progress보기위해 잠시 수정
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
// this.callApi()
// .then(res => this.setState({customers: res}))
// .catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
// <Paper className={classes.root}>
// <Table className={classes.table}>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
// <Paper className={classes.root}>
// <Table className={classes.table}>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
);
}
}
// export default withStyles(styles)(App);
export default App;
vscode-git
Add Customer API Loading View
10강 - AWS RDS 서비스를 이용하여 MySQL DB 구축하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=G6O-u6FkjpY&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=17
SHOW DATABASES;
CREATE DATABASE management DEFAULT CHARACTER SET UTF8;
USE management;
SELECT VERSION();
11강 - 고객(Customer) DB 테이블 구축 및 Express와 연동하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=T5dwM_j8wmU&t=1s
USE management;
CREATE TABLE customer (
id int primary key auto_increment,
image varchar(1024),
name varchar(64),
birthday varchar(64),
gender varchar(64),
job varchar(64)
) default character set utf8 collate utf8_general_ci;
insert into customer values (1, 'https://placeimg.com/64/64/1','나동빈','961222','남자','대학생');
insert into customer values (2, 'https://placeimg.com/64/64/2','홍길동','960305','남자','프로그래머');
insert into customer values (3, 'https://placeimg.com/64/64/3','나동빈','961205','남자','디자이너');
SELECT * FROM management.customer;
.gitignore--------------------
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# database
/database.json
# 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*
database.json--------------------
{
"host": "localhost",
"user": "root",
"password": "패스워드",
"port": "3306",
"database": "management"
}
server.js--------------------
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
const data = fs.readFileSync('./database.json');
const conf = JSON.parse(data);
const mysql = require('mysql');
console.log(conf.host);
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
database: conf.database
});
connection.connect();
app.get('/api/customers', (req, res) => {
connection.query(
"SELECT * FROM customer",
(err, rows, fields) => {
res.send(rows);
}
);
});
app.listen(port, () => console.log(`Listening on port ${port}`));
yarn dev
INSERT INTO customer VALUES (4, 'https://placeimg.com/64/64/4','유관순','950205',' 여자','변호사');
SELECT * FROM management.customer;
vscode-git
Create Customer Database & MySQL Connection
12강 - 고객 추가 양식(Form) 구현 및 이벤트 핸들링 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=MYOujE9Pc_c
cd client
npm install --save axios
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data);
})
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url, formData, config);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<h1>고객추가</h1>
프로필 이미지: <input type="file" name="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
이름: <input type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
생년월일: <input type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
성별: <input type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
직업: <input type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
<button type="submit">추가하기</button>
</form>
)
}
}
export default CustomerAdd;
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
// import { makeStyles } from '@mui/material';
// const styles = theme =>({
// const useStyles = makeStyles(theme => ({
// root: {
// width : '100%',
// marginTop: theme.spacing(3),
// // marginTop: theme.spacing.unit * 3,
// overflowX: "auto"
// },
// table:{
// minWidth:1080,
// }
// }));
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
<div>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
<CustomerAdd/>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
vscode-git
CustomerAdd Form
13강 - Node.js Express에서 파일 업로드 요청 처리 및 DB에 데이터 삽입 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=DOANhiuJ9e4&list=PLRx0vPvlEmdCED62ZIWCbI-6G_jcwmuFB&index=20
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data);
})
this.setState({
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
})
window.location.reload(); // 잠시 테스트 위해서
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url, formData, config);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<h1>고객추가</h1>
프로필 이미지: <input type="file" name="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
이름: <input type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
생년월일: <input type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
성별: <input type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
직업: <input type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
<button type="submit">추가하기</button>
</form>
)
}
}
export default CustomerAdd;
npm install --save multer
server.js--------------------
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
const cors = require('cors');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
const data = fs.readFileSync('./database.json');
const conf = JSON.parse(data);
const mysql = require('mysql');
// console.log(conf.host);
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
database: conf.database
});
connection.connect();
const multer = require('multer');
const exp = require('constants');
const upload = multer({dest: './upload'});
app.get('/api/customers', (req, res) => {
connection.query(
"SELECT * FROM customer",
(err, rows, fields) => {
res.send(rows);
}
);
});
// app.use(cors());
// app.use(express.json());
app.use("/image", express.static("./upload"));
app.post('/api/customers', upload.single('image'), (req, res) => {
let sql = 'INSERT INTO CUSTOMER (id,image,name,birthday,gender,job) VALUES (null,?, ?, ?, ?, ?)';
let image = '/image/' + req.file.filename;
let name = req.body.name;
let birthday = req.body.birthday;
let gender = req.body.gender;
let job = req.body.job;
let params = [image, name, birthday, gender, job];
// console.log(params);
connection.query(sql, params, (err, rows, fields) => {
res.send(rows);
console.log(err);
console.log(rows);
// console.log("입력완료***********************************************")
})
});
app.listen(port, () => console.log(`Listening on port ${port}`));
upload 폴더 생성
.gitignore--------------------
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# upload
/upload
# database
/database.json
# 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*
yarn dev
14강 - 부모 컴포넌트의 상태(State) 변경을 통한 고객 정보 갱신 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=qu-AkdJ5ED4&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=14
고객목록부분만 새로고침
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
class App extends Component {
constructor(props) {
super(props);
this.state = {
customers: "",
completed: 0
}
}
// state = {
// customers: "",
// completed: 0
// }
stateRefresh = () => {
this.setState({
customers: '',
completed: 0
});
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
<div>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
<CustomerAdd stateRefresh={this.stateRefresh}/>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data,"추가하기누름");
this.props.stateRefresh();
})
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
})
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
// const formData = new URLSearchParams();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post.post(url, formData, config);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<h1>고객추가</h1>
프로필 이미지: <input type="file" name="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
이름: <input type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
생년월일: <input type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
성별: <input type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
직업: <input type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
<button type="submit">추가하기</button>
</form>
)
}
}
export default CustomerAdd;
vscode-git
15강 - 고객(Customer) 정보 삭제 기능 구현하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=WiLEzh3KoFw&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=15
use management;
alter table customer add createDate datetime;
alter table customer add isDeleted int;
select * from customer;
update customer set createDate = now();
만약에 안되면
https://lightblog.tistory.com/193
[MYSQL] 에러 번호 1175 Safe Update 해결방법
MySQL에서 쿼리를 실행하다보면 다음과 같은 에러를 마주할 때가 있다. Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in
lightblog.tistory.com
update customer set isDeleted = 0;
select * from customer;
테이블 속성확인
desc customer;
수정완료==========================
server.js--------------------
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
const cors = require('cors');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
const data = fs.readFileSync('./database.json');
const conf = JSON.parse(data);
const mysql = require('mysql');
// console.log(conf.host);
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
database: conf.database
});
connection.connect();
const multer = require('multer');
const exp = require('constants');
const upload = multer({dest: './upload'});
app.get('/api/customers', (req, res) => {
connection.query(
"SELECT * FROM customer",
(err, rows, fields) => {
res.send(rows);
}
);
});
// app.use(cors());
// app.use(express.json());
app.use("/image", express.static("./upload"));
app.post('/api/customers', upload.single('image'), (req, res) => {
let sql = 'INSERT INTO CUSTOMER (id,image,name,birthday,gender,job) VALUES (null,?, ?, ?, ?, ?)';
let image = '/image/' + req.file.filename;
let name = req.body.name;
let birthday = req.body.birthday;
let gender = req.body.gender;
let job = req.body.job;
let params = [image, name, birthday, gender, job];
// let params = [name, birthday, gender, job];
// console.log(params);
connection.query(sql, params, (err, rows, fields) => {
res.send(rows);
// console.log("입력완료***********************************************")
})
});
app.listen(port, () => console.log(`Listening on port ${port}`));
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
class App extends Component {
state = {
customers: "",
completed: 0
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
<div>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => { return ( <Customer key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
<CustomerAdd/>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data,"추가하기누름");
})
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
})
window.location.reload();
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
// const formData = new URLSearchParams();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post.post(url, formData, config);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<h1>고객추가</h1>
프로필 이미지: <input type="file" name="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
이름: <input type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
생년월일: <input type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
성별: <input type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
직업: <input type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
<button type="submit">추가하기</button>
</form>
)
}
}
export default CustomerAdd;
yarn dev
==========
client-src-components-CustomerDelete.js--------------------
import React, { Component } from 'react';
class CustomerDelete extends Component {
deleteCustomer(id){
const url = '/api/customers/' + id;
fetch(url, {
method: 'DELETE'
});
this.props.stateRefresh();
}
render() {
return (
<button onClick={(e) => {this.deleteCustomer(this.props.id)}}>
삭제
</button>
);
}
}
export default CustomerDelete;
client-src-components-Customer.js--------------------
import React from "react";
import { TableRow } from "@mui/material";
import { TableCell } from "@mui/material";
import CustomerDelete from "./CustomerDelete";
class Customer extends React.Component {
render() {
return (
<TableRow>
<TableCell>{this.props.id}</TableCell>
<TableCell><img src={this.props.image} alt="profile" style={{width:64 ,height: 64}}></img></TableCell>
<TableCell>{this.props.name}</TableCell>
<TableCell>{this.props.birthday}</TableCell>
<TableCell>{this.props.gender}</TableCell>
<TableCell>{this.props.job}</TableCell>
<TableCell><CustomerDelete stateRefresh={this.props.stateRefresh} id={this.props.id}/></TableCell>
</TableRow>
)
}
}
export default Customer;
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
class App extends Component {
constructor(props) {
super(props);
this.state = {
customers: "",
completed: 0
}
}
// state = {
// customers: "",
// completed: 0
// }
stateRefresh = () => {
this.setState({
customers: '',
completed: 0
});
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
return (
<div>
<Paper sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
<TableCell>설정</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => {
return ( <Customer stateRefresh={this.stateRefresh} key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
<CustomerAdd stateRefresh={this.stateRefresh}/>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
server.js--------------------
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 5000;
const cors = require('cors');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
const data = fs.readFileSync('./database.json');
const conf = JSON.parse(data);
const mysql = require('mysql');
// console.log(conf.host);
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
database: conf.database
});
connection.connect();
const multer = require('multer');
const exp = require('constants');
const upload = multer({dest: './upload'});
app.get('/api/customers', (req, res) => {
connection.query(
"SELECT * FROM customer WHERE isDeleted = 0",
(err, rows, fields) => {
res.send(rows);
}
);
});
// app.use(cors());
// app.use(express.json());
app.use("/image", express.static("./upload"));
app.post('/api/customers', upload.single('image'), (req, res) => {
// let sql = 'INSERT INTO CUSTOMER (id,image,name,birthday,gender,job,createDate,isDeleted) VALUES (null,?, ?, ?, ?, ?, now(),0)';
let sql = 'INSERT INTO CUSTOMER VALUES (null,?, ?, ?, ?, ?, now(),0)';
let image = '/image/' + req.file.filename;
let name = req.body.name;
let birthday = req.body.birthday;
let gender = req.body.gender;
let job = req.body.job;
let params = [image, name, birthday, gender, job];
// console.log(params);
connection.query(sql, params, (err, rows, fields) => {
res.send(rows);
console.log(err);
console.log(rows);
// console.log("입력완료***********************************************")
})
});
app.delete('/api/customers/:id', (req, res) => {
let sql = "UPDATE CUSTOMER SET isDeleted = 1 WHERE id = ?";
let params = [req.params.id];
connection.query(sql, params,
(err, rows, fields) => {
res.send(rows);
});
})
app.listen(port, () => console.log(`Listening on port ${port}`));
vscode-git
16강 - Material UI 모달(Modal) 디자인 구현하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=TCoyPmY2sns&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=16
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
import { Dialog } from '@mui/material';
import { DialogActions } from '@mui/material';
import { DialogTitle } from '@mui/material';
import { DialogContent } from '@mui/material';
import { TextField } from '@mui/material';
import { Button } from '@mui/material';
// import { withStyles } from '@material-ui/core/style';
const styles = theme => ({
hidden: {
display: 'none'
}
});
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: '',
open: false
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data,"추가하기누름");
this.props.stateRefresh();
})
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: ''
})
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
// const formData = new URLSearchParams();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post.post(url, formData, config);
}
handleClickopen = () => {
this.setState({
open: true
});
}
handleClose = () => {
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: '',
open: false
})
}
render() {
const { classed } = this.props;
return (
<div>
<Button variant="contained" color="primary" onClick={this.handleClickopen}>
고객 추가하기
</Button>
<Dialog open={this.state.open} onClose={this.handleClose}>
<DialogTitle>고객 추가</DialogTitle>
<DialogContent>
{/* <input className={classes.hidden} accept="image/*" id="raised-button-file" type="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/> */}
<input accept="image/*" id="raised-button-file" type="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
<label htmlFor="raised-button-file">
<Button variant="contained" color="primary" component="span" name="file">
{this.state.fileName === "" ? "프로필 이미지 선택" : this.state.fileName}
</Button>
</label>
<TextField label="이름" type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
<TextField label="생일" type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
<TextField label="성별" type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
<TextField label="직업" type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
</DialogContent>
</Dialog>
</div>
)
}
}
export default CustomerAdd;
client-src-components-CustomerAdd.js--------------------
import React from "react";
import post from "axios";
import { Dialog } from '@mui/material';
import { DialogActions } from '@mui/material';
import { DialogTitle } from '@mui/material';
import { DialogContent } from '@mui/material';
import { TextField } from '@mui/material';
import { Button } from '@mui/material';
// import { withStyles } from '@material-ui/core/style';
const styles = theme => ({
hidden: {
display: 'none'
}
});
class CustomerAdd extends React.Component {
constructor(props){
super(props);
this.state = {
file: null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: '',
open: false
}
}
handleFormSubmit = (e) => {
e.preventDefault()
this.addCustomer()
.then((response) => {
console.log(response.data,"추가하기누름");
this.props.stateRefresh();
})
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: '',
open: false
})
}
handleFileChange = (e) => {
this.setState({
file: e.target.files[0],
fileName: e.target.value
})
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
addCustomer = () => {
const url = '/api/customers';
const formData = new FormData();
// const formData = new URLSearchParams();
formData.append('image', this.state.file);
formData.append('name', this.state.userName);
formData.append('birthday', this.state.birthday);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post.post(url, formData, config);
}
handleClickopen = () => {
this.setState({
open: true
});
}
handleClose = () => {
this.setState({
file:null,
userName: '',
birthday: '',
gender: '',
job: '',
fileName: '',
open: false
})
}
render() {
const { classed } = this.props;
return (
<div>
<Button variant="contained" color="primary" onClick={this.handleClickopen}>
고객 추가하기
</Button>
<Dialog open={this.state.open} onClose={this.handleClose}>
<DialogTitle>고객 추가</DialogTitle>
<DialogContent>
{/* <input className={classes.hidden} accept="image/*" id="raised-button-file" type="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/> */}
<input accept="image/*" id="raised-button-file" type="file" file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}/><br/>
<label htmlFor="raised-button-file">
<Button variant="contained" color="primary" component="span" name="file">
{this.state.fileName === "" ? "프로필 이미지 선택" : this.state.fileName}
</Button>
</label>
<br/>
<TextField label="이름" type="text" name="userName" value={this.state.userName} onChange={this.handleValueChange}/><br/>
<TextField label="생일" type="text" name="birthday" value={this.state.birthday} onChange={this.handleValueChange}/><br/>
<TextField label="성별" type="text" name="gender" value={this.state.gender} onChange={this.handleValueChange}/><br/>
<TextField label="직업" type="text" name="job" value={this.state.job} onChange={this.handleValueChange}/><br/>
</DialogContent>
<DialogActions>
<Button variant="contained" color="primary" onClick={this.handleFormSubmit}>추가</Button>
<Button variant="outlined" color="primary" onClick={this.handleClose}>닫기</Button>
</DialogActions>
</Dialog>
</div>
)
}
}
export default CustomerAdd;
client-src-components-CustomerDelete.js--------------------
import React, { Component } from 'react';
import { Dialog } from '@mui/material';
import { DialogActions } from '@mui/material';
import { DialogTitle } from '@mui/material';
import { DialogContent } from '@mui/material';
import { Button } from '@mui/material';
import { Typography } from '@mui/material';
class CustomerDelete extends Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
deleteCustomer(id){
const url = '/api/customers/' + id;
fetch(url, {
method: 'DELETE'
});
this.props.stateRefresh();
}
handleClickopen = () => {
this.setState({
open: true
});
}
handleClose = () => {
this.setState({
open: false
})
}
render() {
return (
<div>
<Button variant='contained' color='secondary' onClick={this.handleClickopen}>삭제</Button>
<Dialog open={this.state.open} onClose={this.handleClose}>
<DialogTitle onClose={this.handleClose}>
삭제 경고
</DialogTitle>
<DialogContent>
<Typography gutterBottom>
선택한 고객 정보가 삭제됩니다.
</Typography>
</DialogContent>
<DialogActions>
<Button variant='contained' color='primary' onClick={(e) => {this.deleteCustomer(this.props.id)}}>삭제</Button>
<Button variant='outlined' color='primary' onClick={this.handleClose}>닫기</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
export default CustomerDelete;
17강 - AppBar 및 웹 폰트를 적용하여 디자인 개편하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=9rJmH_WRyLY&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=17
cd client
npm install --save @mui/icons-material
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
import { styled, alpha } from '@mui/material/styles';
import { AppBar } from '@mui/material';
import { Toolbar } from '@mui/material';
import { IconButton } from '@mui/material';
import { Typography } from '@mui/material';
import { InputBase } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import SearchIcon from '@mui/icons-material/Search';
const styles = theme => ({
root: {
width: '100%',
marginTop: theme.spacing.unit * 3,
overflowX: "auto"
},
table: {
minWidth: 1080
},
progress: {
margin: theme.spacing.unit * 2
},
});
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(1),
width: 'auto',
},
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: '12ch',
'&:focus': {
width: '20ch',
},
},
},
}));
class App extends Component {
constructor(props) {
super(props);
this.state = {
customers: "",
completed: 0
}
}
// state = {
// customers: "",
// completed: 0
// }
stateRefresh = () => {
this.setState({
customers: '',
completed: 0
});
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
render() {
// const { classes } = this.props;
const cellList = ['번호','프로필 이미지','이름','생년월일','성별','직업','설정'];
return (
// <div sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<div>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
>
고객 관리 시스템
</Typography>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
placeholder="검색하기"
inputProps={{ 'aria-label': 'search' }}
/>
</Search>
</Toolbar>
</AppBar>
<div
sx={{ marginTop: 15, marginBottom: 15, display: 'flex', justifyContent: 'center'}}
>
<CustomerAdd stateRefresh={this.stateRefresh}/>
</div>
<Paper >
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
{/* <TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
<TableCell>설정</TableCell> */}
{cellList.map(c => {
return <TableCell>{c}</TableCell>
})}
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ? this.state.customers.map(c => {
return ( <Customer stateRefresh={this.stateRefresh} key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />);
}) :
<TableRow>
<TableCell colSpan="6" align="center">
{/* <CircularProgress sx={{m: 2}} variant="determinate" value={this.state.completed}/> */}
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
client-src-index.css--------------------
@import url(http://fonts.googleapis.com/earlyaccess/notosanskr.css);
body {
margin: 0;
font-family: "Noto Sans KR", -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;
}
테마는 적용하지 않았다
18강 - 필터(Filter) 함수를 이용한 고객(Customer) 검색 기능 구현하기 [React와 Node.js를 활용한 고객 관리 시스템 개발 강의]
https://www.youtube.com/watch?v=0lksi4SN9Uc&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=18
client-src-App.js--------------------
import React, { Component } from 'react';
import Customer from './components/Customer';
import CustomerAdd from './components/CustomerAdd';
import './App.css';
import { Paper } from '@mui/material'
import { Table } from '@mui/material';
import { TableHead } from '@mui/material';
import { TableRow } from '@mui/material';
import { TableCell } from '@mui/material';
import { TableBody } from '@mui/material';
import { CircularProgress } from '@mui/material';
import { styled, alpha } from '@mui/material/styles';
import { AppBar } from '@mui/material';
import { Toolbar } from '@mui/material';
import { IconButton } from '@mui/material';
import { Typography } from '@mui/material';
import { InputBase } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import SearchIcon from '@mui/icons-material/Search';
const styles = theme => ({
root: {
width: '100%',
marginTop: theme.spacing.unit * 3,
overflowX: "auto"
},
table: {
minWidth: 1080
},
progress: {
margin: theme.spacing.unit * 2
},
});
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(1),
width: 'auto',
},
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: '12ch',
'&:focus': {
width: '20ch',
},
},
},
}));
class App extends Component {
constructor(props) {
super(props);
this.state = {
customers: "",
completed: 0,
searchKeyword: ''
}
}
// state = {
// customers: "",
// completed: 0
// }
stateRefresh = () => {
this.setState({
customers: '',
completed: 0,
searchKeyword: ''
});
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
componentDidMount() {
this.timer = setInterval(this.progress, 20);
this.callApi()
.then(res => this.setState({customers: res}))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/customers');
const body = await response.json();
return body;
}
progress = () => {
const { completed } = this.state;
this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
}
handleValueChange = (e) => {
let nextState = {}
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
render() {
const filteredComponents = (data) => {
data = data.filter((c) => {
return c.name.indexOf(this.state.searchKeyword) > -1;
});
return data.map((c) => {
return <Customer stateRefresh={this.stateRefresh} key={c.id} id={c.id} image={c.image} name={c.name} birthday={c.birthday} gender={c.gender} job={c.job} />
})
}
// const { classes } = this.props;
const cellList = ['번호','프로필 이미지','이름','생년월일','성별','직업','설정'];
return (
// <div sx={{width : '100%', mt: 3, overflowX: "auto"}}>
<div>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
>
고객 관리 시스템
</Typography>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
placeholder="검색하기"
inputProps={{ 'aria-label': 'search' }}
name="searchKeyword"
value={this.state.searchKeyword}
onChange={this.handleValueChange}
/>
</Search>
</Toolbar>
</AppBar>
<div
sx={{ marginTop: 15, marginBottom: 15, display: 'flex', justifyContent: 'center'}}
>
<CustomerAdd stateRefresh={this.stateRefresh}/>
</div>
<Paper >
<Table sx={{minWidth: '1080px'}}>
<TableHead>
<TableRow>
{/* <TableCell>번호</TableCell>
<TableCell>이미지</TableCell>
<TableCell>이름</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>직업</TableCell>
<TableCell>설정</TableCell> */}
{cellList.map(c => {
return <TableCell>{c}</TableCell>
})}
</TableRow>
</TableHead>
<TableBody>
{this.state.customers ?
filteredComponents(this.state.customers) :
<TableRow>
<TableCell colSpan="6" align="center">
<CircularProgress sx={{m: 2}} value={this.state.completed}/>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
</div>
);
}
}
// export default withStyles(styles)(App);
export default App;
vscode-git
'컴퓨터 > react' 카테고리의 다른 글
react mysql (0) | 2023.04.28 |
---|---|
React egoing (0) | 2023.04.20 |
react Module not found: Error: Can't resolve './serviceWorker' in (0) | 2022.07.04 |
react vscode 첫번째 import 빨간줄 (0) | 2022.05.28 |
react javascript reduce map (0) | 2022.05.12 |