๐Ÿ›ผ ์šฐ์•„ํ•œ ํ…Œํฌ์บ ํ”„ 5, 6์ฃผ์ฐจ ํšŒ๊ณ (0801~0812)

Lee Jooamยท2022๋…„ 9์›” 5์ผ
1
post-thumbnail

๐ŸŽฏ 5, 6์ฃผ์ฐจ ๋ฏธ์…˜

1. ํ‚ค์˜ค์Šคํฌ ์•ฑ ํ”„๋ก ํŠธ์—”๋“œ ๊ตฌํ˜„(๋ฆฌ์•กํŠธ ์ด์šฉ)

2. ๊ฐ€๊ณ„๋ถ€ ๋ฐฑ์—”๋“œ ๊ตฌํ˜„(NestJS, MySQL ์ด์šฉ)

3. AWS ์ด์šฉ ๋ฐฐํฌ(์Šคํฌ๋ฆฝํŠธ ์ด์šฉ ์ง€์† ๋ฐฐํฌ)


5, 6์ฃผ์ฐจ ๋ฏธ์…˜์€ ํ‚ค์˜ค์Šคํฌ ํ˜•ํƒœ์˜ ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋ฆฌ์•กํŠธ, ๋ฐฑ์—”๋“œ๋Š” NestJS๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ–ˆ๋Š”๋ฐ, ์‹ค์ œ์ ์ธ ์‚ฌ์šฉ์€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์–‘ํ•œ ๋ฌธ์ œ์‚ฌํ•ญ๋“ค์„ ๊ฒช์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜ฅ

๐ŸŽ ๋ฏธ์…˜ ์ง„ํ–‰ ์ค‘ ๊ณ ๋ คํ•œ ์‚ฌํ•ญ

๐ŸŽ€ ํ”„๋ก ํŠธ์—”๋“œ

๋ผ์šฐํ„ฐ ๊ตฌํ˜„

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‚ฌ์šฉ์„ ์ตœ๋Œ€ํ•œ ์ž์ œํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์šฐํŒ… ๊ธฐ๋Šฅ ๋˜ํ•œ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. admin ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์šฐํ„ฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

function Routes({ children }: { children: React.ReactNode }) {
  const { path } = useContext(routerContext);

  let currentRoute;

  Children.forEach(children, (element: React.ReactNode) => {
    if (!isValidChild(element)) {
      invariant(isValidChild(element), `์˜ฌ๋ฐ”๋ฅธ Route ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.`);
      return;
    }

    const { path: routePath, element: component } = element.props;
    const [parsedPath, params] = extractPathAndParams(routePath);

    if (isCurrentRoute(path, parsedPath)) {
      currentRoute = component;
    }
  });

  if (!currentRoute) {
    return <></>;
  }

  return currentRoute;
}
<Routes>
  <Route path="/admin" element={<Admin />} />
  <Route path="/customer-order" element={<CustomerOrder />}/>
  <Route path="/" element={<Entrance />} />
 </Routes>

๋ผ์šฐํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ์˜ ํ˜•ํƒœ๋ฅผ ํ† ๋Œ€๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. Routes ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ children์œผ๋กœ ๋“ค์–ด์˜จ Route ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ˆœํšŒํ•˜๊ณ  ํ˜„์žฌ path์™€ ๋งž๋Š” Route ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์•„๋‚ด๋Š” ํ˜•์‹์ž…๋‹ˆ๋‹ค.

์ด๋•Œ path๋Š” ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— Context API๋ฅผ ์ด์šฉํ•ด ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

export const ROUTE_PARAMETER_REGEX = /:(\w+)/g;
export const URL_FRAGMENT_REGEXP = '([^\\/]+)';
export const QUERY_STRING_REGEXP = /\?[\w=&]+/g;

export function extractPathAndParams(routePath: string): [string, string[]] {
  const params: string[] = [];

  const parsedPath = routePath
    .replace(ROUTE_PARAMETER_REGEX, (_match: string, paramName: string) => {
      params.push(paramName);
      return URL_FRAGMENT_REGEXP;
    })
    .replace(QUERY_STRING_REGEXP, '')
    .replace(/\//g, '\\/');

  return [parsedPath, params];
}

Route ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” path๊ฐ€ ๋งŒ์•ฝ path parameter ํ˜•ํƒœ๋กœ ์ฃผ์–ด์ง„๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ์ ์ ˆํ•œ ํ˜•ํƒœ๋กœ ๊ฐ€๊ณตํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์œ„์˜ ์œ ํ‹ธ ๋ฉ”์†Œ๋“œ์ด๋ฉฐ, path parameter๋ฅผ url fragment๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค. url fragment๋Š” url๋กœ ๋“ค์–ด์˜จ ๋ฌธ์ž์—ด๊ณผ ๋งค์นญ๋˜๋Š” ์ •๊ทœ์‹์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” path parameter๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์ดˆ๊ธฐ ๊ธฐํš์—์„œ๋Š” ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

Typescript

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋‚œ๊ด€์ด ์žˆ์—ˆ๋Š”๋ฐ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋„์ž…๋„ ํฐ ๋‚œ๊ด€ ์ค‘ ํ•˜๋‚˜์˜€์Šต๋‹ˆ๋‹ค. ์ง„ํ–‰ ์ค‘์— ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋„์ž…์„ ํ›„ํšŒํ–ˆ๋˜ ์ ์ด ๋งŽ์•˜์ง€๋งŒ, ์–ธ์  ๊ฐ€๋Š” ๊ฒช์–ด์•ผ ํ•  ์ผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด ์—ด๊ณผ ์••๋ ฅ์„ ๊ฒฌ๋Ž ์Šต๋‹ˆ๋‹ค.

export interface StoreLoginType {
  storeId: string;
  password: string;
}

export interface StoreRegisterType {
  storeId: string;
  name: string;
  password: string;
  branchName: string;
}

export interface StoreInputsType {
  storeId: string;
  name: string;
  password: string;
  passwordConfirm: string;
  branchName: string;
}

export interface StoreType {
  id: number;
  storeId: string;
  name: string;
  branchName: string;
}

export const initialStoreValue = {
  id: 0,
  storeId: '',
  name: '',
  branchName: '',
};

export const initialStoreInputsValue = {
  storeId: '',
  name: '',
  password: '',
  passwordConfirm: '',
  branchName: '',
};

์ฒ˜์Œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํŠน์ • ๊ฐ’์„ null์ด๋‚˜ undefined๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ํ•ด๋‹น ๊ฐ’์˜ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜๋Š” ๋ถ„๊ธฐ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. Type Guard๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์— ์ข‹์ง€ ์•Š์€ ์˜ํ–ฅ์„ ์ฃผ๋Š” ๊ฒƒ ๊ฐ™์•„ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•˜๋‹ค๊ฐ€ initialValue๋ฅผ ์ฃผ๋Š” ๋ฐฉ์‹์„ ๋– ์˜ฌ๋ ธ์Šต๋‹ˆ๋‹ค.

๊ฐ์ฒด ๊ฐ’์˜ ํƒ€์ž…์ผ ๊ฒฝ์šฐ ๊ฐ ์†์„ฑ์— falsyํ•œ ๊ฐ’์„ ์ค€ ์ดˆ๊ธฐ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ• ๋‹นํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„์„ ์ง„ํ–‰ํ–ˆ๋Š”๋ฐ, ๋Œ€์‹  ๊ฐ’์„ ์ด์šฉํ•  ๋•Œ ๊ทธ ๊ฐ’๋“ค์ด ๋ชจ๋‘ validํ•œ์ง€ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ถ„๋“ค์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์‚ฌ์šฉ๋ฒ•๋“ค์„ ๋ณด๋ฉฐ ๋” ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์ด ์—†๋Š”์ง€ ์•Œ์•„๋ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

custom hooks

import { useContext, useEffect, useState } from 'react';
import categoryAPI from '../api/categoryAPI';
import { storeContext } from '../context/StoreProvider';
import { CategoryType, initialCategoryValue } from '../types/category';

const useCategory = () => {
  const [categories, setCategories] = useState<CategoryType[]>([]);
  const [selectedCategory, setSelectedCategory] = useState<CategoryType>({
    ...initialCategoryValue,
  });
  const { store } = useContext(storeContext);

  useEffect(() => {
    const getCategories = async () => {
      if (store.id) {
        const response = await categoryAPI.getCategoriesById(store.id);
        setCategories(response);
        setSelectedCategory(response[0] || { ...initialCategoryValue });
      }
    };

    getCategories();
  }, [store.id]);

  return { categories, setCategories, selectedCategory, setSelectedCategory };
};

export default useCategory;

์ปค์Šคํ…€ ํ›…์— ๋Œ€ํ•œ ์ง€์‹์€ ์žˆ์—ˆ์ง€๋งŒ, ๊ตณ์ด ์‚ฌ์šฉํ• ๋งŒํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ์ ์ธ ์‚ฌ์šฉ์€ ์ด๋ฒˆ์— ์ฒ˜์Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ํ›…์˜ ์žฅ์ ์€ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์žˆ๊ฒ ์ง€๋งŒ, ๊ตฌํ˜„์„ ํ•˜๋ฉด์„œ ๊ทธ๊ฒƒ๋ณด๋‹ค ๋” ๋งค๋ ฅ์ ์ธ ์ ์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

// Admin.tsx
function Admin() {
  const { categories, setCategories, selectedCategory, setSelectedCategory } =
    useCategory();
  const { products, setProducts } = useProduct(selectedCategory);
  const { store } = useContext(storeContext);
  const { changeAdminAuthority } = useContext(adminAuthorityContext);
  const navigate = useNavigate();
  .
  .
  .
  .

๋กœ์ง์— ์ถ”์ƒํ™” ๊ณ„์ธต์„ ์ œ๊ณตํ•ด ์ธํ„ฐํŽ˜์ด์Šค์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ชจ๋“  ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๊ทธ๋Ÿด ๊ฒฝ์šฐ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ์ฑ…์ž„์„ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ปค์Šคํ…€ ํ›…์„ ์ด์šฉํ•œ ์ฑ…์ž„์˜ ๋ถ„๋ฆฌ๋Š” ์ถ”ํ›„ ์œ ์ง€๋ณด์ˆ˜๋‚˜ ๊ธฐ๋Šฅ์˜ ์ถ”๊ฐ€๋ฅผ ๋งค์šฐ ์šฉ์ดํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ ์ธก๋ฉด์—์„œ๋„ ๋งŽ์€ ๋„์›€์„ ์ฃผ๋‹ˆ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์Šต๊ด€์„ ๊ฐ€์ ธ์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

๐ŸŽ‰ ๋ฐฑ์—”๋“œ

TypeORM?

๋ฐฑ์—”๋“œ ๊ตฌํ˜„ ์ดˆ๊ธฐ์— TypeORM์„ ์‚ฌ์šฉํ•ด DB ๊ด€๋ จ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ SQL๋ฌธ์— ๋Œ€ํ•œ ์ˆ™๋ จ๋„๊ฐ€ ์•„์ง ๋ถ€์กฑํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•ด, ๊ธฐ์กด ์ฝ”๋“œ๋“ค์„ ๋ชจ๋‘ ์ผ๋ฐ˜ SQL๋กœ ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค... ๐Ÿ˜ช ๊ทธ๋ž˜์„œ์ธ์ง€ ์ด๋ฒˆ ๋ฏธ์…˜์€ ์‹œ๊ฐ„์ด ๋งŽ์ด ๋ถ€์กฑํ–ˆ์Šต๋‹ˆ๋‹ค.

// mysql.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MySQLService } from './mysql.service';

@Module({
  imports: [ConfigModule],
  providers: [MySQLService],
  exports: [MySQLService],
})
export class MySQLModule {}
// mysql.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as mysql from 'mysql2';

@Injectable()
export class MySQLService implements OnModuleInit {
  pool: mysql.Pool;
  constructor(private configService: ConfigService) {
    this.pool = mysql.createPool({
      host: configService.get('DATABASE_HOST'),
      port: configService.get<number>('DATABASE_PORT'),
      user: configService.get('DATABASE_USERNAME'),
      password: configService.get('DATABASE_PASSWORD'),
      database: configService.get('DATABASE_DATABASE'),
    });
  }

  async onModuleInit() {
    const poolPromise = this.pool.promise();

    await poolPromise.execute(`
      CREATE TABLE IF NOT EXISTS STORE (
        id INT PRIMARY KEY AUTO_INCREMENT,
        storeId VARCHAR(30) NOT NULL,
        name VARCHAR(20) NOT NULL,
        password VARCHAR(20) NOT NULL,
        branchName VARCHAR(20),
        deletedAt DATETIME
      )
    `);

    .
    .
    .
    .
    .

๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ mysql์„ ์—ฐ๋™์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›น์—์„œ ์ฐธ๊ณ ๋ฅผ ์œ„ํ•ด ๊ฒ€์ƒ‰ํ•œ ์ž๋ฃŒ๋“ค์€ ๋ชจ๋‘ TypeORM์„ ์ด์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Nest์— ์•„์ง ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ์ž…์žฅ์—์„œ mysql์„ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ์‹์œผ๋กœ ์—ฐ๋™์‹œํ‚ค๋Š” ๊ฒƒ๋„ ๋‚œ๊ด€์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ผ๋ฐ˜ SQL๋กœ ์ž‘์„ฑํ•˜๋‹ค๋ณด๋‹ˆ ํ˜„์žฌ ์ž์‹ ์ด ์–ด๋–ค ๊ตฌํ˜„์„ ํ•˜๋Š”์ง€ ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์–ด ์ข‹์€ ๊ฒฝํ—˜์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿง  ๊ณ ๋ฏผ๊ณผ ์ƒ๊ฐ

1. ์•Œ๊ณ  ์“ฐ์ž

์ด๋ฒˆ ๋ฏธ์…˜์—์„  ํ”„๋ก ํŠธ์—”๋“œ๋ณด๋‹ค ๋ฐฑ์—”๋“œ ๋ถ€๋ถ„์— ์‹œ๊ฐ„์„ ํ›จ์”ฌ ๋งŽ์ด ์†Œ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. NestJS ์ž์ฒด๊ฐ€ ์ฒ˜์Œ์ด๊ธฐ๋„ ํ–ˆ๊ณ , ORM์ด๋ผ๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ฒ˜์Œ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Nest๋Š” Spring ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ JS ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธด ๋Š๋‚Œ์ž…๋‹ˆ๋‹ค. DI, IOC, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด ๋“ฑ ์ƒ์†Œํ•œ ๊ฐœ๋…๋“ค์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์—…์„ ํ•˜๋ฉด์„œ๋„ ์ง€๊ธˆ ๋ฌด์Šจ ์ž‘์—…์„ ํ•˜๋Š”์ง€ ๋ฉ”ํƒ€์ธ์ง€๋ฅผ ์ œ๋Œ€๋กœ ํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ TypeORM๊นŒ์ง€ ๋„์ž…ํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ ๋จธ๋ฆฟ์†์ด ๋„ˆ๋ฌด ์–ด์ง€๋Ÿฌ์› ์Šต๋‹ˆ๋‹ค.

์ž์‹ ์ด ์‚ฌ์šฉ ์ค‘์ธ ๋„๊ตฌ์˜ ์ „๋ฐ˜์ ์ธ ๊ฐœ๋…๊ณผ ์žฅ๋‹จ์  ๋“ฑ์„ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์„ ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ถ”ํ›„ ํฌ์ŠคํŒ…์„ ํ†ตํ•ด Nest์— ๊ด€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

2. ๊ณผ์ •์„ ๊ธฐ๋กํ•˜๊ธฐ

๋ฌธ์„œํ™”์˜ ์ค‘์š”์„ฑํ•˜๋‹ค๋Š” ๊ฒƒ์€ ์•Œ์ง€๋งŒ ๋ง‰์ƒ ํ•˜๊ธฐ์—๋Š” ํ•ญ์ƒ ์–ด๋ ค์šด ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์‹œ๊ฐ„์ ์ธ ๋ถ€๋ถ„๋„ ๊ทธ๋ ‡๊ณ  ์ž์‹ ์ด ์•Œ๊ฒŒ ๋˜์—ˆ๊ฑฐ๋‚˜ ๋Š๋‚€ ์ ์„ ๊ธ€๋กœ ์ถœ๋ ฅํ•œ๋‹ค๋Š” ๊ฑด ์‰ฝ์ง€ ์•Š์€ ์ž‘์—…์ž…๋‹ˆ๋‹ค.

ํŠน์ • ์ œ์•ฝ์ด๋‚˜ ๋ฌธ์ œ ์‚ฌํ•ญ์—์„œ ์ž์‹ ์ด ์–ด๋–ค ๊ณผ์ •์„ ํ†ตํ•ด ํ•ด๊ฒฐํ–ˆ๋Š”์ง€ ๊พธ์ค€ํžˆ ๊ธฐ๋กํ•˜๋Š” ๊ฑด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ ๋ณธ์ธ์ด ์–ด๋–ค ์‚ฌ๋žŒ์ธ์ง€ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ฆ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ƒํ™ฉ๊ณผ ํ•ด๊ฒฐ ๊ณผ์ •, ์ž์‹ ์ด ์„ ํƒํ•œ ๋ฐฉ๋ฒ•์˜ ์žฅ๋‹จ์  ๋“ฑ ์ „์ฒด์ ์ธ ๊ณผ์ •์„ ์„ค๋ช…ํ•  ์ค„ ์•Œ์•„์•ผํ•œ๋‹ค๋Š” ๊ฒŒ ์ฐธ ์ค‘์š”ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๊ธ€์€ ๋ฏธ์…˜์ด ๋๋‚˜๊ณ  2์ฃผ๊ฐ€ ์ง€๋‚œ ์‹œ์ ์ž…๋‹ˆ๋‹ค.

์•„๋ฌด๋Ÿฐ ์ •๋ณด๊ฐ€ ์—†๋Š” ์ƒํƒœ์—์„œ 2์ฃผ ์ „์˜ ๊ธฐ์–ต์„ ๊ธฐ๋กํ•˜๋ ค๋ฉด ์–ด๋ ค์› ๊ฒ ์ง€๋งŒ ๋‹คํ–‰ํžˆ ๋…ธ์…˜์— ํ‹ˆํ‹ˆํžˆ ๊ธฐ๋กํ•ด๋†จ๊ธฐ ๋•Œ๋ฌธ์— ๋„์›€์ด ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ”ฅ ํ›„๊ธฐ

์ด๋ฒˆ ๋ฏธ์…˜์€ ์Šค์Šค๋กœ์—๊ฒŒ ์ •๋ง ํฐ ๋„์ „์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์ด ์ƒˆ๋กœ์šด ๋„๊ตฌ๋“ค์ด์—ˆ๊ณ , ๊ธฐํš์ด ๋ถˆํˆฌ๋ช…ํ•ด ์Šค์Šค๋กœ ์–ด๋–ค ๊ฒƒ๋“ค์„ ๊ตฌํ˜„ํ•ด์•ผํ• ์ง€ ๊ฒฐ์ •ํ•ด์•ผํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ง€๊ธˆ๊นŒ์ง€ ํ•ด์™”๋˜ ๋ฐฉ์‹๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ ๊ธฐ๋Šฅ์„ ๋ถ„์„ํ•˜๊ณ  ์ด์Šˆ๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋Š” ๊ณผ์ •์„ ์ƒ๋žตํ–ˆ๋Š”๋ฐ, ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์ธ์ง€ ๊ตฌํ˜„์„ ํ•˜๋ฉด ํ• ์ˆ˜๋ก ๋ญ”๊ฐ€ ์ž˜๋ชป๋๋‹ค๋Š” ๋Š๋‚Œ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์—์„œ๋„ ์ž˜๋ชป๋œ ์ถ”์ƒํ™”๋กœ ์ธํ•ด ๊ธฐ๋Šฅ๋“ค์ด ๋ณต์žกํ•ด์กŒ๊ณ  ๊ทธ๋Ÿฐ ๊ฒƒ๋“ค์„ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์‹œ๊ฐ„์„ ์†Œ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์–ด์งธ์„œ ํ”„๋กœ์ ํŠธ์˜ ์ง„ํ–‰์— ๊ธฐ๋Šฅ๋ถ„์„๊ณผ ์ดˆ๊ธฐ ์ด์Šˆ๋ฐœ๊ธ‰ ๋ฐ ์„ค๊ณ„๊ฐ€ ์ค‘์š”ํ•œ์ง€ ๋ชธ์†Œ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋‹จ์ˆœํ•œ ์ฝ”๋“œ์˜ ์ž‘์„ฑ์ด ์•„๋‹Œ ์ฃผ์–ด์ง„ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ฒ ์ €ํ•˜๊ฒŒ ๋ถ„์„ํ•˜๊ณ  ๊ทธ๊ฒƒ์„ ๋งŒ๋“œ๋Š” ๊ณผ์ •์ด๋ผ๋Š” ๋งˆ์Œ๊ฐ€์ง์„ ์ƒˆ๊ฒจ์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ๊ฑธ์–ด๊ฐ€๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

1๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2022๋…„ 9์›” 7์ผ

nestjs ์ •๋ฆฌ ํฌ์ŠคํŒ… ๊ธฐ๋Œ€ํ• ๊ฒŒ์š” ~~ ๐Ÿค“

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ