기억의 실마리
2023. 1. 29. 23:25

데이터베이스

데이터베이스란 여러 사람이나 프로그램이 데이터를 쉽게 공유하기 위해
체계적으로 관리되는 데이터의 집합을 칭한다.

 

DBMS - 데이터베이스 관리 시스템

  • SQL DB (관계형 데이터베이스)
    행과 열로 이루어저 있다.
    엑셀처럼(표형식) 스키마(Schema: 구조와 제약 조건에 관한 명세)
    형식에 맞춰서 데이터가 저장된다.
    그러므로 체계적이고 안정적으로 데이터를 관리할 수 있다.
    대표적으로 PostgreSQL, MySQL, OracleDB, SQLite, MariaDB 가 있다.

  • NoSQL DB (관계형이 아닌 모든 데이터베이스)

    Key-Value : 파이썬의 딕셔너리처럼 키와 벨류로 데이터를 저장하는
    가장 단순한 데이터베이스다. ex) { 'name' : 'zeriong' }
    단순한 만큼 속도가 빠르고 주로 캐싱, 세션관리, 리더보드, 실시간분석 등에
    주로 사용된다.
    대표적인 제품으로 redis , amazon DynamoDB 등이 있다.

    Document : key와 value가 있는건 마찬가지 이지만 json의 형식으로 데이터를
    저장할 수 있는 Document형식의 데이터베이스다.Document형식이기 때문에
    스키마가 변경되는 것에 유연하게 대처할 수 있다.
    대표적으로 mongoDB, Amazon DocumentDB, Firestore 등이 있다.

    Gracph : 노드의 속성별로 데이터를 저장하는 방식이다.
    페이스북같은 소셜 네트워크 혹은 추천엔진을 위한 데이터베이스다.
    대표적으로 neo4j, Amazon Neptune, Giraph 등이 있다.

 

 

MySQL 시작하기

https://dev.mysql.com/downloads/installer/

 

MySQL :: Download MySQL Installer

Select Operating System: Select Operating System… Microsoft Windows Select OS Version: All Windows (x86, 32-bit) Windows (x86, 32-bit), MSI Installer 8.0.32 2.4M (mysql-installer-web-community-8.0.32.0.msi) MD5: 0f882590f8338adc614e9dc5cb00ca0b | Signatu

dev.mysql.com

링크를 통해서 용량이 높은 (mysql-installer-community-8.0.32.0.msi) 를 다운로드한다.

 

기본설정

  1. MySQL Workbench를 실행하고 localhost에서 설정했던 비밀번호로 root 계정에 접속한다.
  2. Add SQL아이콘을 눌러서 SQL을 추가할 수 있는 SQL File을 띄워준다.

  3. 아래의 코드를 입력하여 스키마를 만들어준다.
    CREATE SCHEMA `mydatabase` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ;
    위 코드에서 utf8mb4 는 한글을 포함한 전세계문자와 이모티콘사용이 가능해진다.
    utf8mb4 _ general _ ci 는 정확도가 가장 높지는 않지만 정렬속도가 빠른 코드이다.

원하는 테이블에서 쿼리를 실행하고자 할땐 반드시 스키마리스트에서

해당 테이블을 더블클릭한 후 +SQL을 눌러야 해당 테이블내에서 쿼리를

실행시켜 원하는 데이터를 가져올 수 있다.

 

MySQL서버 파워쉘로 열기

( CLI = Comend Line Interface )

C:\Program Files\MySQL\MySQL Server 8.0\bin 에서
./mysql -u root -p
입력한다. 내용은
-u 유저네임은 root이고 -p 패스워드를 사용한다는 뜻이다.
이 후 지정한 패스워드를 입력하고

show databases; 를 입력하면
데이터베이스 테이블이 쭉 나온다.

이 후 사용할 테이블을 use와 함께 입력하면 사용가능하다.
ex) use mydatabase

이 후에
Database changed
문구가 뜨면 쿼리를 사용해주면 결과가 선택한 테이블 내의

데이터가 적용되어 나온다.

 

 

CREATE TABLE -  테이블 만들기

CREATE TABLE people ( -- 테이블이름은 people
  person_id INT, -- 콜룸데이터명은 person_id이고 int속성이다.
  person_name VARCHAR(10), -- VARCHAR 가변길이 문자열 10byte까지
  age TINYINT, -- TINYINT 범위: 0~255 숫자
  birthday DATE -- 날짜
);

 

ALTER TABLE - 테이블 변경

-- 테이블명 변경
ALTER TABLE people RENAME TO  friends,

-- 컬럼 자료형 변경
CHANGE COLUMN person_id person_id TINYINT,

-- 컬럼명 변경
CHANGE COLUMN person_name person_nickname VARCHAR(10), 

-- 컬럼 삭제
DROP COLUMN birthday,

-- 컬럼 추가
ADD COLUMN is_married TINYINT AFTER age;

 

DROP TABLE - 테이블 삭제

DROP TABLE friends;
-- friends테이블을 삭제한다.

 

INSERT INTO - 데이터 삽입

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES (1, '홍길동', 21, '2000-01-31');
-- 콜룸명과 순서에 맞게 VALUES에 넣어주면 데이터가 입력된다.

INSERT INTO people
  VALUES (2, '전우치', 18, '2003-05-12');
-- 모든 컬럼에 값 넣을 때는 컬럼명들 생략 가능

INSERT INTO people
  (person_id, person_name, birthday)
  VALUES (3, '임꺽정', '1995-11-04');
-- 일부 컬럼에만 값 넣기 가능 (NOT NULL은 생략 불가)

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES (1, '임꺽정', '스물여섯', '1995-11-04');
-- 자료형에 맞지 않는 값은 오류 발생

INSERT INTO people
  (person_id, person_name, age, birthday)
  VALUES 
    (4, '존 스미스', 30, '1991-03-01'),
    (5, '루피 D. 몽키', 15, '2006-12-07'),
    (6, '황비홍', 24, '1997-10-30');
-- 여러 행을 한 번에 입력 가능

 

테이블 생성시 제약 넣기

CREATE TABLE people (
  person_id INT AUTO_INCREMENT PRIMARY KEY,
  person_name VARCHAR(10) NOT NULL,
  nickname VARCHAR(10) UNIQUE NOT NULL,
  age TINYINT UNSIGNED,
  is_married TINYINT DEFAULT 0
);

--AUTO_INCREMENT     = 새 행 생성시마다 자동으로 1씩 증가
--PRIMARY KEY	     = 중복 입력 불가, NULL(빈 값) 불가
--UNIQUE	      = 중복 입력 불가 (NULL값 가능)
--NOT NULL	      = NULL(빈 값) 입력 불가
--UNSIGNED	      = (숫자일시) 양수만 가능
--DEFAULT	      = 값 입력이 없을 시 기본값

= PRIMARY KEY (기본키) =
1. 테이블마다 하나만 가능
2. 기본적으로 인덱스 생성 (기본키 행 기준으로 빠른 검색 가능)
3. 보통 AUTO_INCREMENT와 함께 사용
(각 행을 고유하게 식별 가능 - 테이블마다 하나씩 둘 것)

 

 

숫자 자료형

< 숫자 자료형 표 >

자료형 바이트 SIGNED UNSIGNED
TINYINT 1 -128 ~ 127 0 ~ 255
SMALLINT 2 -32,768 ~ 32,767 0 ~ 65,535
MEDIUMINT 3 -8,388,608 ~ 8,388,607 0 ~ 16,777,215
INT 4 -2,147,483,648 ~ 2,147,483,647 0 ~ 4,294,967,295
BIGINT 8 -2^63 ~ 2^63 - 1 0 ~ 2^64 - 1

 

고정 소수점(Fixed Point) 수

  • 좁은 범위의 수 표현이 가능하며, 정확한 값을 칭한다.
  • DECIMAL( s, d ) 실수 부분 총 자릿수( s ) & 소수 부분 자릿수 ( d ) s 최대 65

    ex: DECIMAL(5, 2) 1번째인자가 5라서 숫자 5자릿수,
    2번째 인자가 2라서 소수점 2자릿수까지만 가능하다.
    12345, 1234.5, 123.45 이런식으로 가능하다 숫자가 5자리면 된다.

부동 소수점(Floating Point) 수

  • 넓은 범위의 수 표현 가능하며, 정확하지 않은 값이지만
    일반적으로 충분히 정확하다.

  • FLOAT
    -3.402...E+38 ~ -1.175...E-38 , 0 , 1.175...E-38 ~ 3.402...E+38

  • DOUBLE
    -1.797...E+308 ~ -2.225E-308 , 0 , 2.225...E-308 ~ 1.797...E+308

 

문자열

  • CHAR( s )
    설명 :  고정 사이즈 (남는 글자 공백으로 채움)
    차지하는 바이트 :  s (고정값)
    MAX 바이트 :  255

  • VARCHAR ( s )
    설명 :  가변 사이즈(남는 글자를 제외하고 맞춰진다.)
    차지하는 바이트 :  실제 글자 수(최대 s) + 1 (글자수 정보)
    MAX 바이트 :  65,535

 

VARCHAR와 CHAR 이해하기

검색시 CHAR가 더 빠르다
VARCHAR 컬럼 길이값이 4글자보다 적을 경우 CHAR로 자동 변환된다

VARCHAR는 남는 글자수를 없애는 처리를 추가로 하고 기본적으로

글자수 정보를 반드시 넣어야 함으로 1바이트가 반드시 추가된다. 

ex)
CHAR(4) :  { VALUE: 'ABCD' } = 4bytes
VARCHAR(4) :  { VALUE: 'ABCD' } = 5bytes

이렇게 상황에 따라 경제적으로 사용하는 것이 효율적이다.

 

< 텍스트 / 최대바이트 >

TINYTEXT 255
TEXT 65,535
MEDIUMTEXT 16,777,215
LONGTEXT 4,294,967,295

 

날짜와 시간

  • DATE
    YYYY-MM-DD

  • TIME
    HHH:MI:SS
    HHH: -838 ~ 838까지의 시간
  • DATETIME
    YYYY-MM-DD HH:MI:SS
    입력된 시간값을 그 자체로 저장 ex) String(1995-05-18)=> Date(1995-05-18)

  • TIMESTAMP
    YYYY-MM-DD HH:MI:SS
    MySQL이 설치된 컴퓨터의 시간대를 기준으로 저장

DATETIME와 TIMESTAMP의 사용은?

  • 시간 데이터를 가감없이 기록할 때 DATETIME
    역사나 계획일자처럼 정해져있는 시간을 기록할때 사용한다.
  • 시간 자동기록, 국제적인 서비스를 할 경우 TIMESTAMP 사용
    국제적인 sns는 시차가 나라별로 다르기 때문에 사용한다.

 

마치며...

다음 포스팅에도 간단한 예시들과 적당한 설명을 포스팅 할 예정이다. 생각보다 MySQL과 데이터베이스에 대해서는 쓸 내용이 많은 것 같다...

 

예시자료 출처 -  https://www.yalco.kr/lectures/sql/

 

얄코 홈

어려운 프로그래밍 개념들을 쉽게 설명해주는 유튜브 채널 '얄팍한 코딩사전'. 영상에서 다 알려주지 못한 정보들이나 자주 묻는 질문들의 답변들, 예제 코드들을 얄코에서 확인하세요!

www.yalco.kr

 

'Backend > MySQL' 카테고리의 다른 글

[ MySQL ] 상관,비상관 서브쿼리와 JOIN  (0) 2023.02.02
[ MySQL ] 데이터베이스 SELECT  (0) 2023.01.30
2023. 1. 24. 15:14

Nest.js

Nest.js는 Node.js기반의 웹  API 프레임워크로서 Express, Fastify 프레임워크를

래핑하여 동작한다. 기본적으로는 Exress를 제공한다.

 

Nest.js의 장점

Node.js는 사용하기에 있어 편리하고 뛰어난 확장성을 가지고 있다.
이러한 특성으로 SW의 품질이 일정하지않고 라이브러리를 찾기위해
사용자가 많은 시간을 할애해야 한다. 반면 Nest.js는 데이터베이스,
ORM, Configuration, 유효성 검사 등 수많은 기능을 기본으로 제공하고 있다.
그러면서도 필요한 라이브러리를 쉽게 설치가능하기 떄문에

Node.js의 장점도그대로 가지고 있다.

 

 

Nest.js의 특징

Angular로부터 영향을 받아 모듈,컴포넌트 기반으로 프로그램을 작성함으로서

재사용성을 높인다. IoC(Inversion of Control), DI(Dependency Injection),

AOP(Aspect Oriented Programming)와 같은 객체지향 개념을 도입했다.

언어는 타입스크립트를 기본으로 채택하고 있다.

 

사용예시

Nest.js Install

npm i -g @nestjs/cli
//인스톨이 잘 되었는지 터미널에 nest를 입력해서 확인해본다.

인스톨이 잘 된 경우 터미널에 nest를 입력시 nest의 리스트가 나오게 된다.

 

nest new Project
//Project이름을 가진 Default create-nest가 생성된다.

프로젝트를 새로 만들어야 할 경우 Nest.js의 createt기능을 사용하여

Default프로젝트를 만들 수 있다.

 

 

src 폴더 구조

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
      //transform은 요청데이터를 필요한 데이터형태로 바꿔준다. 원래는 string으로 받지만 number로 바꿔주고 있다.
    }),
  );
  await app.listen(3000); //run start시에 사용되는 서버포트이다.
}
bootstrap();

 

app.modules.ts

import { Module } from '@nestjs/common';
import { MoviesModule } from './movies/movies.module';
import { AppController } from './app.controller';

@Module({
  imports: [MoviesModule],
  controllers: [AppController],
  providers: [],
})
export class AppModule {}

 

app.controller.ts

import { Controller, Get } from '@nestjs/common';

@Controller('')
export class AppController {
  @Get()
  home() {
    return 'Welcome to my Movie API';
  }
}

src폴더는 Nest.js에서 root가 되는 구조이며 service.ts파일은 현재 사용예시 구조상 필요하지않고

movies폴더 내부에서 실질적인 구현을 대신하고 있으므로  movies폴더 내부에 service.ts파일이 있다.

 

 

movies 폴더구조

dto

dto는 코드를 간결하게 만들어 줄 수 있고 클라이언트로 부터 request를 받을때

유효성 검사를 해주기 위함이다.

 

create-movie.dto.ts

import { IsNumber, IsOptional, IsString } from 'class-validator';

export class CreateMovieDTO {
  @IsString()
  readonly title: string;

  @IsNumber()
  readonly year: number;

  @IsOptional()
  @IsString({ each: true })
  readonly genres: string[];
}
//DTO를 만드는이유는 코드를 간결하게 해주고 NestJS가 들어오는 쿼리에 대해 유효성을 검사할 수 있게 도와준다.

 

update-movie.dto.ts

import { PartialType } from '@nestjs/mapped-types';
import { CreateMovieDTO } from './create-movie.dto';

export class UpdateMovieDTO extends PartialType(CreateMovieDTO) {}

 

entities

entities는 DataBase에 넣어 줄 스키마를 미리 지정해주는 역할을 한다.

현재 예시는 간단하게 클래스를 지정하여 만들어 주었지만 실제 서비스에서는

typeORM을 사용하여 직접적으로 DB에 스키마를 만들어 줄 수 있으며

이 외에도 Prisma, Sequelize ORM 등이 있다.

movie.entity.ts

export class Movie {
  id: number;
  title: string;
  year: number;
  genres: string[];
}

 

movies 내부

movies.controller.ts

import {
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Patch,
  Body,
} from '@nestjs/common';
import { MoviesService } from './movies.service';
import { Movie } from './entities/movie.entity';
import { CreateMovieDTO } from './dto/create-movie.dto';
import { UpdateMovieDTO } from './dto/update-movie.dto';

@Controller('movies')
export class MoviesController {
  constructor(private readonly moviesService: MoviesService) {}
  /* movies.module.ts에서 @Module을 사용해서 controllers와 providers에서 import시켜주었기 때문에
  constructor(private readonly moviesService: MoviesService)와 같이 타입을 추가하는 것만으로
  같이 사용할 수 있게 되었다. 이것을 dependency injection이라고 한다.
   */

  @Get()
  getAll(): Movie[] {
    return this.moviesService.getAll();
  }

  @Get(':id')
  getOne(@Param('id') movieId: number): Movie {
    //필요한 것이 있을땐 반드시 요청을 해야한다. @Param()을 통해서 요청을 하고 props로 받는 구조다.
    console.log(typeof movieId);
    return this.moviesService.getOne(movieId);
  }

  @Post()
  create(@Body() movieData: CreateMovieDTO) {
    //@Body를 사용해서 클라이언트에서 보낸 object형식의 JSON을 추가할 수 있다.
    return this.moviesService.create(movieData);
  }

  @Delete(':id')
  remove(@Param('id') movieId: number) {
    return this.moviesService.deleteOne(movieId);
  }

  @Patch(':id') //데코레이터 Put과 Patch두가지가 있는데 Put은 모든 리소스를 받아오기 때문에 적합하지 않을 수 있다.
  patch(@Param('id') movieId: number, @Body() updateData: UpdateMovieDTO) {
    return this.moviesService.update(movieId, updateData);
  }

 

movies.module.ts

import { Module } from '@nestjs/common';
import { MoviesController } from './movies.controller';
import { MoviesService } from './movies.service';

@Module({
  controllers: [MoviesController],
  providers: [MoviesService],
})
export class MoviesModule {}

 

movies.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entity';
import { CreateMovieDTO } from './dto/create-movie.dto';
import { UpdateMovieDTO } from './dto/update-movie.dto';

@Injectable()
export class MoviesService {
  private movies: Movie[] = [];

  getAll(): Movie[] {
    return this.movies;
  }
  //가짜 데이터베이스이며, 진짜데이터베이스에서는 Query를 가져온다.

  getOne(id: number): Movie {
    const movie = this.movies.find((movie) => movie.id === id);
    if (!movie) {
      throw new NotFoundException(`Movie with ID: ${id} not found.`);
      //HttpException에서 확장된 NestJS의 제공기능. 예외처리 기능을 한다.
    }
    return movie;
  }

  deleteOne(id: number) {
    this.getOne(id);
    this.movies = this.movies.filter((movie) => movie.id !== id);
  }

  create(movieData: CreateMovieDTO) {
    this.movies.push({
      id: this.movies.length + 1,
      ...movieData,
    });
  }

  update(id: number, updateData: UpdateMovieDTO) {
    const movie = this.getOne(id);
    this.deleteOne(id);
    this.movies.push({ ...movie, ...updateData });
  }
}

service.ts에서의 역할은 주로 서버에서 필요한 function의 집합체라고 이해할 수 있다.

가장 상위에서 @Injectable 데코레이터를 통해서 module내부에 provider로서

기능들을 주입할 수 있다.

 

 

마치며...

아키텍쳐를 정해두고 블록처럼 쌓아가는 형식으로 만들 수 있는것은 협업을 했을땐 오히려 가독성이 좋고 오차를 줄일 수 있는 장점이 있을 것 같다. 또한 프로그래밍의 방식을 고민해야할 것들이 이미 가이드라인으로 잡혀있다는 것 자체도 협업을 하는 시점에서 오해의 여지가 줄어들어 작업 효율이 좋아질 것 같아 보였다.

 

https://github.com/zeriong/Nest_intro

2022. 11. 27. 10:21

1. Node.js

자바스크립트를 개조해서 만들어진 런타임이다.

자바스크립트는 특정프로그램, 그중에서도

웹브라우저 안에서만 동작한다.

그런데, Node.js를 사용하면 터미널프로그램에서

node를 입력하여 브라우저없이 바로 실행이 가능하게 된다.

웹브라우저에서만 동작하는 자바스크립트의 한계를 극복하게 되고

이로써 서버를 만들 수 있게 되고 자바스크립트 단 하나의 언어로도

웹페이지를 만들 수 있게 해주는 런타임이다.

 

2. Node.js의 Single Thread

싱글스레드로 구성된 이벤트루프를 활용하여 요청을
받아들이고 스레드풀을 활용하여 병렬처리를 지원한다.

  • Node.js는 Non-Blocking 방식을 사용한다. 클라이언트로부터
    요청을 받으면 다른 스레드에게 요청을 전달한다. 그렇게 되면
    싱글스레드는 자유로워지고 바로 다음 요청을 받을 수 있게 된다.
    그렇게 두번째 요청을 받으면 또 다시 다른 스레드에게 전달하고
    두번째 요청을 전달받은 스레드는 다른서버나 DB에 쿼리를 날려
    요청을 처리한다.
  • asynchronous(비동기/async)방식을 사용하기 때문에
    클라이언트 요청을 처리한 스레드가 응답을 가져오면
    (스레드가 전달받은 요청을 서버 또는 DB에 쿼리를 날려 응답을 받아오면)
    콜백함수를 실행하게 된다.

 

3. 비동기 작업이 가능하게 된 이유

Node.js의 구조를 보면

v8 자바스크립트 엔진과 비동기 작업을 처리하는

libuv라는 라이브러리로 이루어져 있다.

v8에는 memory heap과 하나의 call stack이 있다.

(call stack은 싱글스레드와 같은 의미로 이해하자.)

memory heap은 메모리 할당이 일어나는 곳이며,

call stack은 코드 실행에 따라 호출스택이 차례대로 실행된다.

그렇기 때문에 v8에서는 비동기식 처리를 할 수가 없다.

 

즉, 비동기 작업을 가능하게 만들어 주는 것은 libuv라는 라이브러리가

Non-Blocking IO라는 기능을 가능하게 만들어주는 이벤트루프를

제공하기 때문이다. libuv는 c언어로 만들어졌고 시스템 커넬을 사용한다.

커넬은 멀티스레드를 사용하기 때문에 Node.js가 비동기처리를 할 수 있게 만든다.

 

4. eventLoop

  1. Node.js API로 요청이 들어오면, 들어온 요청은
    event queue(이하 큐)에 추가 된다.

  2. Node.js의 이벤트 루프는 큐를 리슨하여 들어온 요청이 있다면
    선착순으로 요청이 처리된다.

  3. 요청은 스레드풀로 보내진다. 스레드풀은 이벤트루프의 일부로서
    여러 요청을 수행할 수 있다. 이 후에는
    (이벤트루프는 큐에 요청이 있는지 리슨)  =>  (요청이있다면? 스레드풀로)
    이렇게 반복한다.

  4. 스레드풀은 DB또는 파일, 다른 서버 등에 보낸 요청을 수행한다.

  5. 요청 수행을 마쳤다면, 콜백함수를 실행시켜 이벤트 루프로 응답을 전달한다.
  6. 이벤트루프는 응답을 클라이언트로 보낸다.

 

5. 정리

Node.js는 자바스크립트 이벤트 기반 모델에서 영감을 받은

이벤트 루프가 있는 싱글 스레드이다. 자바스크립트와 비슷한 싱글 스레드 이지만

네트워크 호출, 파일시스템 작업 등과 같이 비동기적으로 수행되는 작업은

자바스크립트 코드가 아니라 thread pool에서 실행된다.

Node.js는 thread pool을 가지고 있고 멀티 스레드의 개념도 가지고 있다.

하지만 정의를 내리자면 자바스크립트 환경에서는

싱글 스레드로 사용된다 정의할 수 있다.