본문 바로가기
타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js)/주간정리

데브코스 6주차 정리

by 슈크림 붕어빵 2023. 12. 25.

 

데이터베이스란?

데이터를 통합하여 효율적으로 관리하기 위한 데이터 집합체이다.

데이터를 구조화하여 관리함으로써 데이터 중복을 막고, 효율적이고 빠른 데이터 연산을 가능하게 함.

 

데이터베이스의 특징

  1. 실시간 접근성
  2. 지속적인 변화
  3. 동시 공유
  4. 내용에 대한 참조
  5. 데이터 논리적 독립성

데이터베이스의 장단점

장점

  • 데이터 종속성이 최소화된다.
  • 데이터 중복성이 감소한다.
  • 데이터 동시 공유가 가능하다. 
  • 데이터 일관성 유지가 쉽다.
  • 데이터 무결성 유지가 쉽다.
  • 데이터 보안이 향상된다.
  • 표준화된 데이터 접근이 가능하다.
  • 데이터 가용성이 향상된다.
  • 응용 프로그램의 개발 비용이 감소한다.

무결성: 데이터의 정확성, 일관성 - 데이터에 결함이 없는 상태, 즉 데이터를 정확하고 일관되게 유지하는 것을 의미합니다.

트랜잭션

데이터베이스의 상태를 변환시키는 기능을 수행하기 위한 하나이상의 쿼리를 모아 놓은 작업 단위

트랜잭션 특징 ACID - 하나의 트랜잭션의 안전성을 보장하기 위해 필요한 성질

  • 일관성: 트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것을 의미한다.
    • 예시: 모든 상품은 상품명이 있어야한다는 데이터베이스의 제약
    • 일관성 위반 예 - 상품명 없는 새로운 고객 추가 쿼리, 기존 상품 상품명 삭제 쿼린
  • 지속성 : 완료(commit) 된 트랜잭션은 데이터베이스에 영구적으로 보존되어야 한다.
    • 입출금을 성공적으로 실행한 뒤, 은행 데이터베이스에 오류가 발생하더라도 내역은 남아야함.
    • 로그를 기록하기 전에 오류에 의해 종료된다면, 입출금은 실패로 돌아가며 트랜잭션 수행전으로 돌아간다.
  • 원자성 : 한 트랜잭션의 연산들이 모두 성공하거나, 모두 실패하는 성질. 쪼갤 수 없음.
    • 예) 이체시 출금 후 입금이 모두 성공해야함. 
  • 격리성 :  하나의 트랜잭션이 다른 트랜잭션에게 영향을 주지 않도록 격리되어 수행되어야한다.

 

단점

  • 시스템 운영비용이 증가한다.
  • 시스템 관리 부담이 높아진다.
  • 시스템 취약성이 증가한다.

RDBMS - Relational DBMS

특징

데이터끼리 연관을 지어서 찾는다.

장점

중복됨에 따른 불편함을 해결해줄 수 있다.즉, 테이블을 쪼개서 (정규화) 중복을 없애준다.

 

primary key(PK) : 해당 테이블의 각 row(행)을 유일하게 구분할 수 있는 key

foreign key (FK) : 다른 테이블의 데이터을 받아오기 위한 키 (타 테이블 PK)

 

테이블간의 연관관계

예시 1 ) 게시판

 

 

테이블이 하나일 때는 같은 user가 작성한 경우 이름 생년월일이 중복되는 것을 확인할 수 있다. 아래와 같이 테이블을 분리하면 중복되는 값과 종속들을 없앨 수 있다. 

 

아래와 같이 나눴을 때, 다음 상황을 생각해보자. 나중에 성은 user가 이름을 바꾼다면 모든 성은을 찾아 하나하나 바꿔야할 것이다.

 

하지만 아래처름 FK로 userid를 연결해놓으면 user 테이블에서 한번만 변경해도 된다. 또한 user가 다른 테이블에도 있다면, id만 연결되어 있으므로 user 테이블만 수정하면 되므로 데이터의 일관성이 더욱 잘 유지될 수 있다. 

 

연관관계 - 게시글 / 사용자

 

  • 사용자 1명 : 게시글 N => 1:N (O)
  • 게시글 1개 : 사용자 1 => 1:1
  • 게시글 테이블을 보고 사용자 데이터를 확인 (o) : 연관관계
  • 사용자 테이블을 보고 게시글 데이터 확인 (x)

 

예시2) 유튜브 채널

 

사용자는 여러개의 채널을 만들 수 있으며 이에 따라 user_id, pw,name이 중복되고 있다.

테이블을 나눔으로써, 중복이 해소된다.

 

연관관계 - 채널 / 사용자

  • 사용자 1명 : 채널 N => 1:N (O)
  • 채널1개 : 사용자 1 => 1:1
  • 채널 테이블을 보고 사용자 데이터를 확인 (o) : 연관관계
  • 사용자 테이블을 보고 채널 데이터 확인 (x)

mariadb로 만들어보기

1. 테이블 만들기

CREATE TABLE users( 
	id INT NOT NULL AUTO_INCREMENT, 
	name VARCHAR(30) NOT NULL, 
	birth DATE, 
	PRIMARY KEY (id) );

확인해보기

  • SHOW tables;
  • DESC users

 

CREATE TABLE posts(
    -> id INT NOT NULL AUTO_INCREMENT,
    -> title VARCHAR(30) NOT NULL,
    -> content VARCHAR(200) NOT NULL,
    -> created_at TIMESTAMP DEFAULT NOW(),
    -> PRIMARY KEY(id));

 

AUTO_INCREMENT : INSERT할 때 입력해주지 않아도 자동으로 ID가 올라간다.

NOT NULL: INSERT할 때 입력하지 않으면 안된다. NULL로 INSERT하면 안된다.

name이 NOT NULL인 경우

 

 

DEFAULT : 값이 안들어올 때, 기본값으로 세팅

  • DEFAULT  NOW(): INSERT할 때 입력하지 않으면 현재 시간으로 들어간다.

 

시간

1. DATE 

날짜

YYYY-MM--DD

2.DATETIME : 

날짜+시간

YYYY-MM-DD HH:MM:SS(24시간제)

3. TIME

시간

HH:MM:SS

4.TIMESTAMP : 자동입력을 도와줌

날짜+시간

YYYY-MM-DD HH:MM:SS(24시간제)

 

UTC: 한국 시간 -9 

 

DATETIME VS TIMESTAMP

DATETIME은 자동입력이 안되고 TIMESTAMP는 자동입력이 된다.

2. 데이터 삽입하기

INSERT INTO posts (title,content) VALUES ("생활 꿀팁","여행 꿀팁");
  • AUTO_INCREMENT인 id는 자동으로 1씩 올라감.
  • DEFAULT  NOW() 인 created_at은 하나씩 자동으로 현재 시간이 기록됨.

3. 컬럼 추가하기 - 정보 수정하기(UPDATE) , FK 설정

3-1) 수정일자 추가 - update할 때 시각

ALTER TABLE posts
ADD COLUMN updated_at DATETIME
DEFAULT NOW()
ON UPDATE NOW();

 

 

결과는 위와 같다. update될 때의 timestamp가 들어간다.

 

원래 updated_at은 비어있었기 때문에 현재 시각이 updated_at으로 들어갔다.

 

update해서 확인해보기

UPDATE posts SET content = "여행 기록" WHERE id = 2;

 

3-2) user_id 추가 - FK

 

  • 컬럼 추가
 ALTER TABLE posts 
 ADD COLUMN user_id INT;
  • user_id를 FK로 
ALTER TABLE posts 
ADD FOREIGN KEY(user_id)
REFERENCES users(id);

KEY가 MUL로 바뀐 것을 볼 수 있다.

 

4. JOIN

가상으로 테이블을 만들어서 보여주는 Join

 SELECT * FROM posts 
 LEFT JOIN users 
 ON posts.user_id = users.id;

 

참고

 

INNER JOIN : 교집합

OUTER JOIN : 합집합

 

LEFT OUTER JOIN

SELECT * 
from A LEFT OUTER JOIN B on A.a = B.b;

A의 모든 행과 B의 A와 공통인 행을 뽑는다.

 

RIGHT OUTER JOIN

SELECT * 
from A RIGHT OUTER JOIN B on A.a = B.b;

 

A에서 B와 공통된 부분과 B의 모든 정보가 포함된다.

 

FULL OUTER JOIN

MYSQL에서는 지원하지 않는다. UNION으로 공통되지 않은 행은 NULL 처리를 해서 이용해보자.

 

 

 

workbench로 유튜브 만들기

1. 스키마 만들기

 

2. 테이블 만들기

FK 설정

- 아래에 Foreingn Key에서 설정 가능하다.

 

3. 데이터 삽입하기

표에서 직접 입력이 가능하다.

4. vscode에서 연동하기

https://www.npmjs.com/package/mysql2

 

mysql2

fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS. Latest version: 3.6.5, last published: a month ago. Start using mysql2 in your project by running `npm i mysql2`. There are 4032 other projects in the npm r

www.npmjs.com

 

// get the client
const mysql = require("mysql2");

// create the connection to database
const connection = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "root",
  database: "youtube",
});

// simple query
connection.query("SELECT * FROM `users`", function (err, results, fields) {
  console.log(results); // results contains rows returned by server
  console.log(fields); // fields contains extra meta data about results, if available
});

 

결과

[
  { id: 1, email: 'bsu0404@naver.com', name: '성은', pw: 'qwer123' },
  {
    id: 2,
    email: 'ChimChakMan@naver.com',
    name: '침착맨',
    pw: 'asdf1234'
  },
  { id: 3, email: 'friend@naver.com', name: '친구', pw: 'zxcv1234' }
]
[
  `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `email` VARCHAR(45) NOT NULL UNIQUE_KEY,
  `name` VARCHAR(45) NOT NULL,
  `pw` VARCHAR(30) NOT NULL
]

 

=> result는 jsonArray 형태로 결과를 보여줌

=> feilds는 테이블 정보를 보여줌.

 

jsonArray에서 값 빼오기

connection.query("SELECT * FROM `users`", function (err, results, fields) {
  let { id, email, name, join } = results[0]; // results contains rows returned by server
  console.log(id);
  console.log(email);
  console.log(name);
  console.log(join);
  console.log(fields); // fields contains extra meta data about results, if available
});

다음과 같이 값을 가져오기는 하나, 시간이 한국 시간과 맞지 않는다. utc 기준으로, 9시간이 더해짐.

 

SET GLOBAL time_zone = "Asia/Seoul";

 

위 명령어를 치고 다시 확인해보았는데 데이터베이스와 vscode에서 받다온 값이 일치하지 않을 뿐만 아니라, 현재 시각이 저장되지도 않는다.

SET GLOBAL time_zone = "Asia/Seoul";

새로 삽입할 때, 현재 시각이 들어가지는 않지만 데이터베이스와 vscode에서 받아온 값은 같은 것을 확인할 수 있다..

 

다음 명령어로 time_zone을 확인해보고 둘 다 바뀌어있지 않다면 다시 SET을 해주자.

=> SET GLOBAL time_zone = "Asia/Seoul";

아직 node.js에서는 바뀌지 않았다.

 

 

node.js에서 시간형식 맞추기+마지막의 소수점 없애기 dateStrings: true,

const connection = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "root",
  database: "youtube",
  dateStrings: true,
});

 

 

핸들러에 추가하기, 쿼리에 값 넣는 법

1. "${}"

router
  .route("/users")
  .get((req, res) => {
    //회원개별조회
    let { email } = req.body;
    console.log(email);
    conn.query(
      `SELECT * FROM users WHERE email = "${email}"`,
      function (err, results, fields) {
        console.log(results);
        if (results) {
          res.status(200).json(results);
        } else {
          res.status(404).json({
            message: "회원이 없습니다.",
          });
        }
      }
    );

  })

2.  ? 사용하기

1번과 같이 했는데 강사님이 다음과 같이 ?를 사용하는 법을 알려주셨다. 

router
  .route("/users")
  .get((req, res) => {
    //회원개별조회
    let { email } = req.body;
    console.log(email);
    conn.query(
      `SELECT * FROM users WHERE email = ?`,
      email,
      function (err, results, fields) {
        console.log(results);
        if (results) {
          res.status(200).json(results);
        } else {
          res.status(404).json({
            message: "회원이 없습니다.",
          });
        }
      }
    );
  })

 

여러개 받는 법 - 배열

//회원가입
router.post("/join", (req, res) => {
  let { email, pw, name } = req.body;
  let user;
  if (email && pw && name) {
    conn.query(
      `INSERT INTO users (email,name,pw) VALUES (?,?,?,?)`,
      [email, name, pw],
      function (err, results, fields) {
        res.status(200).json(results);
      }
    );
  } else {
    res.status(400).json({
      message: `id, 비밀번호, 이름은 필수 항목입니다.`,
    });
  }
});

 

 

리팩토링 해준 부분

  • 주석은 너무 많을 필요가 없다.
  • if문을 다르게 쓰는 법 - 논리연산자 이용
  • sql 문 관리
  • ? 에 들어갈 value 빼주기

유효성검사

valid action

사용자가 입력한 값의 유효성(=타당성)을 확인하는 것

 

ex

  • user_id: body에 값이 있어야한다, 숫자
  • name: body에 값이 있어야한다, 문자열

외부모듈 express-validator

https://www.npmjs.com/package/express-validator

 

express-validator

Express middleware for the validator module.. Latest version: 7.0.1, last published: 8 months ago. Start using express-validator in your project by running `npm i express-validator`. There are 10052 other projects in the npm registry using express-validato

www.npmjs.com

유효성 검사 사용하기

const { body, validationResult } = require("express-validator");
  • validationResult : 문제여부
 const err = validationResult(req);
  • err는 배열 형식으로 넘어온다. err.isEmpty()가 참이라면 문제가 없는 것

 

유효성 검사 err 확인하기

1. 공통함수를 만들어서 사용해주기

function validate(req, res) {
  const err = validationResult(req);
  if (!err.isEmpty()) {
    return res.status(400).json(err.array());
  }

콜백함수의 해당 부분에서 호출해주면 잘 수행된다.

 

2. 미들웨어로 만들어주기, 

const validate = (req, res, next) => {
  const err = validationResult(req);
  if (err.isEmpty()) {
    return next();
  } else {
    return res.status(400).json(err.array());
  }
};

 

리스폰스로 해결하려나보다! 로 인식할 수 있다. 다음 콜백함수로 넘어가야한다는 것을 알려줘야한다.

next를 이용한다.

미들웨어를 콜백함수 호출 전 검사하는 부분에서 불러온다.

ex

.get(
    [
      body("user_id")
        .notEmpty()
        .isInt()
        .withMessage("user_id는 숫자여야합니다."),
      validate,
    ],
    (req, res) => {
      //회원의 채널 조회

      const err = validationResult(req);
      if (!err.isEmpty()) {
        return res.status(400).json(err.array());
      }
      let { user_id } = req.body;

      sql = `SELECT * FROM channels WHERE user_id = ?`;

      conn.query(sql, user_id, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }
        if (results.length) {
          res.status(200).json(results);
        } else {
          res.status(404).json({
            message: "해당 id의 회원의 채널이 없습니다.",
          });
        }
      });
    }
  )

 

참고 1 

  • .isEmail() : email인지 검사
  • .isStrongPassword()

 

참고 - 미들웨어란?

  • 미들웨어 함수는 req객체, res객체, 그리고 어플리케이션 요청-응답 사이클 도중 그 다음의 미들웨어 함수에 대한 엑세스 권한을 갖는 함수이다. 즉, 요청 전에 처리할 수 있는 어떤 것이다.
  • next를 통해 미들웨어는 순차적으로 처리된다.
  • 따라서 유효성 검사를 할 때, 미들웨어인 validate에서 next를 해주지 않으면 err.isEmpty()인 경우, 즉 유효성 검사를 통과한 경우에는 핸들러로 넘어가지 않는다.