서버와 통신하는 방식 중 한 가지로 http메서드를 사용하여 데이터를 요청/전송합니다. 기존에 많이 사용하던 SOAP API 의 경우 서비스 인터페이스를 이용해 서버에 접근했다면, REST는 인터넷 식별자(URI)와 HTTP 프로토콜을 이용해서 접근하는 것이 특징이며 사용법이 단순하여 높은 보안수준을 요구하는 작업이 아닐 경우 일반적으로 많이 선호되는 통신방법입니다 . 데이터포맷은 브라우저 호환성이 좋은 JSON을 사용하며 resource, method, message로 구성됩니다.
REST API 메소드
종류
용도
사용예시
사용예시 해석
GET
데이터 조회
GET/users/1
(users의 1번 데이터 조회)
POST
데이터 등록
POST/users
(users 등록)
PUT
데이터 수정
PUT/users/1
(users의 1번 데이터 수정)
DELETE
데이터 삭제
DELETE/users/1
(users의 1번 데이터 삭제)
요청한 Data를 React에서 관리하는 기본방법
useState, useEffect로 데이터를 로딩/저장하여 사용하며, 데이터요청 이후의 요청결과, 로딩상태, 에러처리가 관리대상이 됩니다.
이번에는 브라우저화면에서 특정(sample1.js- sample1s in DB) table의 전체데이터 조회, 키워드로 해당데이터 조회, 여러개 키워드로 조회하는 방법을 알아보겠습니다. (기본 다른 세팅은 이전 글에서 설명한 것으로 이미 준비되었다고 가정하고 진행하겠습니다.)
진행할 작업 내용:
사용할 데이터를 위한 state요소와 구성할 기본화면 준비
App.js에서 server로 데이터요청을 위한 코드 추가
server/server.js에서 각 요청url에 대한 처리코드 추가
데이터조회 테스트를 위한 기본준비
state에서 관리할 name, email, sample1List를 준비하고 render될 화면에서는 데이터목록이 있을 때와 없을 때의 코드를 넣어줍니다. 받아올 데이터는 배열형태로 전달되기 때문에 map메소드를 사용했으며 Search버튼과 ListAll버튼의 실행할 함수는 진행과정에서 정의해주겠습니다.
Op.or은 Sequelize에서 or연산자로 데이터를 조회하기 위한 메소드입니다. or(또는)이므로 name과 email중 한개만 입력해도 해당리스트가 조회됩니다.
그럼 서버를 재실행하여 브라우저에서 확인해보겠습니다.
한 개의 데이터만 조회 - findOne( where : {xxx : yyy} )
findOne은 where조건에 따라 table레코드를 한개만 가져오는 메소드입니다. findAll로 가져올 때랑 다른 점은 findAll의 경우 배열형태로 데이터목록을 담아보내지만 findOne은 객체형태로 보낸다는 점입니다. 그래서 App.js에서 setState를 할때 sample1List를 배열형태로 한번 감싸서 코드를 작성해줍니다.
다음 코드의 내용을 해석해보면 화면에 render되는 폼에서 각각의 input값이 변경될 때마다 _nameUpdate()와 _emailUpdate()가 호출되어 state에 준비된 name과 email의 값을 담아줍니다. 그리고 Add버튼을 클릭하면 _addData가 호출되어 state에 저장된 값을 post로 서버에 보내게 됩니다.
그리고나서 새로 추가될 테이블관계를 적용하기위해 생성한 sample1s와 sample2s를 db에서삭제합니다.
(정상적인 확인을 위해 새로운 관계를 적용할 때마다 먼저 테이블을 삭제해주세요.)
drop table sample1s, sample2s; // 관계표현 test준비 후
drop table sample1s cascade; // 1:1관계생성 test후
drop table sample2s;
drop table sample1s; // 1:N관계생성 test후
drop table reletion;
drop table sample1s;
drop table sample2s; // N:M관계생성 test후
1:1관계표현 - hasOne메소드
server/models/index.js의 테이블 추가코드 아래에 다음과 같은 코드를 입력합니다.
db.Sample1.hasOne(db.Sample2);
서버를 재실행시킨 후 터미널에서 추가된 테이블을 확인해보면 sample2s테이블에 sample1Id컬럼이 추가된 것을 볼 수 있습니다. sample1s의 id가 sample2s의 외래키로 등록되었습니다.
'use strict';
const path = require('path');
const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '..', 'config', 'db.json'))[ env ];
const db = {};
let sequelize = new Sequelize(
config.database,
config.username,
config.password,
config,
{
define: {
charset: 'utf8',
collate: 'utf8_general_ci'
}
}
);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.log('Unable to connect to the database: ', err);
});
db.Cstmr = require('./cstmr')(sequelize, Sequelize);
db.secret = '(9*)5$&!3%^0%^@@2$1!#5@2!4';
module.exports = db;
서버재시작하여 table생성여부 확인
터미널에 아래와 같은 실행문구가 출력됩니다.
DB를 확인해보면 다음과 같이 테이블이 생성되어 있는 것을 볼 수 있습니다. id는 따로 cstmr.js에 추가하지 않았지만 primary키로 자동지정되어 생성되어있습니다. 그리고 js파일에서 지정한 'cstmr'에 's'가 붙은 'cstmrs'로 테이블명이 지정되었습니다.
nodejs에서 mysql을 쉽게 다룰 수 있도록 도와주는 ORM(Object-Relational Mapping)방식의 라이브러리로서 ORM은 객체와 관계형DB의 관계를 매핑해주는 도구입니다. Sequelize를 사용하면 직접 쿼리를 날리지않고도 자바스크립트 코드로 mysql을 제어(DB테이블생성, select, insert, update, delete..) 할 수 있습니다.
Sequelize, mysql2, path 설치
Sequelize와 함께 기타 효율적인 작업을 위해 mysql2와 path도 함께 설치합니다.
mysql2
node.js환경에서 mysql을 사용하기 위한 편의성 및 효율성을 제공하는 API.
javascript환경에서 발생하는 콜백과 관련한 복잡한 처리와 에러상황들을 편리하게 다룰 수 있도록 도와줍니다.
axios를 설치한 뒤 App.js를 수정합니다. 아래 코드는 화면이 처음 mount(랜더링)되었을 때 _getHello를 실행하여 axios를 통해 '/hello'경로로 ajax통신요청을 합니다. 그럼 응답받은 hello데이터를 setState하여 화면을 다시 랜더링하게 됩니다.
index.js 혹은 App.js등 각 파일에서 다른 파일들을 import할때 기본적으로 상대경로로 잡혀있는 것을 볼 수 있습니다. 나중에 프로젝트의 규모가 커지고 또 복잡해지면 상대경로에 의한 관리시 다른 파일들을 import할 때 굉장히 번거로워지기 때문에 절대경로로 세팅해주어야 합니다.
jsconfig.json파일 생성
프로젝트 최상위폴더(root)에 jsconfig.json파일을 추가한 뒤 아래 코드를 넣어줍니다. src를 기본경로로 세팅합니다.
React프로젝트를 시작하려면 기본적으로 SPA의 개념을 이해할 필요가 있습니다. SPA는 Single Page Application 즉 단일페이지 어플리케이션이라는 의미인데요,일반적으로 어떤 웹사이트를 이용할 때 url을 통해 유저가 새로운 페이지를 요청하면 해당되는 페이지리소스를 서버로부터 받아새로운 화면을 브라우저를 통해 보게 됩니다. 이에 따른 서버의 역할을 라우팅(Routing) 이라고 하지요. 그래서 전통적으로 웹 어플리케이션은 각 url에 해당하는 여러 페이지로 구성되어 있었고 규모가 큰 어플리케이션의 경우 요즘과 같이 사용자와의 많은 상호작용과 데이터전송량에 따라 렌더링을 위한 서버자원사용에 따른 불필요한 트래픽, 속도저하 등의 문제가 생기게 되었습니다.
React는 이와 같은 문제점을 해결하기위해 전통방식과 다른 개념을 도입한 라이브러리 또는 프레임워크입니다. 서버측에서는 한 개의 페이지만 제공하고 해당하는 뷰 렌더링은 유저의 브라우저에게 역할을 넘겨 필요한 데이터만 전달한 뒤 적절한 뷰를 보여주도록 한 것입니다. 이때 해당뷰에 따른 url이 필요한 경우가 많은데 이 역할이 React에는 기본적으로 내장되어있지 않아 React Router를 별도 설치 후 사용해야 합니다.
react-router-dom 설치
cmd 또는 콘솔창을 열어 생성한 프로젝트폴더로 이동한 뒤 아래 설치명령어를 입력합니다.
npm install --save react-router-dom
설치완료 후 프로젝트 root폴더의 package.json파일을 열어 dependencies항목에 "react-router-dom"이 추가되었는지 확인합니다.
화면에 컴포넌트가 렌더링(마운트)/리렌더링 되거나 수정,제거 등의 변경 전(언마운트)에 효과를 주기위해 사용.
마운트 시점에 콘솔로 확인
마운트시점이란 컴포넌트요소가 화면에 렌더링될 때를 말합니다. 구체적으로 말하면 페이지를 맨처음 로딩할 때, 또 어떤 값을 수정/제거하여 변경된 내용이 다시 로딩될 때입니다. 이러한 상황은 props로 받은 값을 useState로 설정(관리)하거나 외부 Api요청시, setInterval, setTimeout설정, url체크하여 렌더링할 요소를 설정할 때 등이 있습니다.
ArrayList.js에서 useEffect를 사용하기위해 먼저 import를 해줍니다. 그리고 아래와 같이 화면출력코드가 있는 TempInfo()항목에 useEffect로 실행될 코드를 선언해줍니다. 렌더링되는 시점에 "렌더링" 텍스트와 tempInfo내용이 출력되도록 해보겠습니다.
주의할 점은 배열로 [tempInfo]를 넣은 부분인데요, 디펜던시(deps)라고 부르는 useEffect관리대상(값 또는 function도 포함)을 넣어주지 않으면 이부분의 코드를 변경된 특정 값에 대해만 실행하는게 아니라 추가/수정시 tempInfo를 포함하는 tempInfoArray의 모든 리스트 수만큼, 또 useState로 관리되는 input의 onChange의 모든 상황마다 매번 계속해서 실행하게 됩니다. 불필요하게 코드가 계속 실행되는 거죠.
useEffect(() => {
코드부분
}, [tempInfo]);
그리고 만약 디펜던시(deps)영역을 빈 배열형태로 넣으면 로딩 후 변경된 값이 체크안되서 이전의 값을 참고하게 될 수 있으므로 관리대상값은 꼭 deps배열안에 추가를 해주어야 합니다.
언마운트 시점에 콘솔로 확인
언마운트상황은 수정/제거등의 변경진행 전 시점에 효과를 주기위해 사용됩니다. 라이브러리 인스턴스를 제거하던가 setInterval/setTimeout해뒀던 요소를 변경하기위해 clearInterval/clearTimeout하는 상황들을 예로 들 수 있습니다.
먼저 만들어둔 ArrayList.js에 배열항목 수정기능을 추가해봅니다. 형태는 브라우저화면에서 리스트별 username부분 클릭시 text컬러가 변경되는데 이 컬러가 배열안의 'active'라는 key값이 true/false로 바뀌면서 화면에 변경된 컬러로 적용(toggle)되는 방식입니다.
화면렌더링되는 TempInfo 리턴부분에 style을 넣어주는데 tempInfo.active값이 참일때 orange컬러로, 거짓일때 black컬러로 변경되도록 합니다. (마우스클릭 영역표시를 위해 커서 pointer도 추가)
이전에 작업해둔 ArrayList.js파일에서 배열리스트마다 삭제가 가능하도록 TempInfo()의 화면 렌더링코드부분에 삭제버튼코드를 넣어줍니다. 그리고 해당 삭제이벤트는 RanderTempInfoArray()에서 map()에 의해 파라미터로 받을 것이므로 각 속성과 파라미터를 지정해줍니다. 이때 onRemove함수는 렌더링동시에 실행될 것이 아니라 클릭시 시작되야하므로 호출형식이 아닌 아래 코드와 같은 방식으로 넣어줍니다.