코드스테이츠 12주차 -[데이터베이스] 관계형 데이터베이스 / [데이터베이스] ORM / [데이터베이스] NoSQL

엄혜진·2021년 9월 4일
0

CodeStates

목록 보기
12/15
post-thumbnail

백앤드에 대한 공부를 시작한 일주일이었다. 데이터베이스에서 어떤 방식으로 받아서 가공할지 코드를 작성해보고, CLI를 이용해서 직접 데이터베이스 테이블을 작성해보고 관계 형성을 해보면서 흐름이 어떤 식으로 흘러가고, 상황에 맞는 데이터 형식이 무엇인지 알게 되었다. 시작하기 전에는 아주 어려울 것 같아서 겁먹고 있었는데 다행히 이해도 되고 재미있다고 느껴졌다. 아직 시작단계여서 그럴지도 모르겠지만 겉으로 형태가 드러나지 않아서 답답할 것 같다고 생각했었는데, 이 부분에서 의외로 더 매력을 느끼게 되었다. 약간 마법을 부리는 것 같은 느낌이랄까😲
이번에 공부하면서 공식문서를 굉장히 많이 읽어본 것 같다. 솔직히 공식문서를 읽고 이해돼서 코드를 작성한 것보다 공식문서 외에 정리되어있는 내용을 통해서 공부를 더 많이 했다. 그래도 자주 접해봐야 나중에 사용을 용이하게 할 테니 가깝게 두어야할 것 같다🤤 이번 주에는 토이문제 풀면서 너무 화도 많이 나고 잘 안 풀리길래 프로그래머스 문제를 풀기 시작했다. 1단계부터 다 풀 생각으로 풀었는데, 예전에는 잘 안 쓰던 고차함수나 더 간결한 방식들로 자연스럽게 풀 게 되는 내 모습을 보면서 내가 많이 성장한 것 같아서 기분이 좋았다. 나중에 다른 분들이 작성하신 풀이를 보았을 때 내가 작성한 코드가 나쁘지 않게 풀었다는 걸 확인하면서 뿌듯했다. 앞으로 조급해 하지 말고 내 수준에서 차근차근 밟아봐야겠다. 성급하면 오히려 체하니까. 나 스스로 잘하고 있다고 생각하면서 긴장의 끈을 놓지 말아야겠다. 프로젝트 전까지 남은 기간 동안 페이스 조절 잘하면서 공부해야지🦾


12주차 배운 내용 중 정리하고 싶은 내용

[데이터베이스] 관계형 데이터베이스


  • SQL Advanced
Case사용 (if문과 같은 기능)
SELECT CASE
WHEN CustomerId <= 25 THEN 'GROUP1'
WHEN CustomerId <= 50 THEN 'GROUP2'
ELSE 'GROUP3'
END
FROM customers



SUBQUERY (쿼리 중첩)
SELECT CustomerId, CustomerId = (SELECT CustomerId FROM customers WHERE CustomerId = 2)
FROM customers
WHERE CustomerId < 6



EXISTS (존재여부 확인)
SELECT EmployeeId
FROM employees WHERE EXISTS (
  SELECT 
  FROM c.supportRepId = e.EmployeeId
  )
ORDER BY EmployeeId



FROM (서브 쿼리 사용 가능)
SELECT *
FROM (
  SELECT CustomerId
  FROM customers
  WHERE CustomerId < 10
)



<쿼리문을 이용하여 데이터베이스 접근>
  
  
const db = require('../db');

module.exports = {
  orders: {
    get: (userId, callback) => {	//해당유저가 작성한 모든 주문 가져오기
      // const sql = `SELECT orders.id, orders.total_price, orders.created_at, order_items.order_quantity, items.name, items.price, items.image
      // FROM orders 
      // INNER JOIN users ON (orders.user_id = users.id)
      // INNER JOIN order_items ON (orders.id = order_items.order_id)
      // INNER JOIN items ON (order_items.item_id = items.id)
      // WHERE (users.id =${userId})`;

      // db.query(sql, (err, result) => {
      //   callback(err, result);
      // });
      
      const queryString = `SELECT orders.id, orders.created_at, orders.total_price, items.name, items.price, items.image, order_items.order_quantity FROM items
      INNER JOIN order_items ON (order_items.item_id = items.id)
      INNER JOIN orders ON (orders.id = order_items.order_id)
      WHERE (orders.user_id = ?)`;

      const params = [userId];

      db.query(queryString, params, (error, result) => {
        callback(error, result);
      });
    },

    
    post: (userId, orders, totalPrice, callback) => {	//해당 유저의 주문요청을 데이터베이스에 생성
      
      // orders: [{ quantity: 1, itemId: 2 },{ quantity: 1, itemId: 4 }]

      const sql1 = `INSERT INTO orders (user_id, total_price) VALUES (${userId}, ${totalPrice})`;
      db.query(sql1, (err, result) => { 
        const sql2 = `INSERT INTO order_items (order_id, item_id, order_quantity) VALUES ?`;
        const values = orders.map((el) => [
          result.insertId,
          el.itemId,
          el.quantity,
        ]);

        db.query(sql2, [values], (err, result) => {
          callback(err, result);
        });
      });
    },
  },

  
  items: {
    get: (callback) => {	//모든 상품 데이터를 가져오기
      const query = "SELECT * FROM items";
      db.query(query, (err, result) => {
        callback(err, result);
      });
    },
  },
};



[데이터베이스] ORM


  • MCV : 디자인 패턴 중 하나로 Model, View, Controller의 약자. 하나의 애플리케이션, 프로젝트를 구성할 때 구성요소를 세가지의 역할로 구분한 패턴



    controller를 조작하면 controller는 model을 통해서 데이터를 가져오고, 정보를 바탕으로 시각적인 표현을 담당하는 view를 제어해 사용자에게 전달

  • 용어

    • Model : data, 정보들을 뜻하며 정보들의 가공을 책임지는 컴포넌트. 사용자가 편집하길 원하는 모든 데이터를 갖고 있어야함.
    • View : 데이터 기반으로 사용자들이 볼 수 있는 화면, 사용자 인터페이스 요소
    • Controller : 데이터와 사용자 인터페이스 요소들을 잇는 다리 역할 (Model, View에 대해서 알고 있어야 함)


  • sequlize를 이용하여 MVC패턴 적용

sequelize, sequelize-cli 설치 후 ORM 설정

  1. config.json에 database 설정 이름을 mysql에서 database생성
  2. Model 생성 
"npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string"

  3. migrations을 통해 테이블 생성 
"npx sequelize-cli db:migrate"




Associations을 이용한 join table 구현

  1. 새 테이블의 모델 파일과 마이그레이션 생성
  2. migrations을 통해 테이블 생성
  3. 새로운 마이그레이션 파일을 생성
"sequelize migration:create --name 파일명"

  4. 만든 파이그레이션 파일에 FK만들기
  5. 모델 파일 Association을 belongsTo, hasMany로 정의
  6. 생성하기
"sequelize db:migrate"

             

             
             
[4]

  'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {	=> sequelize db:migrate
  
    await queryInterface.addColumn('urls', 'userId', Sequelize.INTEGER);

    await queryInterface.addConstraint('urls', {
      fields: ['userId'],
      type: 'foreign key',
      name: 'custom_fkey_constraint_name',
      references: {
        table: 'users',
        field: 'id'
      },
      onDelete: 'cascade',
      onUpdate: 'cascade'
    })
  },

  down: async (queryInterface, Sequelize) => {	  => sequelize db:migrate:undo
  
    await queryInterface.removeConstraint('urls', 'custom_fkey_constraint_name')
    await queryInterface.removeColumn('urls', 'userId')

    // await removeColumn('urls', 'userId')
   // FK먼저 지우고 column지움
  }
};




[5]

url.js
    static associate(models) {
      url.belongsTo(models.user, {
        foreignKey: "userId"
      })
    }



user.js
    static associate(models) {
      user.hasMany(models.url)
    }




또는 models/index.js에서 한번에 처리

const { url, user } = sequelize.models;
url.belongsTo(user);
user.hasMany(url);





*만약에 sequelize에 오류가 발생한다면?*
  
  sudo npm install -g sequelize
  sudo npm i -g sequelize-cli 재설치


  node_modules/.bin/sequelize db:migrate로 실행



[데이터베이스] NoSQL


  • NoSQL기반의 비관계형 데이터베이스 사용 경우 => [MongoDB 도큐먼트 이용]
    • 비구조적인 대용량의 데터를 저장하는 경우
    • 클라우드 컴퓨팅 및 저장공간을 최대한 활용하는 경우 [SQL:수직적 확장, NoSQL:수평적 확장]
    • 빠르게 서비스를 구축하고 데이터구조를 자주 업데이트 하는 경우

JSON형식은 읽기 쉽고, 사용하기 편리한 형태.
but, 텍스트 형식이기 때문에 읽기 쉽지만 파싱이 느리고, 메모리 사용이 비효율적 기본데이터 타입만을 지원하기 때문에 사용할 수 있는 데이터 타입 제약 => 문제 해결을 위해 BSON 형식 도입



Export

BSON: mongodump --uri "<Atlas Cluster URI>"

JSON: mongoexport --uri "<Atlas Cluster URI>"
		  --collection=<collection name>
  		  --out=<filename>.json
  
  
Import

BSON: mongorestore --uri "<Atlas Cluster URI>"
		   --drop dump

JSON: mongoimport --uri "<Atlas Cluster URI>"
		  --drop=<filename>.json	==> drop은 선택적 사용
          
          
    
         
- mongodump: BSON형식의 데이터를 내보내기
- mongorestore: mongodump가 생성한 BSON형식 데이터 가져오기


- mongoexport: JSON형식의 데이터를 아틀라스 클러스터에서 내보낼 때., 내보낼때 아틀라스 클러스터에 컬렉션을 더하는 대신 외부에 데이터 복사본 만듬
- mongoimport: 데이터베이스를 아틀라스 클러스터로 가져오기. JSON일수도 있고, 다른 데이터 형식일 수도 있음



  • MongoDB CRUD
-CREATE


_id는 필수. but, 작성하지 않아도 자동 생성(ObjectId로 생성) => 같은 내용이여도 다른 아이디 값인 경우 다르다고 인식
insert를 이용(객체 담아서), 다수의 도큐먼트 삽입시 배열안에 작성

db.test.insert([{"_id":"1", "test":"1"}, {"_id":"1", "test":"2"}, {"_id":"3", "test":"3"}], {"ordered":false})

_id값이 중복인 부분은 삽입하지 않음. 인덱스 순서 삽입이 진행
{"ordered":false} 작성시 앞에서 오류가 발생하면 오류부분을 제외하고 진행





-READ


find를 이용하여 원하는 데이터를 찾음
show dbs => use databasename => db.names.find({"content":"hi"})

전체를 보고 싶은 경우 db.names.find().pretty()  *pretty를 작성시 객체로 보기 편하게 나옴*
갯수를 알고 싶은 경우 db.names.find().count()
조건에 부합한 한개 만 db.names.findOne({"_id": 12513513})





-UPDATE


$inc: 다양한 필드 값을 동시에 업데이트 가능
$set: 주어진 필드 지정된 값을 업데이트. 존재하지 않는 필드명 작성시 새롭게 추가됨
$push: 값이 배열로 이뤄져있을 때 요소 추가

db.names.updateMany({"key":"1"}, {"$inc": {"pop":10}})  *한 번에 여러 데이터 추가 시*
db.names.update({})	*한 번에 하나 데이터 추가 시*





-DELETE


db.name.deleteMany({"content":"hi"})
db.name.deleteOne({"content":"hi"})

컬렉션을 삭제하기 위해서 drop을 사용
db.collection_name.drop()





-비교 연산자


$eq =   $ne ≠   $gt >   $lt <   $gte >=   $lte <=
db.names.fin({"during": {"$lte":70}, "content":{"$ne":"hi"})
              




-논리 연산자


$and: 모든 쿼리절과 일치하는가
$or: 쿼리절 중 하나라도 일치하는가
$nor: 모든 쿼리절과 일치하지 않는가
              
=> {<operator>:[{state1}, {state2}, ...]}
              
              
             
$not: 쿼리와 일치하지 않는가
             
=> {$not:{state}}





-표현 연산자


$expr
{"$expr":{"$eq":["$start", "$end"]}}	=> $는 해당 필드의 값을 뜻함





-배열 연산자와 projection


배열에 담아서 조건으로 찾을 경우 순서가 다르면 결과값이 나오지 않음
$all을 사용하면 배열요소의 순서와 상관없이 지정 요소를 포함된 결과 도출
$size를 이용해서 배열의 길이 제한 가능

{"$size": 숫자, "$all":{"필드명", ...}}
 
0, 1을 사용해 결과를 표시할지 말지 설정 가능[0, 1을 혼합해서 사용 불가, but id만 예외적으로 가능]
배열필드에서 $eleMatch를 사용해서 결과값 도출

0개의 댓글