안경잡이개발자

리액트 강좌 +21
728x90
반응형

  이번 시간에는 고객(Customer) 정보를 삭제하는 기능을 구현하는 시간을 가져보도록 하겠습니다. 특정한 데이터의 삭제 기능을 구현하는 방법은 굉장히 다양합니다. 몇 가지 예제를 살펴보자면 ① 삭제를 수행했을 때 실제 테이블에서 데이터를 삭제하는 방식이 있으며 ② 삭제를 수행했을 때 삭제 체크만 한 뒤에 실제 데이터베이스에는 남겨 놓는 방식이 있습니다.


  우리는 두 번째 방식으로 구현하겠습니다. 따라서 Customer 테이블의 정보부터 수정하도록 하겠습니다.


USE management;

ALTER TABLE CUSTOMER ADD createdDate DATETIME;

ALTER TABLE CUSTOMER ADD isDeleted INT;


  또한 기본적으로 현재 들어있던 데이터에는 새로운 속성(Property)에 대한 데이터가 없으므로 이를 채워넣어줄 수 있도록 합니다.


USE management;

UPDATE CUSTOMER SET createdDate = now();

UPDATE CUSTOMER SET isDeleted = 0;


  위와 같이 MySQL 테이블 설정 명령어를 수행한 이후에 DESC 명령어로 테이블(Table) 정보를 확인해 보시면 다음과 같습니다.



  따라서 모든 데이터는 방금 생성이 된 것으로 처리 되었고, 현재 삭제 되지 않은 상태로 남아있게 되었습니다. 이제 새롭게 CustomerDelete.js를 생성해주도록 합니다.



▶ CustomerDelete.js


import React from 'react';

class CustomerDelete extends React.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;


▶ Customer.js


  이제 위에서 만든 Customer Delete 뷰(View)를 한 명의 고객 정보를 출력할 때 함께 보여줄 수 있도록 처리하면 됩니다.


import React from 'react';
import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
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"/></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;


▶ App.js


  이후에 다음과 같이 App.js에서 각 고객 컴포넌트에게 stateRefresh 함수를 넘겨 줄 수 있도록 합니다.


<div>
<Paper className={classes.root}>
<Table className={classes.table}>
<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 className={classes.progress} variant="determinate" value={this.state.completed} />
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</Paper>
<CustomerAdd stateRefresh={this.stateRefresh} />
</div>


▶ server.js


  이제 실제로 Express 서버에 고객 데이터 삭제 모듈을 만들어주면 됩니다. 이 때 기존에 존재하던 고객 정보 삽입 모듈과 고객 목록 불러오기 모듈도 조금씩 바꾸어주시면 됩니다.


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');

const multer = require('multer');
const upload = multer({dest: './upload'})

const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
port: conf.port,
database: conf.database
});
connection.connect();

app.get('/api/customers', (req, res) => {
connection.query(
'SELECT * FROM CUSTOMER WHERE isDeleted = 0',
(err, rows, fields) => {
res.send(rows);
}
)
});

app.use('/image', express.static('./upload'));

app.post('/api/customers', upload.single('image'), (req, res) => {
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];
connection.query(sql, params,
(err, rows, fields) => {
res.send(rows);
}
)
});

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}`));


  실행 결과는 다음과 같습니다. 다음과 같이 가장 오른쪽에 [삭제] 버튼이 추가된 것을 확인할 수 있습니다.



  그래서 결과적으로 다음과 같이 삭제 요청(Request)이 전송되는 것을 확인할 수 있습니다. REST API의 성격에 맞게 DELETE 메소드(Method)로 삭제 요청이 보내졌으며 성공적으로 삭제가 완료된 것입니다.



  결과적으로 다음과 같이 고객 목록이 갱신된 것을 확인할 수 있습니다.




728x90
반응형

728x90
반응형

  지난 시간까지 작성되었던 소스코드 상으로는 새로운 고객(Customer) 정보가 추가되면 페이지 새로고침(Refresh)를 통해서 새롭게 등록된 고객에 대한 정보를 확인할 수 있습니다. 하지만 기본적으로 리액트(React)는 SPA(Single Page Application)의 구조로 동작합니다. 그러므로 전체 페이지를 새로고침하는 것은 비효율적이며 고객 추가(Customer Add) 컴포넌트에서 부모 컴포넌트의 상태(State)를 변경하는 식으로 필요한 부분만 새로고침 되도록 설정하면 됩니다.


  이 때는 기본적으로 부모 컴포넌트에서 자식 컴포넌트로 함수를 props로 건내주는 방식으로 구현합니다.


전체 고객 목록 다시 불러오기 ※


  고객 목록 추가 이후에 새로고침 하는 과정을 구현하는 가장 대표적인 방법은 전체 고객 목록을 다시 불러옴으로써 State를 갱신하는 방식입니다. App.js에 있던 기존의 state 초기화 변수를 제거해주신 뒤에, 윗 부분을 다음과 같은 소스코드로 채우시면 됩니다.


class App extends Component {

constructor(props) {

super(props);

this.state = {

customers: '',

completed: 0

}

this.stateRefresh = this.stateRefresh.bind(this);

}


stateRefresh() {

this.setState({

customers: '',

completed: 0

});

this.callApi()

.then(res => this.setState({customers: res}))

.catch(err => console.log(err));

}


  이후에 Customer Add 컴포넌트로 stateRefresh() 함수를 보내주시면 돼요. 그러면 Customer Add 컴포넌트가 해당 함수를 호출해서 부모 컴포넌트인 App 컴포넌트의 State를 갱신하게 됩니다.


<CustomerAdd stateRefresh={this.stateRefresh} />


  마지막으로 CustomerAdd.js로 가서 window.location.reload() 함수를 제거하고 다음의 구문을 넣습니다.


this.props.stateRefresh();


  다만 고객 목록 데이터를 불러오는 과정은 비동기적으로 수행된다는 점에서 항상 고객 데이터를 추가(Add)한 이후에 고객 목록 데이터를 불러온다는 것을 보장하지 못합니다. 따라서 다음과 같이 고객 추가 이후에 서버로부터 응답을 받고 나서 비로소 고객 목록을 다시 불러오도록 설정해야 합니다.


handleFormSubmit(e) {

e.preventDefault()

this.addCustomer()

.then((response) => {

console.log(response.data);

this.props.stateRefresh();

})

this.setState({

file: null,

userName: '',

birthday: '',

gender: '',

job: '',

fileName: ''

})

}


  이제 테스트를 진행 해보시면 다음과 같이 정상적으로 동작합니다.



  추가 이후에는 전체 페이지 새로고침 없이 다음과 같이 고객 목록이 출력됩니다. 또한 고객 추가 창은 비워지게 됩니다.



※ 고객 정보 갱신 방법에 대해서 ※


  위에서 구현한 방식은 고객 데이터가 많을 때에는 매 번 새로고침을 거치는 과정에서 리소스를 비효율적으로 사용하게 됩니다. 그래서 실제로 상용 서비스에 적용할 때는 최근 10개의 고객 목록만 가져오도록 하여, 이후의 고객 정보에 대해서는 스크롤을 통해 새롭게 불러오는 식으로 구현할 수 있습니다.


  물론 이렇게 만든 고객 관리 시스템을 자기 혼자 사용하는 경우에는 매 번 서버와 통신하지 않아도 됩니다. 고객을 추가한 이후에 자신의 화면에서 바로 추가된 고객 정보만을 추가적으로 보여주면 됩니다.


  하지만 일반적인 고객 관리 시스템은 여러 명의 관리자가 동시에 작업할 수 있다는 점에서 현재의 방법이 더 효율적일 수 있습니다.

728x90
반응형

728x90
반응형

  기본적으로 리액트(React) 요소에 대한 이벤트 핸들링은 기본적인 자바스크립트에서의 방식과 매우 흡사합니다. 다만 리액트 이벤트는 카멜 케이스(Camel Case) 네이밍 규칙을 사용한다는 특징이 있으며 JSX 문법을 사용해 함수를 호출할 수 있습니다.


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')

);



  이후에 가장 대표적인 예제인 버튼 토글 예제에 대해서 알아보도록 하겠습니다.


class EventHandling extends React.Component {

  constructor(props) {

    super(props);

    this.state = {

      isToggleOn: true

    }

    this.handleClick = this.handleClick.bind(this);

  }

  

  handleClick() {

    this.setState(state => ({

      isToggleOn: !state.isToggleOn

    }))

  }

  

  render() {

    return (

      <button onClick={this.handleClick}>

        {this.state.isToggleOn ? 'ON' : 'OFF'}

      </button>

    );

  }

  

}


ReactDOM.render(

  <EventHandling />, 

  document.getElementById('root')

);



  자바스크립트(JavaScript)에서는 바인딩(Binding)이 기본 설정으로 제공되지 않습니다. 그래서 만약 바인딩 처리를 해주는 것을 잊으면 소스코드가 정상적으로 동작하지 않습니다

728x90
반응형

'리액트(React)' 카테고리의 다른 글

React의 LifeCycle과 API 호출  (3) 2019.01.04
React의 State  (0) 2018.12.25
React의 Component와 Props  (0) 2018.12.25
React에서 JSX란?  (0) 2018.12.25
코드펜(Codepen)을 이용한 React 개발환경 구축하기  (0) 2018.12.25

728x90
반응형

  React에서 Form을 처리하기 위해서는 이벤트 처리(Event Handling)를 수행해야 합니다. 이번 시간에는 그러한 이벤트 처리 방법에 대해서 이해하고, 결과적으로 서버로 고객 데이터를 전송하여 데이터베이스에 신규 고객 정보를 등록하는 방법까지 알아보도록 하겠습니다. 따라서 가장 먼저 React 클라이언트의 components 폴더에 CustomerAdd.js를 만들어 보도록 하겠습니다.



  이제 디자인을 입히지 않은 상태로 하나의 <form> 태그를 작업하는 시간을 가져보도록 하겠습니다. 이를 위해 가장 먼저 서버와의 통신 목적의 라이브러리인 axios를 설치해주도록 하겠습니다. client 폴더로 이동하여 npm install --save axios 명령어를 입력하시면 됩니다. 이제 CustomerAdd.js 컴포넌트를 작업해 줍니다.


▶ 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: ''

}

this.handleFormSubmit = this.handleFormSubmit.bind(this)

this.handleFileChange = this.handleFileChange.bind(this)

this.handleValueChange = this.handleValueChange.bind(this)

this.addCustomer = this.addCustomer.bind(this)

}


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


▶ App.js


  이제 작업한 CustomerAdd 컴포넌트를 화면에 출력해 보도록 하겠습니다. 따라서 App.js를 수정해 봅시다.


import CustomerAdd from './components/CustomerAdd';


  위와 같이 CustomerAdd 컴포넌트를 추가해주신 이후에 다음과 같이 렌더링 되는 부분을 수정합니다.


<div>

<Paper className={classes.root}>

<Table className={classes.table}>

<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 className={classes.progress} variant="determinate" value={this.state.completed} />

</TableCell>

</TableRow>

}

</TableBody>

</Table>

</Paper>

<CustomerAdd/>

</div>


  실제로 이를 브라우저의 개발자 도구에서 테스트를 해보시면 서버로 데이터를 전송하는 것을 알 수 있습니다.



  아래쪽에 고객 추가 양식이 존재하며 여기에 데이터를 입력하여 실제로 고객을 추가할 수 있습니다.



  데이터를 입력해 [추가하기] 버튼을 누른 결과 다음과 같이 서버로 데이터가 전송되는 것을 알 수 있습니다. 실제로 이미지 파일은 바이너리 코드 형태로 서버로 전달됩니다. 따라서 다음 시간에 이러한 사용자의 요청 데이터를 적절히 처리하여 데이터베이스에 등록한 뒤에 그에 맞는 응답을 보내주는 작업을 수행해야 하빈다.





728x90
반응형

728x90
반응형

  흔히 소프트웨어에서는 프로그레스 바(Progress Bar)를 구현하여 API 로딩 메시지를 웹 사이트 상에 출력할 수 있습니다. 이번 시간에는 고객 목록을 불러오는 API를 요청했을 때 서버에서 응답을 늦게 하는 경우 로딩 메시지를 화면에 띄워주는 방법에 대해서 알아보도록 하겠습니다. 프로그레스 바 UI 또한 Material UI에 이미 구현이 되어 있습니다. 따라서 이를 단순히 사용해주면 됩니다.


  ▶ Material UI 프로그레스 바: https://material-ui.com/demos/progress/


  우리는 이 예제 중에서 [Determinate]을 선택해 구현해 보도록 하겠습니다.



※ React의 라이프 사이클(Life Cycle) ※


  기본적으로 리액트 라이브러리가 처음 컴포넌트를 실행할 때는 다음의 순서를 따릅니다.


1) constructor()

2) componentWillMount()

3) render()

4) componentDidMount()


  그리고 컴포넌트의 props 혹은 state가 변경될 때는 shouldComponentUpdate() 함수 등이 사용되어 실질적으로 다시 render() 함수를 불러와 뷰(View)를 갱신하게 됩니다. 또한 컴포넌트가 삭제될 때는 componentWillUnmount() 함수가 실행된다는 특징이 있습니다.


  따라서 일반적으로 API를 불러와서 웹 사이트 화면에 특정한 뷰(View)를 출력하고자 한다면 componentDidMount() 함수에서 API를 비동기적으로 호출하면 됩니다. 이후에 API에서 응답(Response)이 돌아왔을 때 비로소 뷰(View)가 갱신되므로 화면에 API 응답 결과를 출력할 수 있는 것입니다. 비동기적으로 호출한다는 점에서 API 서버에서 응답을 하지 않으면 사용자에게 로딩 화면만 출력이 됩니다.


▶ App.js


  따라서 Circular Progress 라이브러리를 이용하여 프로그레스 바를 API 로딩 메시지 용도로 사용하도록 하겠습니다.


import React, { Component } from 'react';

import Customer from './components/Customer'

import './App.css';

import Paper from '@material-ui/core/Paper';

import Table from '@material-ui/core/Table';

import TableHead from '@material-ui/core/TableHead';

import TableBody from '@material-ui/core/TableBody';

import TableRow from '@material-ui/core/TableRow';

import TableCell from '@material-ui/core/TableCell';

import CircularProgress from '@material-ui/core/CircularProgress';

import { withStyles } from '@material-ui/core/styles';


const styles = theme => ({

root: {

width: "100%",

marginTop: theme.spacing.unit * 3,

overflowX: "auto"

},

table: {

minWidth: 1080

},

progress: {

margin: theme.spacing.unit * 2

}

});


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));

}


componentWillUnmount() {

clearInterval(this.timer);

}


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}>

<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 className={classes.progress} variant="determinate" value={this.state.completed} />

</TableCell>

</TableRow>

}

</TableBody>

</Table>

</Paper>

);

}

}


export default withStyles(styles)(App);


※ 실행 결과 ※


  API를 불러올 때의 화면입니다.



API가 불러와 졌을 때의 결과 화면입니다.



(+ 추가)


  효과적인 로딩 테스트를 위해서는 서버 API의 지연을 발생시키는 것이 좋습니다. 서버에서 응답(Response) 데이터를 보내주는 코드에 Timeout을 적용해 보세요. setTimeout(function() { 소스 코드 }, 지연 시간)의 문법으로 간단히 사용해 볼 수 있습니다.


728x90
반응형

728x90
반응형

  이번 시간에는 지난 시간에 설치한 Node.js Express를 활용하여 REST API를 구축하는 방법에 대해서 알아보도록 하겠습니다. REST API는 최근 상당수의 웹 서버 프레임워크에서 기본적으로 지원하는 기능으로 서버와 클라이언트가 웹 프로토콜을 기반으로 하여 효과적으로 데이터를 주고 받을 수 있도록 해줍니다. 이를 실습하기 위해 가장 먼저 기존의 서버 모듈에 전체 고객 목록을 불러오는 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': '960508',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '961127',
'gender': '남자',
'job': '디자이너'
}
]);
});

app.listen(port, () => console.log(`Listening on port ${port}`));


  우리가 만든 /api/customers 경로에 들어가 보면 다음과 같이 고객 목록이 출력됩니다.



  고객 목록 데이터가 정상인지 확인하기 위하여 JSON 검증 서비스를 이용해보도록 합시다.


▶ JSON 검증 서비스: https://jsonlint.com/


  그대로 결과 JSON 문서를 붙여넣기 해보면 다음과 같이 정상적인 데이터라는 것을 확인할 수 있습니다.



  이후에 클라이언트(Client)에서 해당 API에 접근하여 고객 목록을 비동기적으로 가져오도록 만들면 됩니다. 가장 먼저 5000번 포트를 API 서버로 이용하기 위해서 클라이언트(Client)의 package.json 파일에 다음의 문구를 추가하시면 됩니다.


  "proxy": "http://localhost:5000/"


  이제 app.js 파일을 작성하여 실제로 API 서버에 접근할 수 있도록 처리하시면 됩니다.


▶ app.js


  비동기적으로 API 요청 기능을 수행하기 위해서 async - await 구문을 사용했습니다. 서버로부터 JSON 데이터를 받아올 때까지는 테이블에 내용을 출력하지 않다가 데이터를 모두 받아왔을 때 비로소 테이블에 내용을 채우게 됩니다.


import React, { Component } from 'react';
import Customer from './components/Customer'
import './App.css';
import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table';
import TableHead from '@material-ui/core/TableHead';
import TableBody from '@material-ui/core/TableBody';
import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
import { withStyles } from '@material-ui/core/styles';

const styles = theme => ({
root: {
width: "100%",
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}>
<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);


  이후에 yarn dev 명령어를 이용해 서버를 구동시킬 수 있습니다.



728x90
반응형

728x90
반응형

  이번 시간에는 Node.js Express리액트(React) 클라이언트 서비스와 연동하는 방법에 대해서 알아보도록 하겠습니다. 우리는 이미 클라이언트(Client) 파트를 구축하여 개발하고 있는 상태이므로 이어서 서버(Server) 모듈을 개발하여 클라이언트와 연동시키면 됩니다. 다시 말해 Node.js Express를 이용해 서버 API를 개발해서 고객 관리 시스템의 백 엔드를 구축할 수 있는 것입니다.


  따라서 가장 먼저 우리의 프로젝트 루트 폴더에서 client라는 이름의 폴더를 생성합니다. 그리고 기존에 존재했던 모든 소스코드를 이 client 폴더 안으로 넣으시면 됩니다. 그러면 다음과 같이 루트 폴더와 client 폴더가 구성됩니다.




  이후에 Node.js Express 서버를 개발하기 전에 환경 설정을 진행해주도록 하겠습니다. 이를 위해 루트 폴더에 package.json을 생성합니다. package.json 파일에서는 client 폴더에서 클라이언트 모듈을 실행하고, 루트 폴더에서는 서버 모듈을 실행하도록 명시해주도록 하겠습니다.


▶ 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.18.3",
"express": "^4.16.4"
},
"devDependencies": {
"concurrently": "^4.0.1"
}
}


  이제 루트 폴더에 클라이언트와 동일한 형태의 .gitignore 파일을 위치시킵니다. 이 .gitignore 파일이 없으면 우리가 설치한 노드 모듈 또한 모두 깃 허브(Git Hub)에 올라가기 때문에 비효율적입니다. 이후에 루트 폴더를 기준으로 nodemon을 설치합니다.


▶ npm install -g nodemon

▶ yarn


  이제 서버 모듈 개발을 위해 루트 폴더에 server.js 파일을 생성합니다.


▶ 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 명령어로 서버를 구동시킬 수 있습니다.



※ GET 방식의 API 테스트하기 ※



728x90
반응형

728x90
반응형

  지난 시간에는 한 명의 고객(Customer)에 대한 정보를 담아 출력하는 Customer.js 컴포넌트를 만들어 보았습니다. 다만 한 명의 고객이 담고 있는 정보가 많아 다루기 어려울 때는 어떻게 할 수 있을까요? 바로 컴포넌트의 내용을 분리하는 것입니다. 컴포넌트의 내용을 잘게 분해하면 관리하기가 쉬워지며 생산성이 높아집니다. 따라서 이번 시간에는 고객 컴포넌트의 내용을 분리해 볼 거예요.


▶ 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;


▶ Customer.js


  Customer.js는 실질적으로 한 명의 고객에 대한 정보를 출력하는 역할을 수행합니다. 그래서 출력할 내용이 많으면 이를 적절히 구조화하여 관리하는 것이 중요합니다. 따라서 저는 기존의 Customer를 CustomerProfile(고객 프로필)과 CustomerInfo(고객 정보) 두 가지 요소로 구분하여 구조화하도록 해보겠습니다.


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;



  결과적으로 데이터가 계층적으로 구성되었습니다.


  ※ 출력할 데이터 개수 늘려보기 ※


  이제부터는 데이터의 개수를 늘려보도록 하겠습니다. 기본적으로 컴포넌트(Component)는 리액트 요소(React Element)를 반환하는 형태로 개발이 되어야 하는데, 리액트 요소는 JSX를 이용해 생성할 수 있습니다. JSX의 특징은 내부 데이터가 2개 이상이라면 반드시 <div>와 같은 태그로 감싸서 사용해야 한다는 점입니다.


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': '960508',
'gender': '남자',
'job': '프로그래머'
},
{
'id': 3,
'image': 'https://placeimg.com/64/64/3',
'name': '이순신',
'birthday': '961127',
'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;



  다만 위와 같이 작성하게 되면 소스코드가 지나치게 길어질 수 있습니다. 흔히 프로그래밍에서는 반복문을 이용해서 반복되는 소스코드를 줄일 수 있습니다. 바로 다음과 같이 작성하면 됩니다.


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;


  맵(Map) 문법은 파이썬(Python)에서의 문법과 정확히 동일합니다. 특정한 리스트(List)가 있을 때 해당 리스트의 각 원소에 특정한 문법을 적용한 결과 리스트를 반환합니다. 다시 말해서 customers 배열에 있는 모든 원소에 대한 Customer Element를 반환하는 것입니다. 쉽게 말해 그냥 반복문을 사용해 각 고객에 대한 정보를 출력하도록 명시한 것입니다.


  또한 추가적으로 알아두면 좋은 점은 맵(Map)을 이용해 다수의 정보를 출력할 때는 key라는 이름의 Props를 사용해야 한다는 점입니다. 이를 사용하지 않으면 자바스크립트 콘솔(Console)에 관련 오류가 출력됩니다.


  마지막으로 작업한 내용에 대해서 커밋(Commit)과 푸시를 진행해주시면 됩니다.




728x90
반응형

728x90
반응형

  리액트(React) 개발에 있어서 가장 효과적인 통합 개발 환경(IDE) 중 하나는 바로 비주얼 스튜디오 코드(Visual Studio Code)입니다. 일단 지난 시간에 만들었던 리액트 프로젝트를 실행(Start) 시켜주세요. 기본적으로 서버가 구동 중인 상태에서 소스코드를 수정하면 알아서, 수정된 내역에 맞게 다시 컴파일이 이루어지면서 실제 웹 서비스에 반영된다는 특징이 있습니다.



  이제 이 상태에서 비주얼 스튜디오 코드를 설치해서 프로젝트 소스코드를 비주얼 스튜디오로 열어 보도록 하겠습니다. 혹시 비주얼 스튜디오 코드가 설치되어 있지 않으신 분은 다음의 사이트에서 설치를 진행하시면 됩니다.


  ▶ 비주얼 스튜디오 코드(Visual Studio Code) 사이트: https://code.visualstudio.com/


  접속 이후에는 자신의 운영체제에 맞는 응용 프로그램으로 설치를 진행할 수 있습니다. 설치를 하실 때에는 기본 설정으로 설치를 진행해주셔도 별다른 어려움 없이 따라오실 수 있습니다.



  설치 이후에는 비주얼 스튜디오 코드를 실행해주세요. 이후에 [File] - [Open Folder] 자신의 리액트 프로젝트를 열어주세요.



  기본적으로 리액트(React)는 App.js라는 이름으로 메인 자바스크립트를 관리할 수 있습니다. 실제로 HTML 문서에서 <body> 태그에 해당하는 내용은 이 App.js가 채우게 됩니다. 따라서 한 번 이 App.js를 수정해 봅시다.



  저는 다음과 같이 내용을 대략 아무렇게나 바꾸어 보도록 하겠습니다.



  이후에 저장(Ctrl + S)을 해주시면 자동으로 소스코드가 컴파일 되어 다음과 같이 웹 사이트에서 수정 내역을 확인할 수 있습니다.



728x90
반응형

728x90
반응형

  React와 Node.js로 만드는 고객 관리 시스템 개발 강좌의 첫 번째 시간입니다. 이번 시간에는 Create React App을 이용해 리액트 프로젝트를 시작하는 방법에 대해서 소개하고자 합니다. CRA(Create React App)을 이용하면 별다른 환경 설정을 수행하지 않아도 매우 빠르고 간단하게 리액트 프로젝트를 생성할 수 있습니다. 이번 시간에는 빠르게 Create React App을 이용해 프로젝트를 구축해보도록 하겠습니다.


  기본적으로 리액트(React) 개발환경을 위해서는 node.js가 설치되어 있어야 합니다.


  ▶node.js 사이트: https://nodejs.org/


  저는 최신 버전의 노드인 11.6.0 버전을 설치했습니다.



  기본적인 설정으로 설치를 진행하시면 됩니다.



  설치 이후에는 node -v 명령어로 설치된 노드의 버전을 확인할 수 있습니다.



  node.js를 설치하면 패키지 매니저 도구 NPM이 자동으로 설치됩니다. 이를 이용해서 create-react-app를 설치하시면 됩니다.



  이제 특정한 폴더로 이동해서 React 프로젝트를 생성해주겠습니다. 명령어는 create-react-app {프로젝트 이름}입니다.


  리액트 앱을 만든 이후에는 해당 프로젝트 폴더로 이동하여 yarn start 명령어로 실행할 수 있습니다.



  실행 이후에는 다음과 같이 개발 서버가 구동 중인 것을 확인할 수 있습니다.



  기본 포트 번호는 3000번이므로 브라우저에서 localhost:3000으로 접속하시면 다음과 같이 리액트 앱의 내용이 출력됩니다.


728x90
반응형