[ON SOPT] Server 4th Seminar

[ON SOPT] Server 4th Seminar

4주차 세미나 사전준비

  1. RDS 세팅
  2. MySQL workbench 설치

Database

데이터베이스

서버는 데이터베이스에 접근하여 데이터들을 Read/Write 한다.

db

데이터베이스

  • 관련성을 가지며 중복이 없는 데이터의 집합
  • 데이터베이스를 관리하는 시스템을 DBMS(Database Management System) 이라 함.
  • Relational Database(관계형 데이터 베이스): key-value 관계를 테이블화 한 것


데이터 베이스 용어 정리

Primary Key(기본키) : 다른 데이터와 구별할 수 있는 식별자

  • 한 테이블에는 하나 혹은 그 이상의 primary key가 있어야 함
  • Not Null, Unique

Index : 데이터 색인 기능

  • 관계형 데이터 베이스에서 검색 속도를 높이기 위한 도구
  • Unique, 특정 데이터 대표
  • 주로 기본키로 사용됨

Foreign key(외래키) : 한 테이블의 column 중 다른 테이블의 row를 식별할 수 있는 key


Relation 관계

1 : 1 관계
말 그대로 1:1로 매핑되는 관계 (예: 이름 - 주민번호)

1 : N 관계
한쪽 개체가 관계를 맞은 개체 쪽의 여러 개체를 가질 수 있는 관계 (예: 이름 - 이메일)
테이블 한 개로 담기에는 중복되는 정보가 너무 많아, 테이블을 나누어 준다.

M : N 관계
양쪽 개체가 서로 1:N 관계로 보고 있는 관계 (예: 이름 - 스터디)
이러한 경우에는 테이블을 잘 나누어 주어야 한다 → 정규화


정규화

관계형 데이터 베이스를 논리적이고 직관적으로 만드는 과정

목적

  • 불필요한 데이터 제거
  • 논리적인 데이터 저장
  • 이상현상 방지
    • 삽입 이상 : 새 데이터를 삽입할 때 불필요한 데이터도 함께 넣어줘야 되는 문제
    • 갱신 이상 : 중복 튜플인데 일부만 변경되는 문제 (데이터 모순)
    • 삭제 이상 : 튜플을 삭제했는데 중요 데이터가지 함께 삭제되는 데이터 손실 문제

< 1차 정규화 >
조건: 각 row 마다 column이 값을 하나씩만 가져야 함 (즉, 컬럼이 원자값을 가져야 함)
쉬운 설명:

  1. 모든 Domain에 값이 한 개씩만
  2. 반복되는 group이 없음
  3. 기본키(PK)로 각 데이터가 식별 가능해야함

< 2차 정규화 >
조건: 부분 함수적 종속이 없이, 모든 컬럼이 완전 함수적 종속을 만족함
함수적 종속: X에 의해 Y가 결정되면, Y는 X에 대해 함수적 종속이다
이 때 X를 결정자, Y를 종속자라고 한다
부분 함수적 종속: {X1, X2} → Y 에서, X2 만으로도 Y가 결정되는 경우
완전 함수적 종속: {X1, X2} → Y 에서, X1과 X2가 모두 있어야 Y가 결정되는 경우

< 3차 정규화 >
조건: 2차 정규화가 되어 있고, 이행적 함수 종속이 없어야 함
쉬운 설명: 기본 키가 아닌 속성들은 기본 키에만 의존해야 함

< 역 정규화 >
시스템의 퍼포먼스를 위해 정규화에 위배되는 행위를 하는 것


Sequelize ORM (Object Relational Mapping)

Sequelize ORM이란?

NodeJS에서 JS의 객체와 DB의 관계를 매핑해주는 라이브러리!
SQL을 사용하지 않고 JS로 DB에 접근한다


Sequelize Project

처음: npm install sequelize-cli sequelize mysql2로 모듈 3개 깔아주고, sequelize init 해주면 sequelize project가 생성된다.

  • config/config.json : DB의 정보를 담는 공간
  • models/index.js : DB의 테이블을 정의하는 곳 (MySQL의 Table에 대응)
  • Migrations : 테이블 컬럼을 추가/제거할 때 이 파일을 통해 실제 DB에 반영 (세미나에서 사용x)
  • Seeders : 시드 데이터를 생성하는 곳 (테스트 환경) (세미나에서 사용x)

MySQL Workbench의 RDS에서 Edit Connection - Hostname, Username, Password 확인, 이들을 config/config.json에 잘 적어준다.

예: console로 DB 연결을 확인하는 코드 (App.js에 추가)

1
2
3
4
5
6
7
8
9
const { sequelize } = require('./models');

sequelize.sync({ alter: false })
.then( () => {
console.log('데이터베이스 연결 성공');
})
.catch( (error) => {
console.error(error);
})

추가설명:

  • sequelize.sync({}) 에서 모델 동기화가 시작된다! 테이블이 존재하지 않는 경우 생성함.
  • sequelize.sync({force: true}) : 테이블이 이미 존재하면 삭제하고 생성
  • sequelize.sync({alter: true}) : DB의 테이블 현재 상태(컬럼, DataType)를 확인하고 필요한 변경을 수행하여 모델과 일치시킴


Sequelize로 Model 정의하기

기본적으로는 다음과 같이 sequelize.define()를 이용하여 정의한다.

1
sequelize.define('모델이름', { /* 스키마 */ }, { /* 스키마 옵션 */ });

스키마 표기법은 다음과 같다:
스키마

모델의 옵션에는 다음과 같은 것들이 있다:

  • FreezeTableName: true이면 모델명과 DB 테이블 이름을 동일하게 설정해줌. (Sequelize는 기본적으로 모델이름 - 단수, 테이블이름 - 복수 로 설정)
  • timestamps: 기본값 true, 자동으로 createdAt, updatedAt 컬럼 만들어줌.
  • tableName: 실제 DB의 테이블 이름 설정해줌.
  • paranoid: true로 설정하면 deletedAt라는 컬럼이 생기고, row를 삭제했을 때 실제 데이터는 삭제되지 않고 deletedAt에 지운 시간이 기록됨. 이러면 Select 할 때 집계 X
  • underscored: true이면 기본값 CamelCase에서 snake_case 로 바뀜.

Models/user.js의 예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = (sequelize, DataTypes) => {
return sequelize.define('User', {
// 모델의 Column 정의
email: {
type: DataTypes.STRING(30),
unique: true,
allowNull: false,
},
userName: {
type: DataTypes.STRING(200),
allowNull: false,
},
}, {
// 모델의 Option 정의
freezeTableName: true,
timestamps: false,
});
};

위의 예시와 같이 User Model을 만들면, Models/index.js에서 다음과 같이 Sequelize 객체에 연결할 수 있다.

1
2
3
4
5
6
7
8
9
cosnt db = {};

/* sequelize, Sequelize, env, config 설정 */

db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.User = require('./user')(sequelize, Sequelize);

module.exports = db;

Sequelize Query

Sequelize 쿼리를 이용하여 CRUD 작업을 해보자!
crud

예시들 (Sequelize query에서 const { User } = require('models/index.js 상대경로'); 해줘야 함)

  • Create

    • SQL
      INSERT INTO users (name, age, address) VALUES ('신연상', 21, '강남구');

    • Sequelize query

      1
      2
      3
      4
      5
      6
      // Model.create({ 정보 });
      const user = await User.create( {
      userName: '신연상',
      age: 21,
      address: '강남구',
      });
  • findOne

    • SQL
      select * from users LIMIT 1;
      select * from users WHERE name = '신연상';

    • Sequelize query

      1
      2
      3
      4
      5
      6
      7
      8
      const user = await User.readOne({ });

      // Model.findOne({ /* 읽을 로우 */ });
      const user = await User.findOne({
      where: {
      name: '신연상',
      },
      });
  • findAll

    • SQL
      select * from users;
      select name, address from users where address = '강남구';
      select age, name from users where age > 21;

    • Sequelize query

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      const user = await User.findAll({ });

      // Model.findAll({ attributes, where });
      const users = await User.findAll( {
      attributes: ['name', 'address'],
      where: {
      address: '강남구'
      }
      });

      const { Op } = require('sequelize');
      const users = await User.findAll( {
      attributes: ['age', 'name'],
      where: {
      age: { [Op.gt]: 21},
      }
      });
  • Update

    • SQL
      update users set name = '바뀔이름' where id = 2;

    • Sequelize query

      1
      2
      3
      4
      5
      6
      // Model.update({ /* 수정할 내용 */ }, { /* 어떤 로우를 수정 */ });
      const user = await User.update({ name: '바꿀 이름' }, {
      where: {
      id: 2
      }
      });
  • Destroy

    • SQL
      delete from users where id = 2;

    • Sequelize query

      1
      2
      3
      4
      5
      6
      // Model.destroy({ /* 삭제할 로우 */ })
      const user = await User.destroy( {
      where: {
      id: 2
      }
      });

참고사항들:

  • Sequelize는 자체적으로 promise를 반환하기 때문에 async/await 사용 가능
  • Sequelize 객체 내부의 Op 객체를 불러와 특수한 연산 사용 가능!
    ex: Op.gt(초과) Op.gte(이상) Op.lt(미만) Op.lte(이하) Op.ne(같지않음) Op.or(또는) Op.in(배열 요소 중 하나) Op.notIn(배열 요소와 모두 다름)
Author

Yeonsang Shin

Posted on

2020-11-07

Updated on

2022-12-19

Licensed under

Comments