일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- Git
- 파이썬프로그래밍기초
- HTML
- 코드잇
- 유노코딩
- CSS
- 항해99
- 코딩테스트준비
- TiL
- 방송대
- redis
- JavaScript
- nestjs
- 데이터베이스시스템
- 프로그래머스
- 엘리스sw트랙
- 파이썬
- SQL
- Cookie
- 99클럽
- aws
- 중간이들
- 방송대컴퓨터과학과
- 꿀단집
- Python
- 자격증
- 개발자취업
- MySQL
- 코딩테스트
- node.js
- Today
- Total
배꼽파지 않도록 잘 개발해요
[코드잇] Express 기본기 - ② ORM으로 하는 데이터베이스 작업, 배포하기 본문
◆ Express 기본 익히기
◆ ORM으로 하는 데이터베이스 작업
◆ 배포하기
ORM으로 하는 데이터베이스 작업
데이터베이스와 SQL
· 실제 서비스에서는 배열 대신 데이터베이스를 사용함.
· 데이터베이스 : 일정한 체계 속에 저장된 데이터의 집합
· 테이블 : 표 형식으로 지정된 데이터의 집합
데이터베이스의 데이터 처리
· 테이블에 있는 row를 조회하는 작업
· 테이블에 새로운 row를 추가하는 작업
· 테이블의 기존 row를 수정하는 작업
· 테이블의 기존 row를 삭제하는 작업
DBMS(Database Management System)
· 데이터베이스 제어 프로그램
· 사용자가 입력한 SQL을 해석해서 데이터베이스 작업을 수행
· MySQL(Oracle), PostgreSQL, SQLServer 등
ORM이란?
ORM(Object-relational mapping) : 데이터베이스에 있는 데이터를 하나의 객체에 매핑시키는 기술
자바스크립트에서 데이터베이스를 다루려면 클라이언트 객체가 필요함.
DB 종류에 맞는 클라이언트 객체를 사용해야함.
클라이언트 객체 사용 방법
- 직접 작성한 SQL문을 클라이언트 객체를 통해서 DBMS로 전송
- ORM 기술을 통해 JS로 작성한 DB코드를 자동으로 SQL문으로 변환시켜서 클라이언트 객체를 통해 DBMS로 전송
1. SQL문 직접 작성
member 테이블에서 id column의 값이 1인 row조회
const mysql = require("mysql2")
// 중략
SELECT * FROM 'member' WHERE 'id' = 1
member 테이블에서 id column의 값이 1인 row조회
SQL문을 잘 모르면 사용하기 힘들다.
2. ORM 기술
vehicle.color = 'blue';
→ UPDATE vehicles SET color = 'blue' WHERE 'id' = 1;
DB의 vehicles 테이블에 있는 하나의 row의 color column의 값이 blue로 수정됨.
데이터베이스 생성하기
npx sequelize init
sequalize 패키지 설치
npm install mysql2 sequelize sequelize-cli
sequalize만 있어도 되는데, 여러 작업 명령어를 효율적으로 처리하기 위해서 sequelize-cli 패키지도 함께 사용하는 것이 좋음.
sequelize를 사용할 때는 프로젝트 내부의 디렉토리도 그에 알맞은 구조로 잡아주는 것이 좋음.
Sequelize 프로젝트의 초기 구조 및 구성을 설정하는 명령어
npx sequelize init
config, migrations, models, seeders 각 디렉토리가 새로 생성됨.
config
- configuration의 줄인말
- DB접속이 가능한 설정들이 들어있는 디렉토리
- config.json 파일에는 하나의 객체가 있고, 그 안에 총 development, test, production이라는 3개의 객체가 있음.
보통 개인용 프로젝트를 위해서는 DB 하나만 사용함.
실무 개발에서는 똑같이 A라는 DB가 있어도 개발용, 테스트용 DB, 실제 서비스 용도 등 용도별로 DB를 나누는 경우가 많음.
이 중에서 development DB만 사용할 것임.
userName : root -> 사용자 계정 이름
password : -> MySQL 해당 계정 비밀번호
host : 127.0.0.1 -> 컴퓨터
dialect : mysql -> 사용할 DB의 종류
DB 생성 명령어
npx sequelize db:create --env development
--env development
- DB 생성에 적용할 옵션
- config.json 파일 중에서 어떤 것을 사용할지 결정하는 것
- config.json 파일 중 development 객체에 적은 내용을 바탕으로 DB가 생성됨.
- 옵션을 지정하지 않아도 기본적으로 development 객체의 정보가 지정되게 됨.
DB 생성하려고 하니 mySQL 계정 접속에 오류가 발생함.
컴퓨터에 설치된 MySQL Workbench에 들어가서 Test Connection을 시도함.
현재 계정에서 비밀번호를 입력하면 성공적으로 접속되는 것을 확인함.
계정 비밀번호가 일치하는데, 뭐가 문제일까 고민하다가 포트 번호를 입력해봄.
그래도 에러가 발생함.
이상해서 자세히 살펴보니 터미널에서 프로젝트 폴더 경로에 접속이 되어있지 않았음.
하...^^ 경로를 제대로 설정해주니 정상적으로 DB가 생성됨.
npx의 의미
- npx는 패키지에 내장된 명령을 실행하기 위해 사용하는 키워드
- sequelize라고 하는 파일은 사실 다른 파일에 대한 바로가기 파일
- npx sequelize를 실행하면 node_modules 디렉토리 안에 있는 .bin 디렉토리 안의 sequelize라는 파일을 실행하게 되는데, 이때 이 sequelize 파일은 바로가기 파일이라서 그 원본 파일인 node_modules 디렉토리 안에 있는 sequelize-cli 패키지(디렉토리) 안의 lib 디렉토리 안의 sequelize 파일을 node로 실행하게 되는 것임.
모델과 테이블 생성하기
- sequelize에서는 DB에 존재하는 하나의 테이블이 JS에서는 하나의 class에 대응됨.
- class로 만든 객체는 하나의 row
모델과 테이블 생성하는 명령어
attributes 안에 "" 따옴표 안 붙이니까 인식이 안 되어서 붙여줌.
npx sequelize model:generate --name Member --attributes "name:string,team:string,position:string,emailAddress:string, phoneNumber:string,admissionDate:date,birthday:date,profileImage:string"
- model:generate : model을 생성할 거야
- --name Member: model의 이름은 Member
- --attributes : 속성 이름과 데이터타입 지정
데이터 타입
- Sequelize.STRING 문자열 타입 : VARCHAR(255)
- Sequelize.INTEGER 정수형 타입 : INTEGER
- Sequelize.FLOAT 실수형 타입 : FLOAT
- Sequelize.DATE 날짜형 타입 : DATETIME
https://sequelize.org/v5/manual/data-types.html
Manual | Sequelize
Datatypes Below are some of the datatypes supported by sequelize. For a full and updated list, see DataTypes. Sequelize.STRING // VARCHAR(255) Sequelize.STRING(1234) // VARCHAR(1234) Sequelize.STRING.BINARY // VARCHAR BINARY Sequelize.TEXT // TEXT Sequeliz
sequelize.org
id라는 컬럼이 없음.
Sequelize에서 기본으로 id라는 컬럼을 생성해주기 때문임.
명령어 입력 후 migrations 폴더와 models 폴더에 새로운 파일이 생성됨.
· migration : 데이터베이스 내부에서 일어나는 모든 변경 사항 (테이블 생성, 테이블 컬럼 추가 등)
· 테이블 이름으로 Members라는 복수형으로 나옴.
→ Sequelize는 모델 이름을 단수로 정했더라도 테이블 이름은 자동으로 복수형으로 변환해서 지어줌.
up메소드와 down메소드
- up 메소드 : migration 적용할 때 실행됨. → 테이블 생성
- down 메소드 : migration 적용 해제할 때 실행됨. → 테이블 삭제
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Members', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Members');
}
};
정의한 적 없는데 자동생성되는 컬럼
테이블 설계할 때는 해당 row의 생성 시간과 갱신 시간을 기록하는 컬럼들이 많음.
Sequelize Cli 명령어가 자동으로 명령어를 생성해준 것임.
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
컬럼이 지정되지 않았을 때 들어갈 기본값 설정
defaultValue: Sequelize.fn('now'),
Sequelize 마이그레이션 파일을 사용하여 데이터베이스에 변경 사항을 적용하는 명령
마이그레이션 파일은 데이터베이스 스키마를 수정하거나 테이블을 생성하는 데 사용
npx sequelize db:migrate
테이블 지우기
migration을 직접 실행 해제할 수 있음.
npx sequelize db:migrate:undo
다시 명령어 입력하여 재생성함.
Primary Key란?
id : members테이블에서 각각의 직원정보를 고유하게 식별하게 해주는 프로퍼티
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
primary key
· 데이터베이스의 테이블에서 특정 row를 고유하게 식별할 수 있게 해주는 column
· DB성능과도 직결되기 때문에 어떤 column을 primary key로 고를지도 고민을 많이 함.
· sequelize는 별도로 primary key를 지정하지 않으면 id를 생성하여 그것을 primary key로 지정함.
Null
· column에 값이 없음을 의미
· allowNull = False : Null을 허용하지 않음. 해당 column에는 항상 값이 있어야 함.
autoIncrement: true
· id값을 직접 지정하지 않아도 됨.
· DB가 자동으로 이전값보다 1이 더 큰 값을 넣어줌.
· 직접 적어줄 때는 이전에 동일한 id값이 있는지 없는지 미리 확인해야함.
Seed 데이터 넣기
seed data : 테이블에 가장 처음으로 넣는 데이터
members 테이블에 직원 정보를 넣어보기
Members 테이블에 넣을 seed 데이터 생성
npx sequelize seed:generate --name initialMembers
명령어는 seeddata를 추가하는 내용을 담은 파일을 생성하고
그 이름을 initialMembers라고 함.
Members 테이블에 Seed 데이터 추가
npx sequelize db:seed:all
이거 안 써서 GET 데이터 요청하니까 빈 배열 뜨길래 원인 파악하느라 1시간 날아감^^
seeders 파일
- up 메소드 : seeders 파일에 적용할 내용
- down 메소드 : seeders 파일을 해지할 때 적용할 내용
seeders 디렉토리에 있는 모든 seeder 파일이 적용됨.
npx sequelize db:seed:all
members 테이블에 10명의 직원 정보가 들어감.
모델과 테이블 연동하기
member class --> members Table
member 모델을 통해 Members 테이블을 조작할 수 있도록 모델과 테이블을 연동
함수 안에는 member라는 class가 들어있음.
Member class는 sequelize 패키지에 있는 Model이라는 클래스를 상속받고 있음.
member 클래스는 모델이 됨.
DB에 있는 하나의 테이블에 대응되는 것이 됨.
members.js
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Member extends Model {
static associate(models) {
}
}
Member.init({
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;
};
init 부분이 member와 테이블을 연동하는 코드
두 개의 객체가 argument로 들어가 있음.
1. members 테이블의 각 column에 대응되는 각 프로퍼티 이름과 데이터타입
members 테이블에 존재하는 column이라고 해도 모델에서 같은 이름의 프로퍼티를 설정해주지 않으면 모델이 column을 인식할 수 없음.
2. sequelize와 modelName
sequelize: sequelize
전체 함수의 파라미터로 전달될 sequelize의 객체를 의미함.
module.exports = (sequelize, DataTypes) => {
sequelize 객체가 이 파라미터로 들어와서 member 모델이 DB에 존재하는 members 테이블을 인식하도록 하는데 사용됨.
id 프로퍼티는 없는 이유?
id값을 직접 넣지 않기 때문에 모델에서는 없어도 돼서 그럼.
직접 적어주려면 넣어주기!
type: sequelize 대신 DataTypes를 넣어주기
type: Sequelize.INTEGER
type: DataTypes.INTEGER
index.js
const Sequelize = require('sequelize');
이 Sequelize라는 패키지는 sequelize라는 클래스를 외부에 공개함.
sequelize 클래스에 DB접속에 관한 설정값을 넣고 sequelize 객체를 생성하면 됨.
models 안 index.js
const Sequelize = require('sequelize');
const config = require('../config/config.json');
const {
username, password, database, host, dialect,
} = config.development;
// sequelize 클래스로 sequelize 객체를 생성
// 정해진 형식대로 각 정보를 넣어주기만 하면 sequelize 객체가 생성됨.
const sequelize = new Sequelize(database, username, password, {
host,
dialect,
});
// Member 엔티티에 대한 Sequelize 모델 생성함.
const Member = require('./member')(sequelize, Sequelize.DataTypes);
이 Sequelize라는 패키지는 sequelize라는 클래스를 외부에 공개함.
sequelize 클래스에 DB접속에 관한 설정값을 넣고 sequelize 객체를 생성하면 됨.
const sequelize = new Sequelize(database, username, password, {
host,
dialect,
});
'./member' 경로에 있는 모듈을 가져옴.
가져오는 파일은 Member 엔터티에 대한 Sequelize 모델 정의
require('./member')
sequelize : Sequelize ORM의 인스턴스
Sequelize.DataTypes : Sequelize에서 제공하는 사용 가능한 모든 데이터 유형 보유하는 객체
(sequelize, Sequelize.DataTypes)
App.js 파일에서 이 Member 모델을 사용하도록 하기
Member 모델을 공개해야됨.
index.js 파일에서 Member 모델을 바로 공개해도 되지만
DB라는 객체를 만들고 그 안에 Member 모델을 넣어서 공개
(다른 테이블이 생긴다면 새로운 모델이 필요할 수 있기 때문임)
// App.js 파일에서 이 Member 모델을 사용하도록 하기
// db 객체를 만들어서 그 안에 Member 모델을 넣어서 공개
const db = {};
db.Member = Member;
module.exports = db;
App.js
// db 객체를 불러옴.
const db = require('./models/index');
// db객체에서 Member 모델을 꺼냄
const { Member } = db;
직원 정보 조회하기 - findAll
member 모델을 통해서 테이블 관련 작업을 진행할 수 있음.
전체 직원 정보 조회 코드 수정하기
- 모델.findAll(); Members 테이블에 있는 모든 row를 조회하고 가져오는 기능을 함.
app.js
app.get('/api/members', (req, res) => {
const { team } = req.query;
if (team) {
const teamMembers = members.filter((m) => m.team === team);
res.send(teamMembers);
} else {
const members = Member.findAll(); // ---------> 이 부분 추가
res.send(members);
}
});
request 보내기
Member.findAll() 이라는 코드는 Sequelize에 의해서 결국 이 SQL문으로 변환되어서 DBMS에 전송되는 것임.
이렇게 SQL문이 자동변환되어 실행되는 것이 ORM 기술의 핵심임.
빈 객체가 온 이유?
모델이 갖고 있는 대부분의 객체들은 Promise를 리턴하는 비동기 실행함수임.
app.get('/api/members', async (req, res) => {
const { team } = req.query;
if (team) {
const teamMembers = members.filter((m) => m.team === team);
res.send(teamMembers);
} else {
const members = await Member.findAll();
res.send(members);
}
});
직원 정보를 다 가져올 때까지 기다리기 위해서 async await 구문으로 바꿔줌
await는 async가 붙은 함수 앞에서만 사용할 수 있음.
→ 코드가 붙어 있는 라우터 핸들러 앞에서 async를 붙여줘야 함.
GET 리퀘스트 요청하면 원하는 대로 리스폰스가 옴.
전체 멤버를 조회한 경우
members?team=engineering으로 엔지니어링 팀에 속한 사람들 정보만 요청
특정 직원 정보 조회하기 - findOne
- 모델.findOne(); Members 테이블에 있는 특정 row를 조회하고 가져오는 기능을 함.
조건에 맞는 row가 여러 개여도 한 개만 조회함.
- 여러 개 조회 : findAll
- 한 개만 조회 : findOne
- 조건 설정할 때는 { where : { 프로퍼티 } }
app.js
// 특정 id 컬럼값을 조회
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 res.status(404)
.send({ message: 'There is no such member with the id!' });
});
text.http에서 GET 요청
새 직원 정보 추가하기
app.post('/api/members', async (req, res) => {
const newMember = req.body;
const member = Member.build(newMember);
await member.save();
res.send(member);
});
id 부분은 굳이 추가해주지 않아도 됨.
id: { primaryKey: true, autoIncrement: true }
이전값보다 1이 더 큰 값이 자동으로 들어가게 됨.
Sequelize ORM 사용할 때 async await구문 주의하자!
모델 객체가 갖고 있는 대부분의 메소드는 Promise 객체를 리턴하는 비동기 실행함수임.
build와 save 대신 create
build와 save로 2줄 썼던 부분을 create 메소드로 활용하여 1줄로 작성할 수 있음.
app.post('/api/members', async (req, res) => {
const newMember = req.body;
const member = Member.build(newMember); // build
await member.save(); // save
res.send(member);
});
app.post('/api/members', async (req, res) => {
const newMember = req.body;
const member = await Member.create(newMember);
res.send(member);
});
기존 직원 정보 수정하기
update 메소드에는 2개의 아규먼트
- newInfo : 새로운 직원의 정보
- { where: { id } } : 조건을 특정하기 위한 객체
promise 객체를 리턴하는 비동기 실행함수
update 메소드가 리턴하는 프로미스 객체에는 성공결과로 배열 1개가 들어있음.
app.put('/api/members/:id', async (req, res) => {
const { id } = req.params;
const newInfo = req.body;
const result = await Member.update(newInfo, { where: { id } });
if (result[0]) { // 수정된 row 1개가 리턴됨
res.send( { message: `${result[0]} row(s) affected` });
} else { // 테이블에 존재하지 않는 로우
res.status(404).send({ message: 'There is no member with the id!' });
}
});
update 메소드는 지정해준 프로퍼티만 수정하기 때문에 굳이 POST 할 내용을 다 적어줄 필요가 없음.
수정하고 싶은 프로퍼티에 해당하는 정보만 작성하면 됨.
PUT http://localhost:3000/api/members/1
Content-Type: application/json
{ "position": "IOS developer" }
update 메소드 외 다른 방법
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!' });
}
});
기존 직원 정보 삭제하기
app.delete('/api/members/:id', async (req, res) => {
const { id } = req.params;
const deletedCount = await Member.destroy({ where: { id }});
if (deletedCount) {
res.send({ message: `${deletedCount} row(s) Deleted` });
} else {
res.status(404).send({ message: 'There is no member with the id'});
}
});
https://sequelize.org/api/v6/class/src/model.js~model
Model | Sequelize
An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, validationFailed, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate,
sequelize.org
배포하기
배포 (Deploy)
- 직접 외부의 서버를 빌리고, 서버에 원격 접속해서 설정 후 코드를 실행
- AWS 등의 클라우드 서비스로 서버를 빌려서 코드를 실행하는 방법
NODE_ENV : Node.js 환경에서는 개발 환경과 배포 환경을 구분하기 위해서 사용하는 환경변수
NODE_ENV 의 값이 development 이면 개발 환경, production 이면 배포 환경을 의미함.
Heroku는 서버를 빌리거나 원격접속하지 않아도 배포할 수 있도록 함.
Heroku 유료로 바뀜.
Cloud Application Platform | Heroku
Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.
www.heroku.com
Deploy app servers close to your users · Fly
No Poster Board Necessary Globally Distributed Databases Without the Science Project Fly Anycast HTTP lets apps control routing, enabling single-writer multi-reader clusters for super-fast reads. Fast nVME Storage Create Fly Volumes from 1 to 500 gigabytes
fly.io
Railway
Railway is an infrastructure platform where you can provision infrastructure, develop with that infrastructure locally, and then deploy to the cloud.
railway.app
'코드잇 Codeit > Back-End' 카테고리의 다른 글
[코드잇] SQL 데이터베이스 - SQL로 하는 데이터 관리 (0) | 2024.06.29 |
---|---|
[코드잇] 관계형 데이터베이스를 활용한 자바스크립트 서버 만들기 (0) | 2024.06.25 |
[코드잇] Express 기본기 - ① Express 기본 익히기 (0) | 2024.01.23 |
[코드잇] Node.js 기본기 ② - 웹서버 제작, 서드파티 모듈과 npm (0) | 2024.01.23 |
[코드잇] Node.js 기본기 ① - Node.js 시작하기 및 기본 개념 (0) | 2024.01.22 |