첫 단추로 Nest의 가장 핵심 기능을 알아 볼 것이다. 가장 기본적인 CRUD에 대해 알아 보자.

Language

주로 TypeScript를 사용할 것이고, 또한 Node.js를 애착한다. 그렇기에 Nest는 Typescript과 pure Javascript 둘 다 양립가능하다. Nest는 가장 최근의 language feature들의 이점을 가져온다. 그래서 vanilla JavaScript와 함께 사용하기 위해 Nest는 Babel compier를 사용한다.

Vanilla JS: 외부의 라이브러리나 프레임워크를 이용하지 않는 순수 자바스크립트를 말한다.
    둘을 사용하지 않기에, 사용했을 때 보다 호환성이 좋다는 게 특징이다. 이런 특징은 디버그(Debug)를 할 때 유용하다. 

Babel Compiler: Javascript 코드를 컴파일하는 도구로, 최신 JS 문법과 기능을 이전 버전의 JS로 변환해준다. 
    이를 통해 최신 Javascript 기능을 지원하지 않는 구형 브라우저나 환경에서도 최신 코드를 사용할 수 있게 해준다.

Prerequisites

사용자의 OS에는 Node.js(version >= 16)이 설치되어야 한다.

Setup

새 프로젝트를 setting하는데는 Nest CLI를 이용하여 꽤 간단하다.

$ npm i -g @nestjs/cli
$ nest new project-name

만일 TypeScript의 stricter한 feature set으로 설치하고자 한다면, --strict flag를 nest new command에 덧붙인다.

project-name 디렉토리가 만들어 질 것이며, node modules와 다른 boliderplate files들이 설치될 것이다. 그리고 src/ 디렉터리가 또한 만들어 질 것이며 아래와 같이 핵심 파일들이 있다.

src
    app.controller.spec.ts
    app.controller.ts
    app.module.ts
    app.service.ts
    main.ts

각 파일의 역할은 다음과 같다.

files description
app.controller.ts a single route가 있는 간단한 controller
app.controller.spec.ts controller를 위한 간단한 unit test를 위함
app.module.ts application의 root module
app.service.ts single method가 포함된 가장 기본적인 service
main.ts NestJs application의 진입 파일(entry file)에 대한 것이다. 핵심 함수인 NestFactory를 사용하여 Nest application instance를 생성한다.

main.ts는 동기 함수, bootstrap이 포함되어 있다.

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

Nest Application instance를 만들기 위해, NestFactory class를 사용한다. NestFactory는 application instance를 만들기 위한 여러 개의 method를 제공한다. create()는 application object를 반환한다. 이는 INestApplication interface에 따라진다. main.ts에서는 HTTP listener를 시작하여 애플리케이션이 들어오면 HTTP 요청을 대기하도록 한다.

Nest CLI에 기반된 project는 초기 프로젝트 structure를 만든다. 이는 개발자들이 각 모듈의 관습을 따르도록 장려한다.

//기본적으로, 애플리케이션이 생성되는 동안 오류가 발생하면, 애플리케이션은 코드 1과 함께 종료된다. 이 기본 동작을 변경하여 오류를 발생시키도록하고 싶다면, `abortOnError` 옵션을 비활성화하면 된다.

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

async function bootstrap() {
  try {
    const app = await NestFactory.create(AppModule, { abortOnError: false });
    // abortOnError: 이 옵션을 사용하면 애플리케이션이 종료되지 않고 오류를 던진다.
    // abort: 중단하다.
    await app.listen(3000);
  } catch (error) {
    console.error('Error starting the application:', error);
  }
}
bootstrap();

Platform

Nest는 플랫폼 종속적이지 않은 프레임워크를 지향한다. Platform independence는 framework로써 NestJs는 코드의 재사용성을 높여준다. 즉, 특정 플랫폼에 종속되지 않기에, 한 번 작성한 논리적 모듈이나 컴포넌트를 다양한 애플리케이션에서 다시 사용할 수 있다. 이는 개발 생산성을 높이고, 유지보수를 용이하게 한다. 기술적으로, Nest는 adapter가 만들어지면 어떤 Node HTTP framework라도 작동한다.
두 개의 HTTP platform이 있다. expressfastify가 있다.

플랫폼 독립적
1. 운영 체제 독립성: window, macOS, Linux...
2. 서버 프레임워크 독립성: Express, Fastify 등 다양한 HTTP 서버 프레임워크와 호환되어 사용할 수 있다.
3. 클라우드 플랫폼 독립성: 애플리케이션이 AWS, Google Cloud, Azure 등 다양한 클라우드 서비스 제공업체에서
    문제없이 배포되고 실행될 수 있다. 
HTTP platform description
platform-express Express는 node를 위한 잘 알려진 web framework다. 이는 많은 테스트를 거친, 프로덕션 준비가 된 라이브러리다. 커뮤니티에서 구현한 많은 리소스를 포함하고 있다. @nestjs/platform-express package가 기본적으로 사용된다. 많은 사용자들이 Express로 서비스를 제공받고 있으며, 이를 활성화하기 위해 별도의 작업이 필요치 않다.
platform-fastify Fastify는 높은 성능과 낮은 overhead의 framework이다. 이는 높은 효율성과 속도를 제공해준다.
Express: 웹 및 모바일 애플리케이션을 위한 일련의 강력한 기능을 제공하는 간결하고 
    유연한 Node.js 웹 애플리케이션 프레임워크이다. 즉 Node.js를 사용하여 쉽게 서버를 구성할 수 있게
    만든 클래스와 라이브러리의 집합체이다.

Fastify: Node.js를 위한 빠르면서도 오버헤드가 적은 웹 프레임워크이다.

어떤 플랫폼을 사용하든지, 이는 각자의 application interface를 나타낸다. 이는 각각 NestExpressApplicationNestFastifyApplication 사용된다.

만일 NestFactory.create() method를 사용할 때, (아래와 같이) app 객체는 specifc platform에 맞는 method들을 가질 것이다. 하지만 실제로 해당 platform API에 접근하려는 경우가 아니면 타입을 지정할 필요는 없다. 즉 특정 platform의 API를 사용하고 싶다면 타입을 지정한다.

  1. 기본 사용법(타입 지정 없음)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}

bootstrap();
  1. 타입 지정(Express)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.set('trust proxy', 1); // Express 고유의 메서드 사용 예시
  await app.listen(3000);
}

bootstrap();
  1. 타입 지정(Fastify)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestFastifyApplication } from '@nestjs/platform-fastify';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(AppModule);
  app.register(require('fastify-cors')); // Fastify 고유의 메서드 사용 예시
  await app.listen(3000);
}

bootstrap();

Running the application

설치 과정이 완료되면, 다음 명령어를 운영 체제의 명령 프롬프트에서 실행하여 애플리케이션이 들어오는 HTTP 요청을 대기하도록 시작할 수 있다.

npm run start

<팁!>
만일 process의 build 속도(x20 times faster build)를 높이고자한다면, SWC builder 를 사용할 수 있다. -b swc flag를 start script에 추가하면된다. npm run start -- -b swc

위의 command는 HTTP server가 src/main.ts에 정의된 port에서 대기하도록 한다. application을 running하고 브라우저에서 'http://localhost:3000/' 접속하면 'Hello word!' 메시지를 볼 수 있다.

src의 파일을 바꿀 때 변경사항을 바로바로 확인하고 싶다면 아래의 command로 시작한다.

$ npm run start:dev

이 command는 file의 변경 사항을 자동으로 recompile하고 server를 reloading한다.

Linting and formatting

CLI는 개발함에 있어 최고의 여건을 마련해준다.
생성된 Nest projects linterformatter의 code가 함께 설치된다. 이는 각각 eslintprettier이다.

code linter: 코드의 일관성과 스타일을 유지하고, 잠재적인 오류를 발견하기 위해 코드 품질을 검사하는 도구이다. 
    Nest 프로젝트는 eslint가 사용된다.
code formatter: 코드의 형식을 자동으로 맞춰주는 도구이다. Nest 프로젝트에서는 prettier가 사용된다.

안정성(stability)와 확장성(extensibility)을 보장해주기 위해, eslintprettier package가 사용된다.
이는 공식적인 extenstions을 통해 정돈된 IDE 완성을 시켜준다.

headless 환경에서, IDE가 필요 없는 환경에서는 바로 사용할 수 있는 npm 스크립트를 제공한다.

# Lint and autofix with eslint
$ npm run lint

# Format with prettier
$ npm run format
  • headless environment: IDE(통합 개발 환경)을 사용하지 않고, 주로 명령줄에서 작업하는 환경을 의미한다.
  • npm script: package.json 파일에 정의된 명령어로, 프로젝트의 빌드, 테스트, lint 등의 작업을 자동화하는 데 사용된다.
{
  "scripts": {
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:prod": "node dist/main",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  }
}

'NestJS > Docs_OVERVIEW' 카테고리의 다른 글

Exception filters  (0) 2024.06.05
Middleware  (1) 2024.06.04
Modules  (1) 2024.06.04
Providers  (0) 2024.06.04
Controllers  (0) 2024.06.03

Introduction

Nest는 효율적이고 확장가능한 server-side application을 building하기 위한 프레임워크이다.
또한 Javascript, TypeScript로 작성하는 하며 OOP(Object Oriented Programming), FP(Functional Programming), FRP(Functional Reactive Programming)적 요소를 결합하였다.

client-side: 클라이언트측(브라우저)에서 수행하는 처리를 뜻함
    - HTML, JavaScript, CSS, Ajax, JQuery ...
server-side: 서버 측에서 수행하는 처리를 뜻함
    - PHP, JSP, ASP, Ruby, Python ...

OOP(Object Oriented Programming):  객체 지향적인  프로그래밍, 즉, C언어같은 절차지향적인 프로그래밍이 아닌 객체의 
    관점에서 프로그래밍을 한다는 것
FP(Functional Programming): 모든 것을 순수 함수로 나누어 문제를 해결하는 기법, 작은 문제를 해결하기 위한 함수를 작성하여
    가독성을 높이고 유지보수를 용이하게 해준다.
RP(Reactive Programming): 데이터의 흐름과 변경사항의 전파에 중점을 둔 선언적 프로그래밍 패러다임
    e.g. 엑셀에서 C1 = A1 + B1 라고 선언적으로 정의한다. 이 때 C1의 값은 A1과 B1의 값에 따라 즉시 반영되어 변경된다.
FRP(Functional Reactive Programming): 비동기적인 데이터 처리를 간단한 함수를 통해 수행하는 프로그래밍    

좀 더 상세하게, Nest는 강력한 HTTP 서버 프레임워크를 제공한다. default로 Express와 그리고 Fastify가 있다.

Express: Node.js 환경에서 웹 애플리케이션과 API를 구축하기 위해 널리 사용되는 웹 프레임워크이다.
Fastify: 마찬가지로 Fastify도 Node.js 기반의 웹 프레임워크로, 웹 애플리케이션과 API 구축에 사용된다. 비교적 새로운 프레임워크이다.

Nest는 Node.js 프레임워크(Express/Fastify)를 통해 높은 수준의 추상화를 제공한다. 또한 Nest의 API들은 바로 개발자에게 보여준다. 개발자들은 Nest에서 사용가능한 수많은 yarn, npm 따위를 통해 많은 모듈들을 선택할수 있는 선택권을 가진다.

Philosophy

최근 몇년사이에, Node.js 덕분에 JavaScript는 front와 backend application에서 "lingua franca"가 되었다. Node.js 덕분에 Angular, React, Vue와 같은 프레임워크가 생겼고 이를 통해 frontend Application들은 생상성, 창의성, 속도, testable, 그리고 확장가능해 졌다. 하지만 다양한 Node와 server-side를 위한 JavaScript 라이브러리, 도구들은 존재하지만 소프트웨어 아키텍처와 관련된 주요 문제들을 해결해 주진 못했다.

Nest는 out-of-the-box(별도의 설치가 필요 없는) application architecture을 제공한다. 이는 개발자들에게 testable하고 확장가능하고 또 loosely=coupled하고 마지막으로 쉽게 maintainable한 application을 제공한다. Angular에 의해 영감을 받은 architecture이다.

Installation

시작을 위해서는 Nest CLI을 발판삼아 project를 시작하거나, 혹은 이미 진행되고 있는 project를 clone하는 수가 있다.

$ npm i -g @nestjs/cli
$ nest new project-name

위의 코드를 작성하면 새로운 project를 위한 directory를 만들고 초기 base structure를 위한 Nest files, modules들이 채워진다.

만일 저 엄격한 TypeScript를 만들고자 한다면 --strictnest new에 추가한다.
strict을 추가한다면 tsconfig.json에 몇 가지 설정이 바뀐다.

// 설정 전
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
// 설정 후
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true
  1. strictNullCheck: 변수가 null 또는 undefined 값을 가질 수 있는지 여부를 엄격하게 검사하도록 한다.
    이 옵션을 활성화하면,모든 변수와 속성이 null이나 undefined 값을 가질 수 있는 지 명시적으로 선언해야 한다.
    그렇지 않으면 컴파일 오류가 발생한다.
  2. noImplicitAny: 변수나 함수의 매개변수가 암시적으로 'any' 타입을 가지지 않도록 강제하는 옵션. 이 옵션을 활성화하면, 타입을 명시하지 않은 변수나 함수 매개변수에 대해 TS가 암시적으로 any 타입을 할당하지 못하도록 한다.
  3. strictBindCallApply: Function.prototype.bind, Function.prototype.call, Function.prototype.apply 메서드 사용 시 타입 검사를 엄격하게 수행하도록 강제한다. 이 옵션을 활성화하면, 메서드를 사용할 때 인수의 타입이 정확히 일치하지 않으면 컴파일 오류가 발생한다. 즉 'bind', 'call', 'apply' 메서드를 사용할 때 정확한 타입 검사가 수행되어 더 안전한 코드를 작성할 수 있다.
// strictBindCallApply 비활성화
function add(a: number, b: number): number {
  return a + b;
}

const boundAdd = add.bind(null, '1'); // 'a'는 암시적으로 'any' 타입을 가짐
console.log(boundAdd(2)); // 정상 동작 (하지만 타입 안전성이 없음)

const result = add.call(null, '1', '2'); // 'a'와 'b'는 암시적으로 'any' 타입을 가짐
console.log(result); // 정상 동작 (하지만 타입 안전성이 없음)

// strictBindCallApply 활성화
function add(a: number, b: number): number {
  return a + b;
}

const boundAdd = add.bind(null, '1'); // 컴파일 오류: 'string' 타입은 'number' 타입에 할당할 수 없습니다.
console.log(boundAdd(2)); // 오류 발생

const result = add.call(null, '1', '2'); // 컴파일 오류: 'string' 타입은 'number' 타입에 할당할 수 없습니다.
console.log(result); // 오류 발생

// 수정된 코드
const boundAddCorrect = add.bind(null, 1); // 정상 동작
console.log(boundAddCorrect(2)); // 정상 동작

const resultCorrect = add.call(null, 1, 2); // 정상 동작
console.log(resultCorrect); // 정상 동작
  1. forceConsistentCasingInFileNames: TS의 컴파일러 옵션 중 하나로, 파일 이름의 대소문자 일관성을 강제하는 옵션이다. 이 옵션을 활성화하면, 파일 시스템이 대소문자를 구분하지 않더라도 파일 이름의 대소문자가 일관되지 않으면 컴파일 오류가 발생한다.
// 파일 이름이 MyComponent.ts인 경우.

// 비활성화
import { MyComponent } from './myComponent'; // 오류 없음 (하지만 대소문자 일관성 없음)

// 활성화
import { MyComponent } from './myComponent'; // 컴파일 오류: 대소문자 불일치

// 수정된 코드
import { MyComponent } from './MyComponent'; // 정상 동작 (대소문자 일관성 유지)
  1. noFallThroughtCasesInSwitch: TS의 컴파일러 옵션 중 하나로, switch 문에서 의도하지 않은 경우 값의 누락(fallthrough)을 방지하도록 강제하는 옵션이다. 이 옵션을 활성화하면 'switch'문에서 'case' 블록이 'break'문을 포함하지 않으면 컴퍼일 오류가 발생한다. 이는 코드의 가독성과 안전성을 높이고, 의도하지 않은 논리 오류를 방지하는데 도움이 된다.
npm i -g @nestjs/cli
에서 '-g'는 'global'을 의미한다. 이는 해당 패키지를 전역(global)로 설치하겠다는 뜻이다.
전역으로 설치된 패키지는 시스템 전체에서 접근할 수 있으며, 모든 프로젝트에서 사용할 수 있다.

Alternatives

다른방법으로, Git을 통해 TypeScript기반의 nest를 설치하고자 한다면

$ git clone https://github.com/nestjs/typescript-starter.git project
$ cd project
$ npm install
$ npm run start

참고로, 만일 git history없이 repo를 clone하고자 한다면, degit을 이용할 수 있다.
또한 TS가 아닌 JS로 시작하고자 한다면, typescript-starter대신 javascrip-starter.git을 쓸 수 있다.

마지막으로 the core와 supporting files을 포함한 project로 시작하고자 한다면 다음과 같다.
이 경우에, project의 boilerplace files를 만들 책임이 있다.

npm install @nestjs/core @nestjs/common rxjs reflect-metadata

1. 호스팅이란?

호스트이란 서버 컴퓨터의 전체 또는 일정 공간을 이용할 수 있도록 임대해 주는 서비스
인터넷 비즈니스를 시작하기 위해서는 기본적으로 홈페이지의 글/이미지/동영상 등 정보가 저장될 수 있는 '서버' 공간이 필요하다.
개인이나 기업에서 서버를 직접 보유하고 관리하는 것은 비용과 시간이 많이 들기에, '호스팅'을 이용하면 서버를 일정기간 임대하고 관리받을 수 있어 쉽게 비즈니스를 시작할 수 있다.

2. 호스팅의 구분

기본적으로 "호스팅""웹 호스팅"은 동일 용어로 간주한다.

2.1 공유 호스팅(Shared Hosting)

공유 호스팅은 여러 웹 사이트가 하나의 물리적인 서버를 공유하는 형태이다.
트래픽 양이 제한된, 새로 개설된 작은 규모의 웹사이트라면 공유 호스팅으로 시작하는 것이 좋다.
이는 가장 저렴한 호스팅 유형에 속하며, 서버 및 인프라 구축이 필요가 없다는 장점이 있다.


단점으로는 서버의 일부분만 사용하기에 사용량이 제한되고 서버 관리 권한이 없다.
또한 갑작스럽게 트래픽이 급증하는 경우에는 공유서버가 적절히 대처하지 못한다.
그렇기에 휴일에 트래픽이 크게 증가하는 온라인 쇼핑몰 사이트의 경우에는 공유 호스팅은 적절하지 못하다.
이런 경우에는 비즈니스에 영향을 주지 않고 더 많은 양의 트래픽을 처리할 수 있는 유형이 필요하다.
또한 서버 통제에 대한 권한, 리소스의 부족과 같은 문제들이 발생한다.


용량/트래픽 제한이라는 형태만 있는 채로 동일한 리소스를 나눠 쓰고 있기에
같은 서버를 공유하고 있는 다른 웹사이트에 트래픽이 몰리는 경우에는 서버 다운 타임(이유 없는 503 에러)의 영향을 받을 수 있으므로
자신의 웹사이트 뿐만이 아니라 비즈니스에도 영향을 미친다.

2.2 전용 호스팅(Dedicated Hosting)


공유 호스팅의 경우 서버중 '일부'만 빌리는 형태라면, 전용 호스팅은 서버 하나를 통째로 구매하는 형태이다.
서버 운영에 필요한 인프라와 기술력까지 제공받을 수 있는 서비스이다.


앞서 언급한 대로 공유 호스팅은 저렴한 가격으로 서버 및 인프라의 구축이 필요 없지만 서버의 일부분만 사용하기에
사용량이 제한되고 서버 관리 권한이 없다. 그렇기에 소규모 웹사이트에 주로 사용된다.


허나 전용 호스팅은 서버 관리에 대한 직접권한을 갖고 서버를 단독으로 사용하기에 보안상으로도 유리하지만
초기 구축단계에서 웹호스팅에 비해 시간과 비용이 많이 든다는 단점이 있다.


전용 호스팅은 주로 회사의 인트라넷, 대형 쇼핑몰 등 고정적으로 대용량 트래픽과 DB가 많이 사용되는 곳에 사용된다.

2.3 VPS(Virtual Private Server)


각자 물리 서버 컴퓨터의 자원을 공유하는 "공유 호스팅"과 달리 VPS물리적인 호스팅 서버를 일정 단위로 공간을 나눠 파는 것이다.

물리적인 하나의 서버를 나눠쓴다는 점에서 공유호스팅과 비슷하나, 위 이미지처럼 메모리와 같은 리소스를 애초에 배당 받기에
다른 인원의 트래픽 따위로 인한 피해를 보는 상황이 발생하지 않는다.


또, 물리적인 서버 컴퓨터가 아닐 뿐 가상으로 생겨난 하나의 서버 컴퓨터이기에 모든 설정을 바꿀 수 있는 루트 권한이 있다.
이 있고 OS선택도 가능하기에 리눅스 가상 서버 구축을 해도 되고 윈도우 가상 서버 구축도 가능하다.


단점으로 직접 통제해야하기에 서버에 많이 사용하는 리눅스에 대해서 사용할 줄 알거나 배워야 한다는 점이 있다.
또한 다른 VPS 사용자를 타깃으로 하는 DDoS 공격에 대해 동시에 영향을 받을 수도 있다.

2.4 클라우드 호스팅(Cloud Hosting)

클라우드 호스팅이란 연결된 가상 및 물리적 클라우드 서버의 네트워크에서 애플리케이션이나 웹 사이트를 호스팅하는 것을 말한다. 대표적으로 아마존 AWS, 구글의 클라우드 플랫폼 등 다양한 서비스가 존재한다.


가상 서버의 리소스를 실시간으로 유연하게 확장 및 축소가 가능하다는 점이 가상 서버 호스팅과 다른 클라우드의 큰 차이점이다.


클릭 몇 번으로 10분 안에 서버 생성하고 관리할 수 있고, 트래픽의 변동에도 유연하게 대처할 수 있기 때문에 일시적인 이벤트나 인프라가 유동적인 곳에 사용하기 편리하다는 장점이 있다.


단점으로는 트래픽이 폭증할 경우 종량제 요금으로 인한 비용 상승의 가능성이 있다. 클라우드 호스팅의 변화로 단점이 사라지고 있는 추세이다.

2.5 코로케이션

코로케이션은 고객의 서버를 내부에서 관리하지 않고 데이터 센서에서 직접 위탁 받아 초고속 인터넷 백본망에서 고객의 서버와 통신장비를 직접 연결하고 관리해주는 서비스이다. IDC 공간을 임대해서 그 안에 고객이 소유하고 있던 서버를 입주시키고 위탁 관리를 맡기는 형태다.


IDC란 서버가 입주하기에 온도와 습도를 적절하게 갖춘 쾌적한 환경과 빠른 네트워크 환경을 갖추고 있는 서버보관센터와 같은 서비스를 받쳐주는 보조 기능이다.

참조

https://velog.io/@dreamjh/%ED%98%B8%EC%8A%A4%ED%8C%85%EC%9D%B4%EB%9E%80
https://namaniflow.tistory.com/72#2.2.%20Virtual%20Private%20Server%20(VPS)
https://gentlysallim.com/vps-%ED%98%B8%EC%8A%A4%ED%8C%85%EC%9D%B4%EB%9E%80-%EC%9B%B9%ED%98%B8%EC%8A%A4%ED%8C%85%EC%9D%B4%EB%9E%80-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC/
https://m.post.naver.com/viewer/postView.naver?volumeNo=31780520&memberNo=2521903
https://modoo-game-nam.tistory.com/entry/%EC%BD%94%EB%A1%9C%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%B4%EB%9E%80-%EA%B7%B8-%EB%9C%BB%EA%B3%BC-%ED%98%B8%EC%8A%A4%ED%8C%85-%EC%B0%A8%EC%9D%B4%EC%A0%90-%ED%95%9C%EB%B2%88%EC%97%90

+ Recent posts