1. 데이터베이스와 SQL
데이터베이스(Database)
- 일정한 체계 속에 저장된 데이터의 집합
- 하나의 row는 하나의 직원 정보를 의미
- 하나의 column은 직원 정보의 세부 정보들 중 하나를 의미
- 하나의 Database 안에는 여러 개의 Table이 들어갈 수 있음
데이터베이스의 데이터 처리
- 테이블에 있는 row를 조회하는 작업
- 테이블에 새로운 row를 추가하는 작업
- 테이블의 기존 row를 수정하는 작업
- 테이블의 기존 row를 삭제하는 작업
- 데이터베이스를 사용하려면 DBMS라고 하는 프로그램이 필요
DBMS(Database Managemnet System)
- Database를 제어하기 위해 사용하는 프로그램
- 사용자가 입력한 SQL을 해석해서 데이터베이스 작업을 수
- 종류 : MySQL, PostgreSQL, SQLServer
2. ORM이란?
- 자바스크립트로 데이터베이스를 다루려면 클라이언트 객체라는 것이 필요
- 클라이언트 객체는 SQL을 데이터베이스 서버에 전달해서 원하는 작업을 처리하고 그 결과를 받아오는 객체
- 이때 데이터베이스의 종류에 맞는 클라이언트 객체를 사용해야
- MySQL2라는 패키지에 있는 클라이언트 객체 사용
1) 직접 작성한 SQL문을 클라이언트 객체를 통해서 DBMS로 전송하는 방법
- 단점 : SQL를 모르면 사용 불가
2) ORM이라고 하는 기술을 통해서 자바스크립트로 작성한 데이터베이스 관련 코드를 자동으로 SQL문으로 변환시켜서 클라이언트 객체를 통해 DBMS로 전송
- ORM 패키지에서 ORM은 Object-Relational Mapping의 약자로 데이터베이스에 있는 데이터를 하나의 객체에 매핑시키는 기술
3. 데이터베이스 생성하기
sequelize 패키지 사용
- npm install myslq2 (클라이언트 객체 제공)
- npm install sequelize
- npm install sequelize-cil
(데이터베이스 관련 작업을 sequelize와 연동해서 터미널에서 직접 명령어를 수행할 수 있게 해줌)
npx sequelize init
- 각각의 디렉토리 안에 기본파일들이 새롭게 생성
- config / migrations / models/ seeders 디렉토리 생
- config란 데이터베이스 접속에 관한 각종 설정들이 들어있는 디렉토리
npx sequelize db:create --env development
4. npx의 의미
- npx는 패키지에 내장된 명령을 실행하기 위해 사용하는 키워드
- 우리가 npx sequelize를 실행하면 node_modules 디렉토리 안에 있는 .bin 디렉토리 안의 sequelize라는 파일을 실행하게 되는데
- 이때 이 sequelize 파일은 바로가기 파일이라서 그 원본 파일인 node_modules 디렉토리 안에 있는 sequelize-cli 패키지(디렉토리)안의 lib 디렉토리 안의 sequelize 파일을 nodef로 실행
5. 모델과 테이블 생성하기
- 직원 정보를 저장할 Table
- sequelize에서는 데이터베이스에 존재하는 하나의 Table이 자바스크립트 코드에서 하나의 class에 대응
- class의 객체는 하나의 row에 해당
-- model을 생성할거야, model의 이름은 Member
npx sequelize modle:generate -- name Member --attributes
name : string, team: string, position:string, emailAddress:string,
phoneNumber:string,addmissionDate:data,birthday:date,profilelmage:string
- migration이란 데이터베이스 내부에서 일어나는 모든 변경 사항을 의미
- up 메소드는 이 migration을 적용할 때 실행, Members라고 하는 테이블을 생성
- 해당 row의 생성 시간과 갱신 시간을 기록하는 column들을 추가
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Members', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
team: {
type: Sequelize.STRING
},
position: {
type: Sequelize.STRING
},
emailAddress: {
type: Sequelize.STRING
},
phoneNumber: {
type: Sequelize.STRING
},
admissionDate: {
type: Sequelize.DATE
},
birthday: {
type: Sequelize.DATE
},
profileImage: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
}
});
},
});
- down 메소드는 migration을 적용 해제할 때 실행
- Members 테이블 삭제
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Members');
}
- Migration 파일을 적용해서 Members 테이블 생성
- npx sequelize db:migrate
6. 테이블 지우기
npx sequelize db:migrate:undo
- 가장 최근에 적용된 Migration 하나를 해제, 테이블 초기화
7. 데이터 타입과 기본값
1) 데이터 타입
- 각 프로퍼티에 설정했던 데이터 타입들은 결국 데이터베이스의 테이블의 각 컬럼의 데이터 타입으로 적용
- Sequelize.STRING 은 문자열 타입으로, 데이터베이스에서 VARCHAR(255)라고 하는 타입으로 변환
- Sequelize.INTEGER 은 정수형 타입으로, 데이터베이스에서 INTEGER라고 하는 타입으로 변환
- Sequelize.FLOAT은 실수형 타입으로 데이터베이스에서 FLOAT이라고 하는 타입으로 변환
- Sequelize.DATE은 날짜형 타입으로 데이터베이스에서 DATETIME이라고 하는 타입으로 변환
2) 기본값
// ...
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now')
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now')
}
// ...
- defaultValue 속성을 주게 되면, 테이블의 createdAt, updateAt 컬럼에도 이 속성이 적용됨
8. Primary Key란?
Primary key
- 데이터베이스의 테이블에서 특정 row를 고유하게 식별 할 수 있게 해주는 column
- 반드시 allowNull 속성의 값이 false여야 함 (NULL을 허용하지 않음, 이 컬럼에는 항상 값이 있어야함)
- autoIncrement는 Members 테이블에 새로운 직원 정보를 삽입할 때 마다 id column에 이전 값보다 1이 더 큰값을 데이터베이스가 자동으로 넣어 주는 속성
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
9. seed 데이터 넣기
Seed Data
- 테이블에 가장 처음에 놓는 데이터
- sequelize-cli 명령어로 실행
- npx sequelize seed:generate --name initialMembers
-> seed 데이터를 추가하는 내용을 담은 파일을 생성하고, 그 파일의 이름을 initialMembers로 하라는 뜻
-> seeders 디렉토리에 js 파일 생성
- npx sequelize db:seed:all
-> 실제로 seed 데이터 삽입
-> seeders 디렉토리에 있는 모든 seeder 파일이 적용
10. 모델과 테이블 연동하기
- COWOK 데이터베이스에 있는 Members 테이블을 Member라는 모델이 인식할 수 있도록 연
member.js
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Member extends Model {
static associate(models) {
}
}
Member.init({
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
name: DataTypes.STRING,
team: DataTypes.STRING,
position: DataTypes.STRING,
emailAddress: DataTypes.STRING,
phoneNumber: DataTypes.STRING,
admissionDate: DataTypes.DATE,
birthday: DataTypes.DATE,
profileImage: DataTypes.STRING
}, {
sequelize,
modelName: 'Member',
});
return Member;
};
index.js
const Sequelize = require('sequelize');
const config = require('../config/config.json');
const {
username, password, database, host, dialect,
} = config.development;
const sequelize = new Sequelize(database, username, password, {
host,
dialect,
});
const Member = require('./member')(sequelize, Sequelize.DataTypes);
const db = {};
db.Member = Member;
module.exporst = db;
app.js
const express = require('express');
const app = express();
const db= require('./models');
const { Member } = db;
app.use(express.json());
app.get('/api/members', (req,res)=> {
const { team } = req.query;
if(team) {
const teamMembers = members.filiter((m)=> m.team === team);
res.send(teamMembers);
}else {
res.send(members);
}
});
app.post('/api/members', (req,res) => {
const newMember = req.body;
members.push(newMember);
res.send(newMember);
});
//members/뒤에 오는 값을 id변수에 대입
//라우트 파라미터(Route Parameter)
app.get('/api/members/:id', (req,res)=> {
const { id } = req.params;
const member = members.find((m)=>m.id === Number(id));
if(member) {
res.send(member);
}else {
//404 : 요청한 정보가 없다는 걸 나타냄
res.status(404).send({message:"There is no such member"});
}
});
app.put('/api/members:id', (req,res) => {
const { id } = req.params;
const newInfo = req.body;
const member = members.find((m)=> m.id === Number(id));
if(member) {
Object.keys(newInfo).forEach((prop) => {
member[prop] = newInfo[prop];
});
res.send(member);
}else {
res.status(404).send({message: 'There is no member with the id:'});
}
});
app.delete('/api/members/:id',(req,res) => {
const { id } = req.params;
const memberCount = members.length;
members = members.filter((member) => member.id != Number(id));
if(members.length < membersCount) {
res.send({message: 'Deleted'});
} else {
res.status(404).send({messge: 'There is no member with the id!'});
}
});
11. 직원 정보 조회하기 - findAll
- 전체 직원 조회
-> findAll()은 Members 테이블에 있는 모든 row를 조회해서 가져오는 기능
-> sequelize에 의해서 SQL문으로 변환되어서 DBMS에 전송
app.get('/api/members', async(req,res)=> {
const { team } = req.query;
if(team) {
const teamMembers = members.filiter((m)=> m.team === team);
res.send(teamMembers);
}else {
const members = awaitMember.findAll();
res.send(members);
}
});
- 모델이 갖고 있는 대부분의 메소드들은 프로미스 객체를 리턴하는 비동기 실행 함수
- 특정 팀에 속한 직원 조회
const teamMembers = await Member.findAll({where:{team:team}});
12. 특정 직원 정보 조회하기 -findOne
- 테이블에서 특정 row 하나만을 조회
app.js
app.get('/api/members/:id', async (req,res)=> {
const { id } = req.params;
const member = await Member.findOne({where: {id}});
if(member) {
res.send(member);
}else {
//404 : 요청한 정보가 없다는 걸 나타냄
res.status(404).send({message:"There is no such member"});
}
});
13. 새 직원 정보 추가하기
- 새로운 직원 정보를 Members 테이블에 추가
- 리퀘스트의 바디에 있는 새로운 직원 정보를 받아서 Member 모델의 build 메소드 안에 넣고 호출
- build는 하나의 Member 모델 객체를 생성하고 리턴
-> 이 모델 객체는 테이블에서 하나의 row가 됨
- Member 모델 객체의 save 메소드를 호출하면 실제로 테이블에 이 Member 모델 객체의 내용대로
새로운 row가 추가
- save 역시 프로미스 객체를 리턴하는 비동기 함수이기 때문에 await와 async 추가
app.post('/api/members', async (req,res) => {
const newMebmer = req.body;
const member = Member.build(newMember);
await member.save();
res.send(member);
});
- 직원 정보의 프로퍼티들 중에서 id 부분은 굳이 추가해 주지 않아도 됨
-> id column 자동 증가
14. build와 save 대신 create
- Member 모델의 build 메소드로 저장할 Member 모델 객체를 생성했고, 해당 객체의 save 메소드를 호출해서 실제로 Members 테이블의 새 row로 추가
app.post('/api/members', async (req, res) => {
const newMember = req.body;
const member = Member.build(newMember);
await member.save();
res.send(member);
});
- 메소드 두개를 쓰지 않고, 단 하나의 메소드로도 똑같은 작업을 수행할 수 있음
- create() 메소드 사용
- build + save 조합을 create 메소드 하나로 대체 가능
app.post('/api/members', async (req, res) => {
const newMember = req.body;
const member = await Member.create(newMember);
res.send(member);
});
15. 기존 직원 정보 수정하기
- Member 모델의 update 메소드 사용
- update의 첫번째 인수는 새로운 직원의 정보 , 두번째 인수는 수정할 row를 특정하기 위한 조건 객체
- update 메소드도 프로미스 객체를 리턴하는 비동기 실행 함수
- update 메소드가 리턴하는 프로미스 객체에는 그 작업 성공 결과로 배열 하나가 들어 있음
-> 해당 배열의 첫 번째 요소에는 수정된 row가 리턴됨
-> 배열의 첫번째 요소가 0인 요소에는 그런 직원이 없다는 메시지를 그렇지 않은 경우에는 해당 개수의 row들이 영향을 받았다는 메시지를 보내줌
app.put('/api/members:id', async (req,res) => {
const { id } = req.params;
const newInfo = req.body;
const request = await Member.update(newInfo, {where: {id}});
if(result[0]) {
res.send({message: `${result[0]} row(s) affected}`});
} else {
res.status(404).send({message: `There is no member with the id!`});
}
});
- Member 모델의 update 메소드는 딱 지정해준 프로퍼티에 해당하는 column만 수행해주므로 수정할 column의 새 값만 적어줘도 됨
16. 직원 정보를 수정하는 또 다른 방법
- findOne 메소드는 해당 id값을 가지는 row를 가져오고 있음
- member 모델 객체는 해당 row와 연동되어 있음
- Member 객체의 프로퍼티 값을 리퀘스트의 바디에 있던 새 객체의 프로퍼티의 값들로 수정
- 모델 객체의 save 메소드를 호출하면 Member 모델 객체와 연동되어 있던 테이블의 실제 row가 수정
app.put('./api/members:id', async (req,res) => {
const { id } = req.params;
const newInfo = req.body;
const member = await Member.findOne({where: { id }});
if(member) {
Object.keys(newInfo).forEach((prop)=> {
member[prop] = newInfo[prop];
});
await member.save();
res.send(member);
}else {
res.status(404).send({message: `There is no member with the id!`});
}
});
17. 기존 직원 정보 삭제하기
- Member 모델의 destroy 메소드를 사용하면 원하는 row를 삭제할 수 있음
- destroy 메소드도 프로미스 객체를 리턴하는데 그 작업 성공 결과로는 실제로 삭제된 row의 개수가 들어 있음
- 삭제가 row가 없다면 그런 id를 가진 직원이 없다는 내용의 리스폰스를 돌려주고 1개 이상 삭제가 되었다면 해당 개수의 row들이 삭제되었다는 메시지를 줌
18. 모델의 주요 메소드 정리
- 모델 = 클래스, 모델 객체 = 해당 클래스로 만든 객체
1) findAll 메소드
- 테이블에 있는 모든 row들을 조회
const members = await Member.findAll();
- 특정 조건을 만족하는 row들만 조회
- where 프로퍼티를 가진 조건 객체를 넣어줌
const teamMembers = await Member.findAll({ where: { ~ } });
2) findOne 메소드
- 테이블에 있는 특정 row 하나를 조회할 때 사용
const member = await Member.findOne({ where: { ~ } });
3) build, save 메소드
- 테이블에 새로운 row를 추가할 때 사용
- build 메소드로 하나의 row에 대응하는 모델 객체를 생성하고, 해당 모델 객체의 save 메소드를 호출
const member = Member.build(newMember);
await member.save();
- create 메소드 사용
const member = Member.build(newMember);
await member.save();
4) update 메소드
- 원하는 특정 row들을 수정하는 메소드
- update 메소드가 리턴하는 Promise 객체는 그 작업 성공 결과로 배열 하나를 가지는데 해당 배열의 첫 번째 요소에는 갱신된 row 수가 담김
const arr = await Member.update(newInfo, { where: { ~ } });
5) destroy 메소드
- 원하는 row를 삭제하는 메소드
- destroy 메소드가 리턴하는 Promise 객체는 그 작업 성공 결과로 삭제된 row 수를 가짐
const deletedCount = await Member.destroy({ where: { ~ } });
'Node.js' 카테고리의 다른 글
[코드잇] Express 기본 익히기 (1) | 2023.10.26 |
---|---|
[코드잇] 서드파티 모듈과 npm 제대로 배우기 (1) | 2023.10.24 |
[코드잇] 초간단 웹서버 만들기 (0) | 2023.10.07 |
[코드잇] Node.js 기본 개념 (1) | 2023.10.04 |