Skip to content
kimsungwon edited this page Apr 11, 2024 · 5 revisions

๋ชฉ์ฐจ

Swagger builder ํŒจํ‚ค์ง€ ๋น„๊ต

๋น„๊ตํ‘œ

Package Stars Open issues Close issues Last commit
tsoa 2752 95 816 2023.06.23
swagger-autogen 317 37 153 2023.06.25
swagger-jsdoc 1536 30 150 2023.01.16
tspec 42 0 2 2023.06.21

๊ธฐ๋Šฅ ๋น„๊ต

์žฅ์ 

  1. TypeScript ๊ธฐ๋ฐ˜.
  2. ์ฝ”๋“œ๋กœ swagger ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์Šค์›จ๊ฑฐ ํ˜•์‹์„ ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๊ฐ€๋Šฅ. (SSOT)
  3. ํ…์ŠคํŠธ ํ˜•์‹์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฃผ์„์œผ๋กœ ์ž‘์„ฑ ๊ฐ€๋Šฅ.
  4. ์ž์„ธํ•œ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํ’๋ถ€ํ•˜๊ฒŒ ์ œ๊ณต.

๋‹จ์ 

  1. TypeScript ์— ๊ด€๋ จ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ๋จ.
  2. TypeScript ์˜ decorator ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋˜์–ด ์žˆ์Œ.(ํ˜„์žฌ decorator ๋Š” experimental ํ•œ ๊ธฐ๋Šฅ)
  3. Controller ์ฝ”๋“œ๋ฅผ tsoa ๋ฌธ๋ฒ•์— ๋งž๊ฒŒ ๋ณ€๊ฒฝํ•ด์•ผ ํ•จ.

์˜ˆ์‹œ ์ฝ”๋“œ

import { Get, Route } from "tsoa";

interface PingResponse {
  message: string;
}

@Route("ping")
export default class PingController {
  @Get("/")
  public async getMessage(): Promise<PingResponse> {
    return {
      message: "pong",
    };
  }
}

์žฅ์ 

  1. Controller ์˜ ๊ฒฝ๋กœ๋“ค๊ณผ ์ฃผ์„์„ ์ฝ์–ด ์ž๋™์œผ๋กœ ํ†ตํ•ฉ๋œ ํŒŒ์ผ์„ ์™„์„ฑ.

๋‹จ์ 

  1. ์ฃผ์„์œผ๋กœ swagger ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ • ์‹œ ์ผ์ผ์ด ํ™•์ธํ•ด์„œ ๋ฐ˜์˜ํ•ด์•ผ ๋จ.

์˜ˆ์‹œ ์ฝ”๋“œ

app.post('/users', (req, res) => {
	...
	/*  #swagger.parameters['obj'] = {
			in: 'body',
			description: 'Add a user',
			schema: { $ref: '#/definitions/AddUser' }
	} */
	...
})

์žฅ์ 

  1. ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ค์ •์ด ํŽธํ•จ.

๋‹จ์ 

  1. YAML ํŒŒ์ผ๋กœ ์ž‘์„ฑ๋œ ํŒŒ์ผ๋“ค์„ ์ฝ์–ด ํ•˜๋‚˜์˜ ํŒŒ์ผ๋กœ ํ†ตํ•ฉํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ์„ ํฌํ•จ.
  2. ์ฃผ์„์œผ๋กœ swagger ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ • ์‹œ ์ผ์ผ์ด ํ™•์ธํ•ด์„œ ๋ฐ˜์˜ํ•ด์•ผ ๋จ.

์˜ˆ์‹œ ์ฝ”๋“œ

/**
 * @openapi
 * /:
 *   get:
 *     description: Welcome to swagger-jsdoc!
 *     responses:
 *       200:
 *         description: Returns a mysterious string.
 */
app.get('/', (req, res) => {
  res.send('Hello World!');
});

์žฅ์ 

  1. TypeScript ๊ธฐ๋ฐ˜.
  2. ์ฝ”๋“œ๋กœ swagger ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์Šค์›จ๊ฑฐ ํ˜•์‹์„ ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๊ฐ€๋Šฅ. (SSOT).
  3. ์ž์„ธํ•œ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํ’๋ถ€ํ•˜๊ฒŒ ์ œ๊ณต.

๋‹จ์ 

  1. ์ถœ์‹œํ•œ์ง€ ์–ผ๋งˆ๋˜์ง€ ์•Š์•„ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ.
  2. Open API Spec ์„ ์œ„ํ•œ ํƒ€์ž…๊ณผ handler ํ•จ์ˆ˜์˜ ํƒ€์ž… ๊ฐ„ ์‹ฑํฌ๋ฅผ ๋งž์ถฐ์•ผ ๋จ.

์˜ˆ์‹œ ์ฝ”๋“œ

interface Greeting {
  message: string;
}

type GreetingApiSpec = Tspec.DefineApiSpec<{
  basePath: '/api/v1/dev';
  paths: {
    '/greeting': {
      post: {
        summary: 'Greeting';
        requestBody: Greeting;
        responses: {
          201: Greeting;
        };
      };
    };
  };
}>;

๊ฒฐ๋ก 

Controller ์˜ ํ˜•ํƒœ๋ฅผ ์ „ํ˜€ ์ œํ•œํ•˜์ง€ ์•Š์•„ ๊ฐ€์žฅ ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  SSoT ๋ฅผ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์œผ๋กœ tspec์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •. ๋‹จ์ ์œผ๋กœ ๊ผฝ์•˜๋˜ ์ถœ์‹œ๋œ์ง€ ์–ผ๋งˆ๋˜์ง€ ์•Š์•„ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์€ ๊ณ„์†ํ•ด์„œ ์ถ”์ด๋ฅผ ์ง€์ผœ๋ด์•ผํ•  ํ•„์š”๊ฐ€ ์žˆ์Œ. ์ด์™ธ๋กœ Open API Spec ์„ ์œ„ํ•œ ํƒ€์ž…๊ณผ handler ํ•จ์ˆ˜์˜ ํƒ€์ž… ๊ฐ„ ์‹ฑํฌ๋ฅผ ๋งž์ถฐ์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ์ง€๋งŒ ์žฅ์ ์— ๋น„ํ•ด ํฐ ๋‹จ์ ์ด๋ผ ํŒ๋‹จํ•˜์ง€ ์•Š์•˜์Œ.

Request validate ํŒจํ‚ค์ง€ ๋น„๊ต

๋น„๊ตํ‘œ

Package Stars Open issues Closed issues Last commit
class-validator 9.6k 204 711 2023.07.14
express-validator 5.6k 46 813 2023.07.15
express-openapi-validator 781 131 250 2023.05.01

๊ธฐ๋Šฅ ๋น„๊ต

ํŠน์ง•

  • Class ์— decorator ๋ฅผ ์ ์šฉํ•˜์—ฌ ๊ฐ propertry ๋“ค์„ validate ํ•˜๋Š” ํŒจํ‚ค์ง€

์žฅ์ 

  1. ๊ฐ ํ•„๋“œ์— ๋Œ€ํ•ด ์ •๋ฐ€ํ•œ validation ๋‚ด์šฉ๋“ค์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. (์ž์„ธํ•œ ๋‚ด์šฉ)

๋‹จ์ 

  1. ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด class ๋ฅผ ๋ฒ ์ด์Šค๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— plain object ์— ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ณ„๋„์˜ class instantiate ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ•œ๋‹ค.
  2. ๊ฐ ํ•„๋“œ๋งˆ๋‹ค ๊ฒ€์ฆํ•  ๋‚ด์šฉ๋“ค์„ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์ž‘์„ฑํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์„ฑํ•ด์•ผ๋  ์ฝ”๋“œ๊ฐ€ ๋งŽ๋‹ค.
  3. 1์˜ ๋‚ด์šฉ๋Œ€๋กœ class instantiate ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์ˆ˜์ด๊ธฐ์— target class ๋ฅผ ์•Œ์•„์•ผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์š”์ฒญ์˜ ํƒ€์ž… ์ •๋ณด๋ฅผ ์•Œ๊ณ ์žˆ๋Š” handler ์ˆ˜ ๋งŒํผ์˜ validate ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

import {
  validate,
  validateOrReject,
  Contains,
  IsInt,
  Length,
  IsEmail,
  IsFQDN,
  IsDate,
  Min,
  Max,
} from 'class-validator';

export class Post {
  @Length(10, 20)
  title: string;

  @Contains('hello')
  text: string;

  @IsInt()
  @Min(0)
  @Max(10)
  rating: number;

  @IsEmail()
  email: string;

  @IsFQDN()
  site: string;

  @IsDate()
  createDate: Date;
}

let post = new Post();
post.title = 'Hello'; // should not pass
post.text = 'this is a great post about hell world'; // should not pass
post.rating = 11; // should not pass
post.email = 'google.com'; // should not pass
post.site = 'googlecom'; // should not pass

validate(post).then(errors => {
  // errors is an array of validation errors
  if (errors.length > 0) {
    console.log('validation failed. errors: ', errors);
  } else {
    console.log('validation succeed');
  }
});

validateOrReject(post).catch(errors => {
  console.log('Promise rejected (validation failed). Errors: ', errors);
});
// or
async function validateOrRejectExample(input) {
  try {
    await validateOrReject(input);
  } catch (errors) {
    console.log('Caught promise rejection (validation failed). Errors: ', errors);
  }
}

ํŠน์ง•

  • ๋ฏธ๋ฆฌ ์ •์˜๋œ express ์˜ middleware ๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ request ๋ฅผ validate ๋ฐ sanitize ํ•˜๋Š” ํŒจํ‚ค์ง€

์žฅ์ 

  1. Validation chain ์„ ํ™œ์šฉํ•˜์—ฌ ๊ฒ€์ฆ๊ณผ ๋‚ด์šฉ์˜ ์ •์ œ๋ฅผ ์›ํ•˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ 

  1. Validation chain ์„ ์žฌ์‚ฌ์šฉํ• ๋•Œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.(์ฐธ๊ณ  )
  2. 1์˜ ๋ฒ„๊ทธ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋งค๋ฒˆ ๊ฐ™์€ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

import * as express from 'express';
import { query, validationResult } from 'express-validator';
const app = express();

app.use(express.json());
app.get('/hello', query('person').notEmpty(), (req, res) => {
  const result = validationResult(req);
  if (result.isEmpty()) {
    return res.send(`Hello, ${req.query.person}!`);
  }

  res.send({ errors: result.array() });
});

app.listen(3000);

ํŠน์ง•

  • OAS(OpenAPI Specification) ์— ๋ช…์‹œ๋œ ๋‚ด์šฉ์„ ํ†ตํ•ด์„œ request ๋ฐ response ๋ฅผ validation ํ•˜๋Š” ํŒจํ‚ค์ง€

์žฅ์ 

  1. OAS ์˜ schema ๋‚ด์šฉ์„ ๋ณด๊ณ  validate ํ•˜๊ณ  ๋Œ€๋ถ€๋ถ„์˜ ํƒ€์ž…๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์„ฑํ•ด์•ผ๋  ์ฝ”๋“œ์˜ ์–‘์ด ์ ์Œ.(๋ฏธ๋ฆฌ ์ •์˜๋œ middleware ๋ฅผ ์ ์šฉํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋จ)
  2. ํ’๋ถ€ํ•œ ์—๋Ÿฌ ๋‚ด์šฉ ์ œ๊ณต (status code ํฌํ•จ)

๋‹จ์ 

  1. ํŒจํ‚ค์ง€์˜ ์—๋Ÿฌ ํ˜•ํƒœ๋ฅผ ์„œ๋ฒ„์—์„œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ฐ€๊ณตํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•จ.
  2. ๋ฌธ์„œ ๋‚ด์šฉ์—์„œ ๋ถ€์กฑํ•œ ๋‚ด์šฉ์ด ์ผ๋ถ€ ๋ณด์ž„.

์˜ˆ์‹œ ์ฝ”๋“œ

const express = require('express');
const path = require('path');
const http = require('http');
const app = express();

// 1. Import the express-openapi-validator library
const OpenApiValidator = require('express-openapi-validator');

// 2. Set up body parsers for the request body types you expect
//    Must be specified prior to endpoints in 5.
app.use(express.json());
app.use(express.text());
app.use(express.urlencoded({ extended: false }));

// 3. (optionally) Serve the OpenAPI spec
const spec = path.join(__dirname, 'api.yaml');
app.use('/spec', express.static(spec));

// 4. Install the OpenApiValidator onto your express app
app.use(
  OpenApiValidator.middleware({
    apiSpec: './api.yaml',
    validateResponses: true, // <-- to validate responses
  }),
);

// 5. Define routes using Express
app.get('/v1/pets', function (req, res, next) {
  res.json([
    { id: 1, type: 'cat', name: 'max' },
    { id: 2, type: 'cat', name: 'mini' },
  ]);
});

// 6. Create an Express error handler
app.use((err, req, res, next) => {
  // 7. Customize errors
  console.error(err); // dump error to console for debug
  res.status(err.status || 500).json({
    message: err.message,
    errors: err.errors,
  });
});

http.createServer(app).listen(3000);

๊ฒฐ๋ก 

์‹ ๋ขฐ์„ฑ์˜ ์ธก๋ฉด์—์„œ star ์ˆ˜ , open / close issue ์˜ ๋น„์œจ๋“ฑ์˜ ์ˆ˜์น˜๋Š” class-validator ์™€ express-validator ๊ฐ€ ์ข‹์€ ๋ฉด์ด ์žˆ์Œ. ํ•˜์ง€๋งŒ, ๋งค๋ฒˆ ๊ฐ™์€ ๋‚ด์šฉ์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ณ  ํŒจํ‚ค์ง€์˜ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๋ฐฉ์‹์œผ๋กœ ์ธํ•œ ๊ณ ์ •์ ์ธ ๊ตฌ์กฐ(i.e validate ๋ฅผ ์œ„ํ•ด์„œ๋Š” class instance ํ•˜๋Š” ๊ณผ์ •์ด ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค๋“ ์ง€)๋Š” ์‚ฌ์šฉ์„ฑ์— ํฐ ์ œ์•ฝ์ด ์žˆ์Œ. ๋”ฐ๋ผ์„œ, ํŒจํ‚ค์ง€์˜ ์‹ ๋ขฐ์„ฑ์€ ๋–จ์–ด์ง€๋”๋ผ๋„ ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š” ๋ณด๋‹ค ๋” ๊น”๋”ํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” express-openapi-validator ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •. ๋‹ค๋งŒ, ์‹ ๋ขฐ์„ฑ์ด ๋–จ์–ด์ง€๋Š” ํŒจํ‚ค์ง€์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐ repo ์— ์˜ฌ๋ผ์˜จ ์ด์Šˆ ๋‚ด์šฉ๋“ค์„ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์žˆ์Œ.

Raw Query VS Query Builder VS ORM

Raw Query

์žฅ์ 

  1. ์งˆ์˜๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋ช…์‹œ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„๋œ ๋‹ค๋ฅธ tool ๋“ค๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค.
  3. ์œ ์—ฐ์„ฑ์ด ์ข‹๋‹ค. ์ง์ ‘ ์งˆ์˜๋ฅผ ์ž‘์„ฑํ•˜๋Š”๊ฒŒ ์œ ์—ฐ์„ฑ์ด ๋‹ค๋ฅธ ์–ด๋–ค ์ถ”์ƒํ™”๊ฐ€ ์ ์šฉ๋œ tool ๋“ค๋ณด๋‹ค ์ข‹์„ ์ˆ˜ ์—†๋‹ค.

๋‹จ์ 

  1. ๊ฐ€ํŒŒ๋ฅธ ๋Ÿฌ๋‹ ์ปค๋ธŒ. ์˜๋„ํ–ˆ๋˜ ๋Œ€๋กœ ์ œ๋Œ€๋กœ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ตฌ์กฐ์™€ SQL ์— ๋Œ€ํ•ด์„œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฒŒ ๋งŽ๋‹ค.
  2. SQL injection ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ์ž…๋ ฅ์„ ์ •ํ™”ํ•˜๋Š” ๋กœ์ง์„ ๋งŒ๋“ค์–ด ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๊ฐ„๊ณผ ์‹ค์ˆ˜๋กœ ์ธํ•œ ์น˜๋ช…์ ์ธ ๋ณด์•ˆ์— ๋Œ€ํ•œ ๊ฒฐ์ ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.
  3. ์ผ๋ฐ˜ ๋ฌธ์ž์—ด๋กœ ๊ด€๋ฆฌ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ๊ณ„์ธต์˜ ๋ฐ์ดํ„ฐ๋“ค๊ณผ ์„ž์ด๋ฉฐ ์กฐ์ž‘ํ•˜๊ธฐ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

Query Builder

์žฅ์ 

  1. ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ๋ฉ”์†Œ๋“œ๋‚˜ ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„๋˜๊ธฐ ๋•Œ๋ฌธ์— raw query ๋ณด๋‹ค๋Š” ์žฅ๊ธฐ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ํŽธํ•œ๋‹ค.
  2. Raw query ๋ณด๋‹ค๋Š” ์•„๋‹ˆ๋”๋ผ๋„ ์‹ค์ œ ๋งŒ๋“ค์–ด์ง€๋Š” ์งˆ์˜์˜ ๋‚ด์šฉ์ด ๊ฝค ํˆฌ๋ช…ํ•˜๋‹ค. ๊ธฐ๋Šฅ์ƒ์œผ๋กœ ์‹ค์ œ ์งˆ์˜๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ณ  ๊ทธ๊ฒŒ ์•„๋‹ˆ๋”๋ผ๋„ ์—ฐ์‚ฐ์ด ์–ด๋–ค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ• ์ง€ ์‰ฝ๊ฒŒ ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  3. Query builder ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ด€๊ณ„ํ˜• DBMS ๋ฅผ ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ DBMS ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ• ๋•Œ, ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ 

  1. Raw query ๋ฅผ ์‚ฌ์šฉํ• ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ DB ์˜ ๊ตฌ์กฐ์™€ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ํ•™์Šตํ•˜๋Š” ๊ฒƒ์ด ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋ฟ๋งŒ์•„๋‹ˆ๋ผ query builder ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ์ง€์— ๋Œ€ํ•ด์„œ๋„ ์ถ”๊ฐ€์ ์œผ๋กœ ํ•™์Šต์„ ํ•ด์•ผํ•œ๋‹ค.
  2. Query builder ๋Š” DB ์˜ ๋ฐ์ดํ„ฐ์™€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋งคํ•‘๋˜๋Š”์ง€ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

ORM (Object-Relation Mapper)

์žฅ์ 

  1. ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•˜์—ฌ ํ˜„์žฌ์˜ ์ž‘์—…์˜ ์—ฐ์žฅ์—์„œ ๋ฐ์ดํ„ฐ ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•˜๊ณ  ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ์— ๋„์›€์„ ์ค€๋‹ค.
  2. DB ์™€์˜ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ ์— ํ•„์š”ํ•œ ๋งŽ์€ boilerplate ๊ฐ€ ํ•„์š” ์—†์–ด์ง€๊ฒŒ ๋œ๋‹ค. ORM ์—๋Š” migration tool ์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐœ๋ฐœ์ž๊ฐ€ schema ๋ณ€๊ฒฝ์ ์„ ์™„๋ฒฝํžˆ ์ฐพ์•„ DB ์— ์ ์šฉํ•  ํ•„์š”์—†์ด DB ์˜ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์— ๋„์›€์„ ์ค€๋‹ค.

๋‹จ์ 

  1. ๋†’์€ ์ •๋„์˜ ์ถ”์ƒํ™” ๋•Œ๋ฌธ์— ORM ์€ DB backend ์˜ ๋””ํ…Œ์ผํ•œ ์‚ฌํ•ญ๋“ค์„ ์ˆจ๊ฒจ๋ฒ„๋ฆฐ๋‹ค. ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๋‚˜ ์ž‘์€ ๊ทœ๋ชจ์˜ ์ž‘์—…์—์„œ๋Š” ORM ์„ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•ด์ฃผ์ง€๋งŒ, ๋ณต์žก์„ฑ์ด ์ปค์งˆ์ˆ˜๋ก ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  2. ORM ๊ณผ ์งˆ์˜ ์–ธ์–ด์— ๋Œ€ํ•œ ์ดํ•ด์—†์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๋ฌธ์ œ์  ์ƒํ™ฉ๋“ค์„ ์ดˆ๋ž˜ํ•˜๊ฒŒ ๋œ๋‹ค. ๋””๋ฒ„๊น…๊ณผ ์„ฑ๋Šฅ์—์„œ๋Š” ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
  3. Object ์™€ Relation ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ์ฐจ์ด์—์„œ ์˜ค๋Š” ๋ถˆ์ผ์น˜๋กœ ๊ฐ„๋‹จํ•œ ์งˆ์˜๋„ ์ ์  ๋ณต์žกํ•ด์งˆ ์šฐ๋ ค๊ฐ€ ์žˆ๋‹ค.

๊ฒฐ๋ก 

ORM ์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •

  1. Raw query/query builder ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, object-relation ๋งตํ•‘์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๊ฐ ์งˆ์˜์— ๋Œ€ํ•ด์„œ ํƒ€์ž…์„ ์ž‘์„ฑํ•˜๊ณ  ๋งตํ•‘ํ•˜๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด, ์ „์ฒด ํ”„๋กœ์ ํŠธ ์ง„ํ–‰์†๋„๊ฐ€ ์ง€๊ธˆ๋ณด๋‹ค ๋”๋ŽŒ์งˆ ๊ฒƒ์ด ์šฐ๋ ค๋œ๋‹ค.
  2. ORM ์˜ ๊ฐ€์žฅ ํฐ ๋‹จ์ ์ธ object-relation ์„ ๋งตํ•‘ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ณต์žก์„ฑ์ด ๋Š˜์–ด๋‚˜ ์„ฑ๋Šฅ ๋ฐ ๋””๋ฒ„๊น…์— ์•…์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ธ๋ฐ, ํ˜„์žฌ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํ…Œ์ด๋ธ”์˜ ์ˆ˜๋„ ์ ๊ณ  ๋ณต์žกํ•œ ์งˆ์˜๋ฅผ ์‚ฌ์šฉํ• ๋งŒํ•œ ๊ธฐ๋Šฅ๋„ ์—†๊ธฐ์— ํฐ ์˜ํ–ฅ์€ ์—†์„ ๊ฒƒ ๊ฐ™๋‹ค.

๋”ฐ๋ผ์„œ ์œ„์˜ ์ด์œ ๋“ค๋กœ ORM ์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •!

ORM ํŒจํ‚ค์ง€ ๋น„๊ต

๋น„๊ตํ‘œ

Package Stars Open issues Close issues Last commit
Prisma 33.5k 2731 6265 2023.08.30
TypeORM 32k 2052 5594 2023.08.20
Sequelize 28.3k 782 9351 2023.08.30

๊ธฐ๋Šฅ ๋น„๊ต

ํŠน์ง•

  • ๋น„๊ต์  ์ตœ๊ทผ์— ์ถœ์‹œ๋œ DB toolkit
  • Prisma schema๋กœ ๋ชจ๋ธ์„ ์„ ์–ธํ•˜๋ฉฐ, ์„ ์–ธ์  ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์ •์˜์™€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง€์›
  • Prisma Client: Prisma schema๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํƒ€์ž… ์•ˆ์ •ํ•œ ์ฟผ๋ฆฌ ๋นŒ๋” ์ œ๊ณต
  • Prisma Migrate: Prisma schema ๊ธฐ๋ฐ˜์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ์Šคํ…œ
  • Prisma Studio: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ง์ ‘ ์กฐ์ž‘ํ•˜๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋Š” GUI ๋„๊ตฌ

์žฅ์ 

  • ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐ ์กฐํšŒ์— ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๋ณด์žฅ (์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐ ์กฐํšŒ ํฌํ•จ)
  • ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์› (PostgreSQL, MySQL, MSSQL ๋“ฑ)
  • SSoT(Single source of truth)์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด Prisma schema ๊ธฐ๋ฐ˜์œผ๋กœ ํƒ€์ž… ์ •๋ณด ์ƒ์„ฑ
  • ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ๋ฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ง€์†์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง

๋‹จ์ 

  • ๋‹ค๋ฅธ ORM์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ (์ž์ฒด ์Šคํ‚ค๋งˆ ํ˜•์‹ ์‚ฌ์šฉ)
  • ํŒจํ‚ค์ง€ ํฌ๊ธฐ๊ฐ€ ํฌ๊ณ  ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์ด ๋ถ€์กฑํ•  ์ˆ˜ ์žˆ์Œ

์˜ˆ์‹œ ์ฝ”๋“œ

// schema.prisma
model UserModel {
  id    BigInt @id @default(autoincrement())
  name  String @db.VarChar(64)
  email String @db.VarChar(64)
}

// user.repository.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

const user = await prisma.user.create({
    data: {
        name: 'John Doe',
        email: '[email protected]',
    },
});

ํŠน์ง•

  • Hibernate ์˜ํ–ฅ์„ ๋ฐ›์€ TypeScript ๊ธฐ๋ฐ˜์˜ ORM
  • Active Record ๋ฐ Data Mapper ํŒจํ„ด ์ง€์›

์žฅ์ 

  • ๋ฐ์ดํ„ฐ ์ƒ์„ฑ์— ๋Œ€ํ•œ ํƒ€์ž… ์•ˆ์ •์„ฑ ๋ณด์žฅ (์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ํฌํ•จ)
  • ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์› (MySQL, PostgreSQL, SQLite ๋“ฑ)
  • Active Record ๋ฐ Data Mapper ํŒจํ„ด ๋ชจ๋‘ ์ง€์›ํ•˜์—ฌ ์œ ์—ฐํ•œ ๊ฐœ๋ฐœ ๊ฐ€๋Šฅ

๋‹จ์ 

  • ๋ฐ์ดํ„ฐ ์กฐํšŒ์— ๋Œ€ํ•œ ํƒ€์ž… ์•ˆ์ •์„ฑ์€ ๋ถ€๋ถ„์  (์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ ์กฐํšŒ์— ์ถ”๊ฐ€ ์ž‘์—… ํ•„์š”)
  • ์ดˆ๊ธฐ ํ•™์Šต ๊ณก์„ ์ด ๊ฐ€ํŒŒ๋ฆ„
  • ์„ค์ •์ด ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ

์˜ˆ์‹œ ์ฝ”๋“œ

// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    email: string;
}

// user.repository.ts
const firstUser = await conn
    .getRepository(User)
    .createQueryBuilder("user")
    .where("user.id = :id", { id: 1 })
    .getOne();

ํŠน์ง•

  • Promise ๊ธฐ๋ฐ˜ Node.js ORM
  • Active Record ํŒจํ„ด ์ง€์›
  • ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์› (PostgreSQL, MySQL, SQLite ๋“ฑ)

์žฅ์ 

  • ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์›
  • ๊ธฐ์กด์— ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ํŒจํ‚ค์ง€๋กœ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ํ’๋ถ€

๋‹จ์ 

  • ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋ฐ ์กฐํšŒ ๋ชจ๋‘ ํƒ€์ž… ์•ˆ์ •์„ฑ ๋ถ€์กฑ
  • ํ”„๋กœ์ ํŠธ ์ง„ํ–‰์ด ๋‘”ํ™”๋˜์–ด ๊ฐœ๋ฐœ์ด ์ œ๋Œ€๋กœ ๋˜๊ณ  ์žˆ์ง€ ์•Š์Œ

์˜ˆ์‹œ ์ฝ”๋“œ

const { Sequelize, Model, DataTypes } = require('sequelize');

const sequelize = new Sequelize('database', 'username', 'password', {
  dialect: 'mysql',
});

class User extends Model {}
User.init({
  name: DataTypes.STRING,
  email: DataTypes.STRING,
}, {
  sequelize,
  modelName: 'user',
});

User.hasMany(Photo);
Photo.belongsTo(User);

(async () => {
  await sequelize.sync({ force: true });
  const user = await User.create({
    name: 'John Doe',
    email: '[email protected]',
  });
  const photo = await user.createPhoto({
    url: 'example.com/photo.jpg',
  });
})();

๊ฒฐ๋ก 

์„ธ ๊ฐ€์ง€ ํŒจํ‚ค์ง€๋ฅผ ๋น„๊ตํ•ด๋ณด๋ฉด, Prisma๊ฐ€ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ๊ณผ ์กฐํšŒ ๋ชจ๋‘์— ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ œ๊ณตํ•˜๋ฉฐ, SSoT๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์›๊ณผ ์ง€์†์ ์ธ ์—…๋ฐ์ดํŠธ๋กœ ์ธํ•ด ๊ฐ€์žฅ ์ข‹์€ ์„ ํƒ์ด ๋  ๊ฒƒ์œผ๋กœ ํŒ๋‹จ.