<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_sprinkler.log</title>
        <link>https://velog.io/</link>
        <description>2년차 백엔드 개발자</description>
        <lastBuildDate>Tue, 21 Jun 2022 07:06:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_sprinkler.log</title>
            <url>https://images.velog.io/images/dev_sprinkler/profile/09116d45-fcd9-40d6-b668-8564e2582720/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_sprinkler.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_sprinkler" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Nest.js 알아보기 - 컨트롤러]]></title>
            <link>https://velog.io/@dev_sprinkler/Nest.js-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC</link>
            <guid>https://velog.io/@dev_sprinkler/Nest.js-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC</guid>
            <pubDate>Tue, 21 Jun 2022 07:06:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_sprinkler/post/6336223a-22fe-4c2f-9b0e-6f77adc37382/image.png" alt=""></p>
<h3 id="서론">서론</h3>
<p>컨트롤러의 목적은 애플리케이션을 위한 특정 요청들을 받는 것이다.
클라이언트로부터 들어온 HTTP 요청들은 라우팅 메커니즘에 따라서 알맞은 컨트롤러로 분배된다.</p>
<p>Nest에서는 @Controller, @Get, @Post 등 데코레이터를 이용해서 컨트롤러를 구성할 수 있다.</p>
<blockquote>
<p>nestcli를 이용해서 CRUD 컨트롤러를 간단히 만들 수 있다.</p>
<pre><code class="language-bash">$ nest g resource [name]</code></pre>
</blockquote>
<hr>
<h3 id="controller-데코레이터">Controller 데코레이터</h3>
<pre><code class="language-typescript">import { Controller, Get } from &#39;@nestjs/common&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  @Get()
  findAll(): string {
    return &#39;This action returns all cats&#39;;
  }
}</code></pre>
<p>@Controller, @Get 데코레이터를 사용하는 예제 코드</p>
<p>@Controller 데코레이터는 관계있는 라우터를 그룹화하는데 사용한다. 데코레이터의 인자로 라우팅 주소를 받는다. 
@Controller(&#39;cats&#39;)라고 작성하면 /cats 주소로 접근하는 모든 요청은 해당 컨트롤러를 거쳐갈 것이다.</p>
<p>Express.js로 따지자면 아래와 같이 만들 수 있다.</p>
<pre><code class="language-typescript">app.use(&#39;cats&#39;, CatsController);</code></pre>
<p>@Get 데코레이터는 get메소드로 들어온 HTTP 요청을 받는 핸들러를 의미한다.</p>
<p>@Controller 데코레이터와 마찬가지로 인자로 라우팅 주소를 받는다. 인자로 아무 값도 넘기지 않으면 &#39;/&#39;로 연결된다.</p>
<p>즉, @Controller(&#39;cats&#39;) 내부의 @Get()는 /cats 경로로 GET 메소드 HTTP 요청이 들어오는 경우에 실행하는 핸들러가 된다.</p>
<p>만약 @Get(&#39;/profile&#39;) 애노테이션으로 라우터를 만든다면 /cats/profile 경로로 들어온 HTTP 요청을 처리할 것이다.</p>
<hr>
<h3 id="response-object">Response object</h3>
<p>기본적으로 Nest 컨트롤러는 상태코드 200을 반환한다.
Nest는 두 종류의 response 옵션을 제공하는데 아래와 같다.</p>
<ul>
<li><p>Standard (권장)
핸들러가 객체 또는 배열인 경우에 자동으로 이를 JSON으로 직렬화한다.
원시타입(string, number...)인 경우에는 직렬화하지 않고 보낸다.
상태코드는 항상 기본 200이다. (POST의 경우 201)
상태코드는 @HttpCode 데코레이터로 설정할 수 있다.</p>
</li>
<li><p>Library-specific
특정 라이브러리의 response 객체를 의미함. (e.g. Express)
이러한 객체는 @Res 데코레이터를 사용해서 호출한다.</p>
</li>
</ul>
<blockquote>
<p>Nest에서도 @Res(), @Next() 등을 호출해서 사용할 수 있다.
이 데코레이터들을 호출하면 standard 옵션은 자동으로 비활성화 된다.
두 옵션을 동시에 사용하려면 @Res({ passthrough: true }) 이렇게 passthrough 옵션을 true로 설정하면 된다.</p>
</blockquote>
<hr>
<h3 id="request-object">Request object</h3>
<p>핸들러가 클라이언트의 request 객체에 접근할 일이 종종있다.
Nest는 request 객체에 접근할 수 있는 @Req 데코레이터를 지원한다.</p>
<pre><code class="language-typescript">import { Controller, Get, Req } from &#39;@nestjs/common&#39;;
import { Request } from &#39;express&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return &#39;This action returns all cats&#39;;
  }
}</code></pre>
<p>@Req 데코레이터 사용 예시</p>
<p>request 객체 전체가 아니라 클라이언트로부터 전달받은 body나 querystring만 필요한 경우에도 @Body, @Query 데코레이터를 위와 같이 호출하여 사용할 수 있다.</p>
<hr>
<h3 id="resources">Resources</h3>
<p>Nest는 모든 기본 HTTP 메소드를 데코레이터로 제공한다. 이 데코레이터들은 &#39;@nestjs/common&#39; 모듈에 선언되어 있다.
@Get, @Post, @Put, @Delete, @Patch, @Options, @Head가 있으며 추가로 @All 데코레이터로 이 모든 메소드를 핸들링하는 라우터를 만들 수 있다.</p>
<pre><code class="language-typescript">import { Controller, Get, Post } from &#39;@nestjs/common&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  @Post()
  create(): string {
    return &#39;This action adds a new cat&#39;;
  }

  @Get()
  findAll(): string {
    return &#39;This action returns all cats&#39;;
  }
}</code></pre>
<p>POST 메소드 라우팅 예시</p>
<hr>
<h3 id="route-wildcards">Route wildcards</h3>
<p>라우트 주소에 와일드카드 문자를 포함할 수 있다.</p>
<pre><code class="language-typescript">@Get(&#39;ab*cd&#39;)
findAll() {
  return &#39;This route uses a wildcard&#39;;
}</code></pre>
<p>여기서 &#39;ab*cd&#39; 라우트 주소는 &#39;abcd&#39;, &#39;ab_cd&#39;, &#39;abecd&#39; 등과 매치된다.
?, +, *, (, ) 문자를 라우트 주소에 사용할 수 있고 정규표현식에서의 사용법을 일부 포함한다.</p>
<hr>
<h3 id="asynchronicity">Asynchronicity</h3>
<p>Nest는 async / await과 RxJS의 observable streams을 지원한다.
async 함수는 반드시 Promise 타입을 반환한다.</p>
<pre><code class="language-typescript">// async 함수
@Get()
async findAll(): Promise&lt;any[]&gt; {
  return [];
}

// observable streams 사용
@Get()
findAll(): Observable&lt;any[]&gt; {
  return of([]);
}</code></pre>
<hr>
<h3 id="request-payloads">Request payloads</h3>
<p>위에서 살펴본 POST 메소드 예시 코드를 보면 핸들러가 클라이언트로부터 아무런 값도 받지 않고 있다.</p>
<p>@Body 애노테이션을 이용해서 클라이언트로부터 request body를 받아보자.</p>
<pre><code class="language-typescript">class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  return &#39;This action adds a new cat&#39;;
}</code></pre>
<blockquote>
<p>ValidationPipe 기능을 사용해서 클라이언트로부터 받은 값들이 유효한지 검사할 수 있다.</p>
</blockquote>
<hr>
<pre><code class="language-typescript">import { Controller, Get, Query, Post, Body, Put, Param, Delete } from &#39;@nestjs/common&#39;;
import { CreateCatDto, UpdateCatDto, ListAllEntities } from &#39;./dto&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return &#39;This action adds a new cat&#39;;
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(&#39;:id&#39;)
  findOne(@Param(&#39;id&#39;) id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(&#39;:id&#39;)
  update(@Param(&#39;id&#39;) id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(&#39;:id&#39;)
  remove(@Param(&#39;id&#39;) id: string) {
    return `This action removes a #${id} cat`;
  }
}</code></pre>
<p>기본적인 CRUD 컨트롤러 구성</p>
<hr>
<p>이렇게 만든 컨트롤러를 Nest가 인식할 수 있게 root module에 추가해주어야 한다.</p>
<pre><code class="language-typescript">import { Module } from &#39;@nestjs/common&#39;;
import { CatsController } from &#39;./cats/cats.controller&#39;;

@Module({
  controllers: [CatsController],
})
export class AppModule {}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nest.js 알아보기 - 개요]]></title>
            <link>https://velog.io/@dev_sprinkler/Nest.js-Node.js-%EC%84%9C%EB%B2%84-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@dev_sprinkler/Nest.js-Node.js-%EC%84%9C%EB%B2%84-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Fri, 17 Jun 2022 07:03:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_sprinkler/post/3c483aeb-61c2-4bc0-927e-2819706e118d/image.png" alt=""></p>
<p><a href="https://docs.nestjs.com/">Nest.js 공식 문서</a>
<a href="https://github.com/nestjs/nest">깃허브</a></p>
<p>Nest.js는 Node.js 서버사이드 애플리케이션을 만드는 프레임워크다.
타입스크립트를 완벽하게 지원하며, 오픈소스 프로젝트다.</p>
<p>기본적으로 <a href="https://expressjs.com/ko/">Express</a>를 이용해서 HTTP 통신을 하며 <a href="https://www.fastify.io/">Fastify</a>를 사용할 수도 있다.</p>
<hr>
<p>공식 홈페이지에서는 두가지 설치 방법을 설명하고 있다.</p>
<pre><code class="language-bash">$ npm i -g @nestjs/cli
$ nest new project-name</code></pre>
<p>첫째는 @nestjs/cli 모듈을 사용하는 것이고,</p>
<pre><code class="language-bash">$ git clone https://github.com/nestjs/typescript-starter.git project
$ cd project
$ npm install
$ npm run start</code></pre>
<p>둘째는 스타터 프로젝트를 클론하여 사용하는 것이다.</p>
<p>위에 링크한 깃 저장소에 nest에서 공식으로 지원하는 여러 서버 애플리케이션 샘플을 참고할 수도 있다.</p>
<hr>
<p>Nest.js는 기본적으로 컨트롤러 - 프로바이더 - 모듈 구조로 애플리케이션을 구성한다.</p>
<p>클라이언트로부터 HTTP 요청이 nest 서버에 들어오면 라우팅 메커니즘에 따라 알맞은 컨트롤러로 요청이 분배된다.
컨트롤러는 HTTP 요청을 받아서 메소드를 수행하고 결과를 클라이언트로 리턴한다.</p>
<pre><code class="language-typescript">import { Controller, Get } from &#39;@nestjs/common&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  @Get()
  findAll(): string {
    return &#39;This action returns all cats&#39;;
  }
}</code></pre>
<p>공식 문서의 예제 코드를 보면 @nestjs/common 모듈으로부터 Controller와 Get 애노테이션을 임포트해서 Cats 컨트롤러 클래스를 만들고 있다.</p>
<p>@Controller(&#39;cats&#39;)는 &#39;/cats&#39; 경로에 요청이 오면 해당 컨트롤러를 참조하겠다는 뜻이 된다.
바로 아래에 @Get()는 &#39;/cats&#39; 경로에 Get 메서드로 요청이 오면 해당 함수를 실행하겠다는 뜻이 된다.</p>
<p>결과적으로 클라이언트가 &#39;http://{domain}/cats&#39; 에 Get 메서드로 요청을 보내면 클라이언트는 &#39;This action returns all cats&#39;라는 문자열을 서버로부터 리턴받을 것이다.</p>
<hr>
<p>프로바이더는 Nest.js의 근본적인 개념으로, 수많은 Nest 기본 클래스들이 프로바이더로 취급된다.
프로바이더의 핵심은 --마치 자바 스프링처럼-- 의존성 주입(Dependency Injection)을 통해 관리한다는 것이다.</p>
<pre><code class="language-typescript">import { Injectable } from &#39;@nestjs/common&#39;;
import { Cat } from &#39;./interfaces/cat.interface&#39;;

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}</code></pre>
<p>다시 예제 코드다. 이번엔 CatsService라는 클래스를 만들었다. @Injectable 애노테이션을 임포트해서 사용하고 있는데, 이름 그대로 다른 클래스에서 의존성을 주입할 수 있게 해준다.</p>
<pre><code class="language-typescript">import { Controller, Get, Post, Body } from &#39;@nestjs/common&#39;;
import { CreateCatDto } from &#39;./dto/create-cat.dto&#39;;
import { CatsService } from &#39;./cats.service&#39;;
import { Cat } from &#39;./interfaces/cat.interface&#39;;

@Controller(&#39;cats&#39;)
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise&lt;Cat[]&gt; {
    return this.catsService.findAll();
  }
}</code></pre>
<p>위에서 만든 프로바이더(CatsService)는 이렇게 컨트롤러에서 호출해서 사용할 수 있다.</p>
<hr>
<p>마지막으로 모듈이다. 모듈은 컨트롤러와 마찬가지로 @Module 애노테이션을 임포트하여 사용한다.
모듈은 Nest가 애플리케이션의 계층 구조를 만들 수 있도록 메타데이터를 전달해준다.</p>
<p>Nest 애플리케이션은 기본적으로 root module이라고 하는 기본 모듈을 가진다.
root module은 Nest가 각 모듈과 프로바이더의 관계성, 의존성을 확인할 수 있게하는 애플리케이션 그래프의 시작점이 된다.</p>
<pre><code class="language-typescript">import { Module } from &#39;@nestjs/common&#39;;
import { CatsController } from &#39;./cats.controller&#39;;
import { CatsService } from &#39;./cats.service&#39;;

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}</code></pre>
<p>다시 예제 코드로 돌아와서, 앞서 만들었던 컨트롤러와 프로바이더를 포함하는 CatsModule을 만들었다.</p>
<pre><code class="language-typescript">import { Module } from &#39;@nestjs/common&#39;;
import { CatsModule } from &#39;./cats/cats.module&#39;;

@Module({
  imports: [CatsModule],
})
export class AppModule {}</code></pre>
<p>CatsModule은 다시 AppModule(root module)에서 임포트하여 Nest 애플리케이션에서 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[npm install --global EACCESS: permission denied]]></title>
            <link>https://velog.io/@dev_sprinkler/npm-install-global-EACCESS-permission-denied</link>
            <guid>https://velog.io/@dev_sprinkler/npm-install-global-EACCESS-permission-denied</guid>
            <pubDate>Mon, 30 May 2022 05:16:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev_sprinkler/post/dc0d512e-c0a8-4c1f-8289-8af8b672cd5e/image.png" alt=""></p>
<p>npm install --global으로 전역 모듈 설치 시 에러 발생.</p>
<p>/usr/lib/node_modules 디렉터리에 접근할 권한이 없어서 발생하는 에러.</p>
<p>sudo로 설치하면 설치가 잘 되지만 해당 모듈을 사용하려면 역시 sudo 권한이 필요함.</p>
<p>npm 공식 사이트에서는 npm 기본 경로를 변경할 것을 권장함.</p>
<p>그 방법은 <a href="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally#manually-change-npms-default-directory">링크</a>에 자세히 설명되어 있음.</p>
<pre><code>1. mkdir ~/.npm-global
2. npm config set prefix &#39;~/.npm-global&#39;
3. export PATH=~/.npm-global/bin:$PATH
4. source ~/.profile
5. npm install -g {module}</code></pre><p><img src="https://velog.velcdn.com/images/dev_sprinkler/post/f23ef05f-fb66-4411-9fc8-59a33abcd0df/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_sprinkler/post/943988cd-724e-477c-9b0a-9a97a60434e7/image.png" alt=""></p>
<p>위와 같이 새로 생성한 .npm-global 디렉터리 하위에 전역 모듈이 설치됨.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js 클러스터 모듈]]></title>
            <link>https://velog.io/@dev_sprinkler/Node.js-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@dev_sprinkler/Node.js-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Mon, 07 Feb 2022 03:44:20 GMT</pubDate>
            <description><![CDATA[<p>node.js의 이벤트 루프는 싱글스레드에서 돌아간다. node.js 서버에서 멀티코어 CPU를 제대로 활용하기 위해서는 클러스터 모듈을 사용해 프로세스를 포크하여 실행해야 한다.</p>
<p>node.js 클러스터는 기본적으로 윈도우즈를 제외하고 라운드로빈 방식으로 스케쥴링 한다.</p>
<blockquote>
<p>윈도우즈에서는 기본적으로 OS가 직접 스케쥴링 하지만, libuv가 IOCP 처리를 오버헤드 없이 잘 분산할 수 있다면 라운드로빈으로 바뀐다.</p>
</blockquote>
<p>CPU 코어 수만큼 워커를 생성하는 예</p>
<pre><code class="language-javascript">import cluster from &#39;cluster&#39;;
import { cpus } from &#39;os&#39;;
import http from &#39;http&#39;;

const numCPUs = cpus().length;

if (cluster.isPrimary) {
    for (let i = 0; i &lt; numCPUs; i++) {
        cluster.fork();
    }
} else {
    http.createServer((req, res) =&gt; {
        res.writeHead(200);
        res.end(&#39;hello world\n&#39;);
    }).listen(8080);
}</code></pre>
<blockquote>
<p>마스터는 워커들을 관리해야 하기에 워커를 생성 및 관리하는 기능 등만 넣는 것이 좋다.</p>
</blockquote>
<p>아래와 같이 스케쥴링 방식을 직접 변경할 수 있다.</p>
<pre><code class="language-javascript">// 라운드로빈 방식
cluster.schedulingPolicy = cluster.SCHED_RR

// OS가 직접 스케쥴링
cluster.schedulingPolicy = cluster.SCHED_NONE</code></pre>
<p>워커는 fork한 만큼 생성되고, 다음과 같이 현재 스레드가 마스터인지 아닌지 확인할 수 있다.</p>
<pre><code class="language-javascript">let worker = cluster.fork();

if (cluster.isPrimary) {
    // primary
} else if (cluster.isWorker) {
  // none primary
}</code></pre>
<blockquote>
<p>cluster.isMaster 는 deprecate 되고 cluster.isPrimary에 통합되었다.</p>
</blockquote>
<p>클러스터 이벤트 리스너를 아래처럼 사용할 수 있다.</p>
<pre><code class="language-javascript">// 워커가 죽으면 새로운 워커를 포크하는 함수
cluster.on(&#39;exit&#39;, (worker, code, signal) =&gt; {
    console.log(&#39;worker %d died (%s). restarting...&#39;,
        worker.process.pid, signal || code);
    cluster.fork();
});</code></pre>
<blockquote>
<p>클러스터 이벤트는 disconnect, exit, fork, listening, message, online, setup 등이 있다.</p>
</blockquote>
<p><a href="https://velog.io/@dev_sprinkler/PM2-Node.js-Process-manager">pm2</a>의 클러스터 모드를 사용하면 클러스터 생명주기를 개발자가 직접 관리하지 않아도 된다. pm2 공식 문서에 따르면 클러스터 모드는 node.js의 클러스터 모듈을 사용해 구현했다고 한다.</p>
<p>참고</p>
<p><a href="https://nodejs.org/dist/latest-v17.x/docs/api/cluster.html">Node.js 공식문서 | Cluster</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[V8 엔진]]></title>
            <link>https://velog.io/@dev_sprinkler/V8-%EC%97%94%EC%A7%84</link>
            <guid>https://velog.io/@dev_sprinkler/V8-%EC%97%94%EC%A7%84</guid>
            <pubDate>Tue, 25 Jan 2022 09:05:09 GMT</pubDate>
            <description><![CDATA[<h1 id="what-is-v8">What is V8?</h1>
<p>V8은 C++로 작성된 구글의 오픈소스 자바스크립트, 웹어셈블리 엔진이다. 구글 크롬과 Node.js에서 사용한다. </p>
<p>자바스크립트로 작성된 코드를 실행시간에 바이트코드로 컴파일하여 실행한다. → JIT 컴파일 방식</p>
<p>ECMA ES-262 표준에 맞추어 구현되어 있다.</p>
<p>내부적으로 몇가지 스레드를 사용한다.</p>
<ul>
<li>코드를 가져오고, 컴파일하고 실행하는 메인 스레드</li>
<li>컴파일을 위한 스레드 → 코드를 최적화하는 동안 메인스레드가 계속 동작할 수 있도록 함</li>
<li>어떤 메소드를 실행할 때 시간이 많이 걸리는지 측정하는 프로파일러 스레드 → TurboFan으로 최적화</li>
<li>가비지 컬렉터 스윕을 관리하기 위한 스레드</li>
</ul>
<p>인라인 캐싱, 히든 클래스 등 여러 최적화 기법을 사용하고 있다.</p>
<h1 id="v8-엔진이-동작하는-과정">V8 엔진이 동작하는 과정</h1>
<ol>
<li>JS 코드를 파서에 전달</li>
<li>파서에서 소스코드 분석 후 추상 구문 트리(AST)로 변환</li>
<li>AST를 Ignition에 전달 → JS를 바이트코드로 변환하는 인터프리터</li>
<li>바이트코드 실행</li>
<li>자주 사용되는 코드를 TurboFan에 전달 → 최적화 코드로 컴파일</li>
<li>그중 자주 사용하지 않는 코드를 Deoptimize</li>
</ol>
<p>v5.9부터 Full-codegen과 Crankshaft를 사용하지 않는다.</p>
<p><a href="%5Bhttps://v8.dev/blog/launching-ignition-and-turbofan%5D(https://v8.dev/blog/launching-ignition-and-turbofan)">full-codegen, crankshaft 버리기 까지의 히스토리</a></p>
<p><img src="https://images.velog.io/images/dev_sprinkler/post/575c0745-9569-4666-a7a8-ae22419e5576/Untitled.png" alt=""></p>
<blockquote>
<p>V8 엔진의 컴파일러 파이프라인</p>
</blockquote>
<h3 id="bytecode">Bytecode</h3>
<p>고급 언어로 작성된 소스코드를 가상머신이 이해할 수 있는 중간 코드로 컴파일한 것</p>
<p><img src="https://images.velog.io/images/dev_sprinkler/post/6ebe6bc4-4613-4733-a9e2-f1ed81b13a1b/Untitled.png" alt=""></p>
<blockquote>
<p>ignition의 바이트코드 파이프라인</p>
</blockquote>
<h3 id="참고">참고</h3>
<p><a href="https://nodejs.dev/learn/the-v8-javascript-engine">https://nodejs.dev/learn/the-v8-javascript-engine</a></p>
<p><a href="https://evan-moon.github.io/2019/06/28/v8-analysis/">https://evan-moon.github.io/2019/06/28/v8-analysis/</a></p>
<p><a href="https://v8.dev/">https://v8.dev/</a></p>
<p><a href="https://github.com/v8/v8">https://github.com/v8/v8</a></p>
<p><a href="https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e">https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ORM과 TypeORM]]></title>
            <link>https://velog.io/@dev_sprinkler/ORM%EA%B3%BC-TypeORM</link>
            <guid>https://velog.io/@dev_sprinkler/ORM%EA%B3%BC-TypeORM</guid>
            <pubDate>Thu, 06 Jan 2022 05:35:15 GMT</pubDate>
            <description><![CDATA[<h1 id="orm은-무엇인가">ORM은 무엇인가?</h1>
<p>ORM(Object-Relation Mapping)은 이름 그대로 객체와 관계형 데이터베이스를 연결해주는 기술으로, 쿼리를 객체지향 언어처럼 작성할 수 있게 해준다. Java의 Hibernate, Node.js의 Sequelize 등이 ORM 프레임워크이다.</p>
<pre><code class="language-jsx">var orm = require(&#39;generic-orm-library&#39;);
var user = orm(&quot;users&quot;).where({ email: &#39;test@test.com&#39; });</code></pre>
<p>가상의 ORM 라이브러리로 쿼리를 실행하는 예시.</p>
<p>자바스크립트뿐 아니라 다른 여러 언어에서 위와 같이 쿼리를 SQL이 아닌 익숙한 언어로 작성하여 사용할 수 있다. </p>
<h1 id="orm-사용의-장점">ORM 사용의 장점</h1>
<ol>
<li>SQL 사용에 익숙하지 않더라도 쉽게 접근할 수 있다.</li>
<li>특정 DBMS에 종속적이지 않아 데이터베이스 마이그레이션이 쉽다.</li>
<li>트랜잭션, 커넥션풀 관리 등 여러 기능을 간편하게 사용할 수 있어 생산성이 높아진다.</li>
</ol>
<h1 id="orm-사용의-단점">ORM 사용의 단점</h1>
<ol>
<li>SQL을 직접 사용하는 것에 비해 성능이 떨어진다.</li>
<li>ORM 자체의 사용법을 배우는데 러닝커브가 있다.</li>
</ol>
<h1 id="typeorm">TypeORM</h1>
<p>타입스크립트와 자바스크립트 ES5~ES8에서 사용가능한 ORM 프레임워크로 Active Record와 Data Mapper 패턴을 모두 지원한다.</p>
<h3 id="active-record">Active Record</h3>
<p>모델 자체에 쿼리 메소드를 정의해서 모델의 메소드를 사용하여 객체를 조작하는 패턴.</p>
<pre><code class="language-tsx">import {BaseEntity, Entity, PrimaryGeneratedColumn, Column} from &quot;typeorm&quot;;

@Entity()
export class User extends BaseEntity {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    isActive: boolean;

    static findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder(&quot;user&quot;)
            .where(&quot;user.firstName = :firstName&quot;, { firstName })
            .andWhere(&quot;user.lastName = :lastName&quot;, { lastName })
            .getMany();
    }

}</code></pre>
<p>모델을 정의한 클래스에 스태틱 메소드형태로 쿼리를 작성해 객체를 조작한다.</p>
<h3 id="data-mapper">Data Mapper</h3>
<p>분리된 클래스에 쿼리 메소드를 정의하고 Repository를 이용하여 객체를 조작하는 패턴.</p>
<pre><code class="language-tsx">import {EntityRepository, Repository} from &quot;typeorm&quot;;
import {User} from &quot;../entity/User&quot;;

@EntityRepository()
export class UserRepository extends Repository&lt;User&gt; {

    findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder(&quot;user&quot;)
            .where(&quot;user.firstName = :firstName&quot;, { firstName })
            .andWhere(&quot;user.lastName = :lastName&quot;, { lastName })
            .getMany();
    }

}</code></pre>
<p>모델을 정의한 클래스와 분리하여 리포지토리 클래스를 만들어 리포지토리에서 객체를 조작한다.</p>
<p>공식 문서에 따르면 Data Mapper 패턴은 유지보수성이 좋고 대규모 앱에서 효과적으로 사용할 수 있다고 하며, Active Record 패턴은 구현이 간단하여 소규모 앱에서 사용하기 좋다고 한다.</p>
<h2 id="참고">참고</h2>
<p><a href="https://dev.solita.fi/2021/06/01/why-avoid-an-orm.html">https://dev.solita.fi/2021/06/01/why-avoid-an-orm.html</a></p>
<p><a href="https://axiomq.com/blog/comparing-orm-vs-sql-what-to-know/">https://axiomq.com/blog/comparing-orm-vs-sql-what-to-know/</a></p>
<p><a href="https://blog.bitsrc.io/what-is-an-orm-and-why-you-should-use-it-b2b6f75f5e2a">https://blog.bitsrc.io/what-is-an-orm-and-why-you-should-use-it-b2b6f75f5e2a</a></p>
<p><a href="https://www.freecodecamp.org/news/a-comparison-of-the-top-orms-for-2018-19c4feeaa5f/">https://www.freecodecamp.org/news/a-comparison-of-the-top-orms-for-2018-19c4feeaa5f/</a></p>
<p><a href="https://typeorm.io/#/">https://typeorm.io/#/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Express와 http의 차이점]]></title>
            <link>https://velog.io/@dev_sprinkler/Express%EC%99%80-http%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@dev_sprinkler/Express%EC%99%80-http%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 04 Jan 2022 07:22:30 GMT</pubDate>
            <description><![CDATA[<h1 id="http">http</h1>
<ul>
<li>node.js의 내장 모듈</li>
<li>서버 생성과 커넥션 설정에 사용</li>
<li>http를 통한 데이터 전송을 할 수 있음</li>
</ul>
<h1 id="express">Express</h1>
<ul>
<li>node.js의 웹 애플리케이션 프레임워크 | <a href="https://expressjs.com/">공식 사이트 링크</a></li>
<li>웹 서버 제작에 유용한 API, 서브모듈, 함수, 방법론, 컨벤션 등 제공
(e.g. 정적 호스팅, 템플릿, CSRF/CORS 설정, POST 데이터 핸들링, 미들웨어 등)</li>
</ul>
<table>
<thead>
<tr>
<th>http</th>
<th>Express</th>
</tr>
</thead>
<tbody><tr>
<td>node.js 내장 모듈</td>
<td>npm으로 설치 가능한 외부 모듈</td>
</tr>
<tr>
<td>모듈</td>
<td>프레임워크</td>
</tr>
<tr>
<td>정적 호스팅 지원하지 않음</td>
<td>정적 호스팅 지원함 (ex. app.use(express.ststic(’public’));</td>
</tr>
<tr>
<td>독립 모듈</td>
<td>http 모듈 기반으로 만들어짐</td>
</tr>
<tr>
<td>네트워킹을 위한 여러 툴 제공</td>
<td>http 모듈 이상으로 많은 기능과 편의성 제공</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 콜백 (JavaScript Callback)]]></title>
            <link>https://velog.io/@dev_sprinkler/JavaScript-Callback</link>
            <guid>https://velog.io/@dev_sprinkler/JavaScript-Callback</guid>
            <pubDate>Mon, 27 Dec 2021 05:06:04 GMT</pubDate>
            <description><![CDATA[<pre><code>A callback is a function passed as an argument to another function

This technique allows a function to call another function

A callback function can run after another function has finished</code></pre><p>Callback 함수란 다른 함수의 매개변수로 넘겨준 함수를 의미함</p>
<h3 id="자바스크립트에-콜백함수가-필요한-이유">자바스크립트에 콜백함수가 필요한 이유?</h3>
<p>자바스크립트는 이벤트기반 (event-driven) 언어임</p>
<p>자바스크립트는 다음 명령어를 실행하기 전 이전 명령어의 응답을 기다리기보다 다른 이벤트를 기다리며 계속 명령을 수행함 (non-blocking)</p>
<p>A함수 이후에 B함수를 실행하고 싶은데 A함수의 작업이 즉시 끝나는 작업이 아니라면? (e.g. setTimeout())</p>
<pre><code class="language-javascript">function A() {
    setTimeout( function() {
        console.log(1);
    }, 1000);
}

function B() {
    console.log(2);
}

A();
B();

// output:
// 2
// 1</code></pre>
<p>이런 코드가 있을 때 A를 B보다 먼저 호출하더라도 출력은 B가 먼저 나옴</p>
<p>이는 자바스크립트가 우리가 원하는 순서대로 함수를 실행하지 않은 것이 아니라 A() 함수의 실행 이후 끝을 기다리지 않고 바로 B() 함수를 실행하기 때문</p>
<p>즉 비동기 데이터를 처리하기 위해서 콜백함수가 필요함</p>
<h3 id="콜백함수를-언제-어떻게-사용하는지">콜백함수를 언제 어떻게 사용하는지?</h3>
<p>위에서 살펴봤듯이 함수의 실행순서를 보장받고 싶을 때 콜백을 사용할 수 있음</p>
<p>한마디로 콜백함수는 함수의 실행이 종료될 때까지 다른 함수가 실행되지 않게 기다리는 것</p>
<pre><code class="language-javascript">function A(callback) {
    setTimeout(function() {
        console.log(1);
        callback();
    }, 1000);
}

function B() {
    console.log(2);
}

A(B);

// output:
// 1
// 2</code></pre>
<p>B() 함수를 A() 함수의 매개변수로 넘겨서 A()의 콘솔 출력 이후 callback()을 호출할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js logger Winston / Morgan]]></title>
            <link>https://velog.io/@dev_sprinkler/Node.js-logger-Winston-Morgan</link>
            <guid>https://velog.io/@dev_sprinkler/Node.js-logger-Winston-Morgan</guid>
            <pubDate>Tue, 14 Dec 2021 03:24:42 GMT</pubDate>
            <description><![CDATA[<p>git repo : <a href="https://github.com/winstonjs/winston">winston</a> / <a href="https://github.com/expressjs/morgan">morgan</a></p>
<h3 id="winston">Winston</h3>
<ul>
<li>콘솔, 파일 등 다양한 출력형식을 지원하는 간단한 범용 로깅 라이브러리</li>
<li>아래와 같이 총 7가지 로그 레벨이 정의되어 있음</li>
</ul>
<pre><code class="language-javascript">const levels = {
    error: 0,
    warn: 1,
    info: 2,
    http: 3,
    verbose: 4,
    debug: 5,
    silly: 6
};</code></pre>
<h3 id="사용예">사용예</h3>
<ul>
<li>아래 예시 코드처럼 직접 로거를 만들어 사용하는 것이 추천됨</li>
<li>아래는 에러와 일반 로그를 다른 파일로 분리하고 NODE_ENV 환경변수가 &#39;production&#39;이 아닐 때, 즉 개발/테스트 모드일 때는 콘솔에 출력을 하고 production 환경에서는 파일 출력만 하는 예제 코드</li>
</ul>
<pre><code class="language-javascript">const winston = require(&#39;winston&#39;);

const logger = winston.createLogger({
  level: &#39;info&#39;,
  format: winston.format.json(),
  defaultMeta: { service: &#39;user-service&#39; },
  transports: [
    //
    // - Write all logs with importance level of `error` or less to `error.log`
    // - Write all logs with importance level of `info` or less to `combined.log`
    //
    new winston.transports.File({ filename: &#39;error.log&#39;, level: &#39;error&#39; }),
    new winston.transports.File({ filename: &#39;combined.log&#39; }),
  ],
});

//
// If we&#39;re not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== &#39;production&#39;) {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}</code></pre>
<h3 id="winston-daily-rotate-file">winston-daily-rotate-file</h3>
<ul>
<li>로그 파일을 날짜, 크기제한 등에 따라 분리할 수 있게 하는 모듈</li>
</ul>
<h3 id="사용예-1">사용예</h3>
<pre><code class="language-javascript">const logger = winston.createLogger({
  level: &#39;info&#39;,
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: &#39;combined.log&#39; })
  ]
});

//
// Replaces the previous transports with those in the
// new configuration wholesale.
//
const DailyRotateFile = require(&#39;winston-daily-rotate-file&#39;);
logger.configure({
  level: &#39;verbose&#39;,
  transports: [
    new DailyRotateFile(opts)
  ]
});</code></pre>
<h3 id="같은-타입의-다중-출력예">같은 타입의 다중 출력예</h3>
<ul>
<li>info, error 레벨 로그를 각각 다른 파일로 출력</li>
</ul>
<pre><code class="language-javascript">const logger = winston.createLogger({
  transports: [
    new winston.transports.File({
      filename: &#39;combined.log&#39;,
      level: &#39;info&#39;
    }),
    new winston.transports.File({
      filename: &#39;errors.log&#39;,
      level: &#39;error&#39;
    })
  ]
});</code></pre>
<h3 id="morgan">Morgan</h3>
<ul>
<li>HTTP 리퀘스트 로깅 미들웨어</li>
</ul>
<h3 id="사용예-2">사용예</h3>
<pre><code class="language-javascript">const morgan = require(&#39;morgan&#39;);

// Using a predefiend format string
morgan(&#39;tiny&#39;);

// Using format string of predefined tokens
morgan(&#39;:method :url :status :res[content-length] - :response-time ms&#39;);

// Using a custom format function
morgan(function (tokens, req, res) {
    return [
        tokens.method(req, res),
        tokens.url(req, res),
        tokens.status(req, res),
        tokens.res(req, res, &#39;content-length&#39;), &#39;-&#39;,
        tokens.[&#39;response-time&#39;](req, res), &#39;ms&#39;
    ].join(&#39; &#39;);
});</code></pre>
<h3 id="사전-정의된-포맷">사전 정의된 포맷</h3>
<ul>
<li>combined, common, dev, short, tiny 다섯 가지 포맷이 정의돼 있음</li>
</ul>
<pre><code>combined
:remote-addr - :remote-user [:date[clf]] &quot;:method :url HTTP/:http-version&quot; :status :res[content-length] &quot;:referrer&quot; &quot;:user-agent&quot;

common
:remote-addr - :remote-user [:date[clf]] &quot;:method :url HTTP/:http-version&quot; :status :res[content-length]

dev
:method :url :status :response-time ms - :res[content-length]

short
:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms

tiny
:method :url :status :res[content-length] - :response-time ms</code></pre><h3 id="winston--morgan-함께-사용">winston + morgan 함께 사용</h3>
<ul>
<li>morgan 메소드의 두번째 인자에 winston logger를 넣으면 morgan에서 출력하는 로그가 winston으로 정의한 로거에 출력됨</li>
</ul>
<pre><code class="language-javascript">const morgan = require(&#39;morgan&#39;);
const logger = require(WINSTON_LOGGER_LOCATION);

morgan(&#39;tiny&#39;, { logger });</code></pre>
<h3 id="새로운-토큰-생성">새로운 토큰 생성</h3>
<ul>
<li>morgan.token() 메소드를 호출해 새로운 토큰을 정의할 수 있음</li>
</ul>
<pre><code class="language-javascript">morgan.token(&#39;type&#39;, function (req, res) { return req.headers[&#39;content-type&#39;] });</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[PM2 - Node.js Process manager]]></title>
            <link>https://velog.io/@dev_sprinkler/PM2-Node.js-Process-manager</link>
            <guid>https://velog.io/@dev_sprinkler/PM2-Node.js-Process-manager</guid>
            <pubDate>Mon, 22 Nov 2021 05:11:00 GMT</pubDate>
            <description><![CDATA[<p>pm2 도큐먼트 페이지 : <a href="https://pm2.keymetrics.io/docs/usage/quick-start/">PM2 - Quick Start</a></p>
<h3 id="pm2">pm2</h3>
<hr>
<p>Node.js 프로그램 백그라운드 실행 및 프로세스 관리를 도와주는 패키지.</p>
<h3 id="설치-및-사용법">설치 및 사용법</h3>
<hr>
<pre><code class="language-bash">npm install pm2 --global</code></pre>
<p>pm2 설치 명령어 (npm)</p>
<pre><code class="language-bash">pm2 start app.js</code></pre>
<p>프로세스 시작 명령어</p>
<ul>
<li>스크립트를 직접 지정해서 시작하는 방법 외에도 별도로 콘피그 파일(.js)을 작성해 여러 옵션을 지정할 수 있음</li>
</ul>
<pre><code class="language-bash">pm2 kill [optional : process_name]</code></pre>
<p>프로세스 종료 명령어</p>
<ul>
<li>프로세스 지정하지 않으면 실행 중인 모든 프로세스 종료</li>
</ul>
<pre><code class="language-bash">pm2 log [optional : process_name]</code></pre>
<p>로그 확인 명령어</p>
<ul>
<li>프로세스 지정하지 않으면 모든 프로세스의 로그 출력</li>
</ul>
<h3 id="클러스터-모드">클러스터 모드</h3>
<hr>
<p>pm2의 클러스터 모드를 사용하면, 기본적으로 싱글 스레드에서 실행되는 Node.js 프로세스를 멀티 스레드로 실행할 수 있다.</p>
<p>pm2의 클러스터 모드는 내부적으로 Node.js ‘cluster’ 모듈을 사용해서 구현되어있다.</p>
<p>Cluster 모듈 레퍼런스 : <a href="https://nodejs.org/dist/latest-v16.x/docs/api/cluster.html">Cluster | Node.js v16.13.0 Documentation</a></p>
<pre><code class="language-javascript">// pm2.config.js

module.exports = {
  apps: [
    {
      name: &quot;server.account&quot;,
      script: &quot;./dist/app.js&quot;,
      instances: 0,
      exec_mode: &quot;cluster&quot;,

      ...

    }
  ]
};</code></pre>
<p>위 예시처럼 exec_mode: “cluster”로 설정하면 클러스터 모드로 프로세스를 실행하게 된다.
‘instances’ 옵션을 설정하여 실행할 프로세스의 수를 정할 수 있고 0으로 설정하면 CPU 코어 수 만큼 (하이퍼스레딩을 지원한다면 코어 수의 2배) 프로세스를 실행한다.</p>
<pre><code class="language-bash">pm2 start app.js -i max</code></pre>
<p>혹은 위 명령어로 프로세스를 실행하는 방법도 있다.
-i 옵션 뒤에 실행할 프로세스 수를 설정하여 사용한다.</p>
<pre><code class="language-bash">pm2 scale app [원하는 프로세스 개수]</code></pre>
<p>실행 중인 앱의 프로세스 수를 조절할 때는 scale 명령어를 사용하여 조절할 수 있다.</p>
<p>참고하면 좋은 포스팅</p>
<p><a href="https://engineering.linecorp.com/ko/blog/pm2-nodejs/">PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글 앱스크립트 (Apps Script)]]></title>
            <link>https://velog.io/@dev_sprinkler/%EA%B5%AC%EA%B8%80-%EC%95%B1%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Apps-Script</link>
            <guid>https://velog.io/@dev_sprinkler/%EA%B5%AC%EA%B8%80-%EC%95%B1%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Apps-Script</guid>
            <pubDate>Wed, 10 Nov 2021 05:02:50 GMT</pubDate>
            <description><![CDATA[<h2 id="앱스크립트">앱스크립트</h2>
<hr>
<ul>
<li>자바스크립트 1.6 기반 (ES5 및 1.7, 1.8 기능 일부 포함)</li>
<li>스프레드시트 등 구글 워크스페이스 태스크 자동화에 사용 가능</li>
</ul>
<h2 id="스프레드시트-기준-사용법">스프레드시트 기준 사용법</h2>
<hr>
<ul>
<li>스프레드시트 → 도구 → 스크립트 편집기 클릭하면 앱스크립트 에디터 화면으로 이동함</li>
<li>함수 단위로 실행 / 디버그 가능</li>
<li>작성한 프로젝트를 배포하여 외부에서 호출 가능 (doGet, doPost 등 함수를 만들면 웹앱형태로 배포하여 GET, POST 방식으로 호출 가능함)</li>
</ul>
<h3 id="배포-웹-앱">배포 (웹 앱)</h3>
<ol>
<li>상단 배포 탭에서 새 배포 선택</li>
<li>배포 유형 웹 앱 선택 후 설명을 작성한 뒤 배포</li>
<li>배포 관리 탭에서 엔드포인트 확인 가능</li>
</ol>
<h3 id="통계">통계</h3>
<ol>
<li>좌측 네비게이터에서 개요 탭으로 들어가면 모든 배포에 대한 오류율, 실행 횟수 등을 확인할 수 있음</li>
</ol>
<h3 id="트리거-설정">트리거 설정</h3>
<ol>
<li>좌측 네비게이터에서 트리거 탭으로 들어가면 새 트리거(예약 실행 등)를 추가할 수 있음</li>
<li>트리거 추가를 누르고 실행할 함수, 배포, 트리거의 종류 및 옵션을 선택하고 저장하면 트리거 설정 완료</li>
<li>이후 설정된 트리거를 다음과 같이 확인 가능</li>
</ol>
<h3 id="라이브러리-문서">라이브러리 문서</h3>
<ol>
<li>앱스크립트는 자동으로 각 함수를 문서화하여 제공함</li>
<li>배포 관리에서 확인하고 싶은 배포를 선택하고 라이브러리 주소로 브라우저에서 접근하면 간단한 문서 확인 가능</li>
</ol>
<h3 id="레퍼런스">레퍼런스</h3>
<ul>
<li><a href="https://developers.google.com/apps-script/reference">Reference overview | Apps Script | Google Developers</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Elasticsearch] 중단 시간 없이 매핑 변경]]></title>
            <link>https://velog.io/@dev_sprinkler/Elasticsearch-%EC%A4%91%EB%8B%A8-%EC%8B%9C%EA%B0%84-%EC%97%86%EC%9D%B4-%EB%A7%A4%ED%95%91-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@dev_sprinkler/Elasticsearch-%EC%A4%91%EB%8B%A8-%EC%8B%9C%EA%B0%84-%EC%97%86%EC%9D%B4-%EB%A7%A4%ED%95%91-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Tue, 09 Nov 2021 02:28:00 GMT</pubDate>
            <description><![CDATA[<p>출처 : <a href="https://www.elastic.co/kr/blog/changing-mapping-with-zero-downtime">https://www.elastic.co/kr/blog/changing-mapping-with-zero-downtime</a></p>
<pre><code class="language-JSON"># 새 인덱스를 만들고 데이터 카피
PUT my_index_v1.0.1
{
  &quot;mappings&quot;: {
    ...
  },
  &quot;settings&quot;: {
    ... 
  }
}

POST _reindex
{
  &quot;source&quot;: {
    &quot;index&quot;: &quot;my_index_v1.0.0&quot;
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;my_index_v1.0.1&quot;
  }
}

# aliases 설정
POST _aliases
{
  &quot;actions&quot;: [
    {
      &quot;remove&quot;: {
        &quot;alias&quot;: &quot;my_index&quot;,
        &quot;index&quot;: &quot;my_index_v1.0.0&quot;
      }
    },
    {
      &quot;add&quot;: {
        &quot;alias&quot;: &quot;my_index&quot;,
        &quot;index&quot;: &quot;my_index_v1.0.1&quot;
      }
    }
  ]
}

# 기존 인덱스 제거
DELETE my_index_v1.0.0</code></pre>
<ol>
<li><p>elasticsearch는 모든 인덱스를 수정 불가능한 세그먼트에 저장하고 각 세그먼트들은 실행 중에는 업데이트할 수 없음</p>
</li>
<li><p>인덱스 별칭(aliases)을 설정하면 백그라운드에서 데이터를 다시 인덱싱할 수 있음</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Elasticsearch nori 한글 형태소 분석기]]></title>
            <link>https://velog.io/@dev_sprinkler/Elasticsearch-nori-%ED%95%9C%EA%B8%80-%ED%98%95%ED%83%9C%EC%86%8C-%EB%B6%84%EC%84%9D%EA%B8%B0</link>
            <guid>https://velog.io/@dev_sprinkler/Elasticsearch-nori-%ED%95%9C%EA%B8%80-%ED%98%95%ED%83%9C%EC%86%8C-%EB%B6%84%EC%84%9D%EA%B8%B0</guid>
            <pubDate>Mon, 08 Nov 2021 09:14:38 GMT</pubDate>
            <description><![CDATA[<p>Elasticsearch 공식 페이지 : <a href="https://esbook.kimjmin.net/06-text-analysis/6.7-stemming/6.7.2-nori">6.7.2 노리 (nori) 한글 형태소 분석기</a></p>
<h1 id="nori">nori</h1>
<ul>
<li><p>Elasticsearch 6.6 버전부터 공식 지원하는 한글 형태소 분석기</p>
</li>
<li><p>mecab-ko-dic 사전 사용</p>
</li>
</ul>
<h1 id="설치">설치</h1>
<hr>
<pre><code class="language-bash">설치
$ bin/elasticsearch-plugin install analysis-nori

제거
$ bin/elasticsearch-plugin remove analysis-nori</code></pre>
<h1 id="인덱스-정의-예시">인덱스 정의 예시</h1>
<hr>
<pre><code class="language-JSON">PUT nori_index
{
  &quot;settings&quot;: {
    &quot;analysis&quot;: {
      &quot;analyzer&quot;: {
        &quot;nori_discard&quot;: {
          &quot;type&quot;: &quot;custom&quot;,
          &quot;tokenizer&quot;: &quot;nori_t_discard&quot;,
          &quot;filter&quot;: [&quot;synonym&quot;, &quot;my_shingle&quot;]
        }
      },
      &quot;tokenizer&quot;: {
        &quot;nori_t_discard&quot;: {
          &quot;type&quot;: &quot;nori_tokenizer&quot;,
          &quot;decompound_mode&quot;: &quot;discard&quot;,
          &quot;user_dictionary&quot;: &quot;analysis/userdic.txt&quot;
        }
      },
      &quot;filter&quot;: {
        &quot;synonym&quot;: {
          &quot;type&quot;: &quot;synonym_graph&quot;,
          &quot;synonyms_path&quot;: &quot;analysis/synonyms.txt&quot;
        },
        &quot;my_shingle&quot;: {
          &quot;type&quot;: &quot;shingle&quot;,
          &quot;token_separator&quot;: &quot;&quot;,
          &quot;max_shingle_size&quot;: 3
        }
      }
    }
  },
  &quot;mappings&quot;: {
    &quot;properties&quot;: {
      &quot;title&quot;: {
        &quot;type&quot;: &quot;text&quot;,
        &quot;fields&quot;: {
          &quot;nori&quot;: {
            &quot;type&quot;: &quot;text&quot;,
            &quot;analyzer&quot;: &quot;nori_discard&quot;
          }
        }
      }
    }
  }
}</code></pre>
<h1 id="rooms-도큐먼트-검색-api">rooms 도큐먼트 검색 api</h1>
<hr>
<pre><code>request example
GET nori_index/_search
{
  &quot;query&quot;: {
    &quot;match&quot;: {
      &quot;title.nori&quot;: &quot;동해&quot;
    }
  }
}

response example
{
  &quot;took&quot; : 0,
  &quot;timed_out&quot; : false,
  &quot;_shards&quot; : {
    &quot;total&quot; : 1,
    &quot;successful&quot; : 1,
    &quot;skipped&quot; : 0,
    &quot;failed&quot; : 0
  },
  &quot;hits&quot; : {
    &quot;total&quot; : {
      &quot;value&quot; : 1,
      &quot;relation&quot; : &quot;eq&quot;
    },
    &quot;max_score&quot; : 2.3166962,
    &quot;hits&quot; : [
      {
        &quot;_index&quot; : &quot;nori_index&quot;,
        &quot;_type&quot; : &quot;_doc&quot;,
        &quot;_id&quot; : &quot;iVF7_nwB93HRpra7cb9E&quot;,
        &quot;_score&quot; : 2.3166962,
        &quot;_source&quot; : {
          &quot;title&quot; : &quot;동해물과 백두산이&quot;
        }
      }
    ]
  }
}</code></pre><h1 id="nori_tokenizer">nori_tokenizer</h1>
<hr>
<ul>
<li><p>사전 정보를 이용해 형태소를 분리</p>
</li>
<li><p>user_dictionary 옵션</p>
</li>
</ul>
<blockquote>
<p>사용자 사전이 저장된 파일 경로 입력
사전 내용 변경시 _close / _open 하여 인덱스에 변경사항 반영</p>
</blockquote>
<ul>
<li>user_dictionary_rules</li>
</ul>
<blockquote>
<p>사용자 정의 사전을 배열 형태로 입력</p>
</blockquote>
<ul>
<li>decompound_mode</li>
</ul>
<blockquote>
<p>합성어 저장 방식 결정
none : 완성된 합성어만 저장
discard (default) : 합성어를 분리하여 어근만 저장
mixed : 어근, 합성어 모두 저장</p>
</blockquote>
<ul>
<li>nori_part_of_speech</li>
</ul>
<blockquote>
<p>제거할 품사를 지정하는 토큰 필터
옵션 stoptags 배열에 제외할 품사코드 나열해서 사용</p>
</blockquote>
<ul>
<li>품사코드
<img src="https://images.velog.io/images/dev_sprinkler/post/8f58d85f-b35d-494a-99fb-76d5ad17e3cf/image.png" alt="품사코드 테이블"></li>
</ul>
<h1 id="nori_readingform">nori_readingform</h1>
<hr>
<ul>
<li>한자 단어를 한글로 바꾸어 저장하는 토큰 필터</li>
</ul>
<h1 id="termvectors-api">termvectors api</h1>
<hr>
<ul>
<li>색인된 도큐먼트의 역인덱스를 확인할 때 사용하는 api</li>
</ul>
<pre><code>GET {index_name}/_termvectors/{document_id}?fields={fieldname}</code></pre><pre><code>request example
GET nori_index/_termvectors/jVHQ_nwB93HRpra7_L_M?fields=title.nori

response example
{
  &quot;_index&quot; : &quot;nori_index&quot;,
  &quot;_type&quot; : &quot;_doc&quot;,
  &quot;_id&quot; : &quot;jVHQ_nwB93HRpra7_L_M&quot;,
  &quot;_version&quot; : 1,
  &quot;found&quot; : true,
  &quot;took&quot; : 4,
  &quot;term_vectors&quot; : {
    &quot;title.nori&quot; : {
      &quot;field_statistics&quot; : {
        &quot;sum_doc_freq&quot; : 102,
        &quot;doc_count&quot; : 8,
        &quot;sum_ttf&quot; : 102
      },
      &quot;terms&quot; : {
        &quot;과&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 3,
              &quot;end_offset&quot; : 4
            }
          ]
        },
        &quot;과백두&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 3,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;과백두산&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 3,
              &quot;end_offset&quot; : 8
            }
          ]
        },
        &quot;동해&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 2
            }
          ]
        },
        &quot;동해물&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 3
            }
          ]
        },
        &quot;동해물과&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 4
            }
          ]
        },
        &quot;물&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 2,
              &quot;end_offset&quot; : 3
            }
          ]
        },
        &quot;물과&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 2,
              &quot;end_offset&quot; : 4
            }
          ]
        },
        &quot;물과백두&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 2,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;백두&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 5,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;백두산&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 5,
              &quot;end_offset&quot; : 8
            }
          ]
        },
        &quot;백두산이&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 5,
              &quot;end_offset&quot; : 9
            }
          ]
        },
        &quot;산&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 7,
              &quot;end_offset&quot; : 8
            }
          ]
        },
        &quot;산이&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 7,
              &quot;end_offset&quot; : 9
            }
          ]
        },
        &quot;이&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 5,
              &quot;start_offset&quot; : 8,
              &quot;end_offset&quot; : 9
            }
          ]
        }
      }
    }
  }
}</code></pre><h1 id="analyze-api">analyze api</h1>
<hr>
<ul>
<li>형태소 분석 결과를 확인할 때 사용하는 api</li>
</ul>
<pre><code>request example
GET nori_index/_analyze
{
  &quot;text&quot;: &quot;동해물과 백두산이&quot;,
  &quot;analyzer&quot;: &quot;nori&quot;,
  &quot;explain&quot;: true
}

response example
{
  &quot;detail&quot; : {
    &quot;custom_analyzer&quot; : false,
    &quot;analyzer&quot; : {
      &quot;name&quot; : &quot;nori&quot;,
      &quot;tokens&quot; : [
        {
          &quot;token&quot; : &quot;동해&quot;,
          &quot;start_offset&quot; : 0,
          &quot;end_offset&quot; : 2,
          &quot;type&quot; : &quot;word&quot;,
          &quot;position&quot; : 0,
          &quot;bytes&quot; : &quot;[eb 8f 99 ed 95 b4]&quot;,
          &quot;leftPOS&quot; : &quot;NNP(Proper Noun)&quot;,
          &quot;morphemes&quot; : null,
          &quot;posType&quot; : &quot;MORPHEME&quot;,
          &quot;positionLength&quot; : 1,
          &quot;reading&quot; : null,
          &quot;rightPOS&quot; : &quot;NNP(Proper Noun)&quot;,
          &quot;termFrequency&quot; : 1
        },
        {
          &quot;token&quot; : &quot;물&quot;,
          &quot;start_offset&quot; : 2,
          &quot;end_offset&quot; : 3,
          &quot;type&quot; : &quot;word&quot;,
          &quot;position&quot; : 1,
          &quot;bytes&quot; : &quot;[eb ac bc]&quot;,
          &quot;leftPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;morphemes&quot; : null,
          &quot;posType&quot; : &quot;MORPHEME&quot;,
          &quot;positionLength&quot; : 1,
          &quot;reading&quot; : null,
          &quot;rightPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;termFrequency&quot; : 1
        },
        {
          &quot;token&quot; : &quot;백두&quot;,
          &quot;start_offset&quot; : 5,
          &quot;end_offset&quot; : 7,
          &quot;type&quot; : &quot;word&quot;,
          &quot;position&quot; : 3,
          &quot;bytes&quot; : &quot;[eb b0 b1 eb 91 90]&quot;,
          &quot;leftPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;morphemes&quot; : null,
          &quot;posType&quot; : &quot;MORPHEME&quot;,
          &quot;positionLength&quot; : 1,
          &quot;reading&quot; : null,
          &quot;rightPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;termFrequency&quot; : 1
        },
        {
          &quot;token&quot; : &quot;산&quot;,
          &quot;start_offset&quot; : 7,
          &quot;end_offset&quot; : 8,
          &quot;type&quot; : &quot;word&quot;,
          &quot;position&quot; : 4,
          &quot;bytes&quot; : &quot;[ec 82 b0]&quot;,
          &quot;leftPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;morphemes&quot; : null,
          &quot;posType&quot; : &quot;MORPHEME&quot;,
          &quot;positionLength&quot; : 1,
          &quot;reading&quot; : null,
          &quot;rightPOS&quot; : &quot;NNG(General Noun)&quot;,
          &quot;termFrequency&quot; : 1
        }
      ]
    }
  }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[mac homebrew로 설치한 mysql에 접속이 안 될 때]]></title>
            <link>https://velog.io/@dev_sprinkler/mac-homebrew%EB%A1%9C-%EC%84%A4%EC%B9%98%ED%95%9C-mysql%EC%97%90-%EC%A0%91%EC%86%8D%EC%9D%B4-%EC%95%88-%EB%90%A0-%EB%95%8C</link>
            <guid>https://velog.io/@dev_sprinkler/mac-homebrew%EB%A1%9C-%EC%84%A4%EC%B9%98%ED%95%9C-mysql%EC%97%90-%EC%A0%91%EC%86%8D%EC%9D%B4-%EC%95%88-%EB%90%A0-%EB%95%8C</guid>
            <pubDate>Mon, 25 Oct 2021 02:14:43 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-bash">Bootstrap failed: 5: Input/output error
Error: Failure while executing; `/bin/launchctl bootstrap gui/501 ~/Library/LaunchAgents/homebrew.mxcl.mysql@5.7.plist` exited with 5.</code></pre>
<pre><code class="language-bash">ERROR 2002 (HY000): Can&#39;t connect to local MySQL server through socket &#39;/tmp/mysql.sock&#39; (2)</code></pre>
<p>위 에러가 발생하며 homebrew로 설치한 mysql에 접속이 안 될 때,</p>
<pre><code class="language-bash">mysql --verbose --help | grep my.cnf</code></pre>
<p>my.cnf 파일 위치를 찾아서 아래와 같이 수정해보자</p>
<pre><code># Default Homebrew MySQL server config
[mysqld]
# Only allow connections from localhost
bind-address = 0.0.0.0</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Node + Typescript 개발환경에 Path alias 적용]]></title>
            <link>https://velog.io/@dev_sprinkler/Node-Typescript-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD%EC%97%90-Path-alias-%EC%A0%81%EC%9A%A9</link>
            <guid>https://velog.io/@dev_sprinkler/Node-Typescript-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD%EC%97%90-Path-alias-%EC%A0%81%EC%9A%A9</guid>
            <pubDate>Tue, 12 Oct 2021 00:54:33 GMT</pubDate>
            <description><![CDATA[<p>package.json에 아래 구문 추가</p>
<pre><code class="language-json">&quot;_moduleAliases&quot;: {
  &quot;@src&quot;: &quot;src&quot; // 경로 및 이름은 알맞게 수정
}</code></pre>
<p>tsconfig.json에 아래 구문 추가</p>
<pre><code class="language-json">&quot;baseUrl&quot;: &quot;./&quot;,
&quot;paths&quot;: {
  &quot;@src/*&quot;: [ &quot;src/*&quot; ] // 경로 및 이름은 알맞게 수정
}</code></pre>
<p>npm 또는 yarn으로 아래 패키지 설치</p>
<pre><code class="language-bash">npm i module-alias --save-dev
yarn add module-alias --save-dev</code></pre>
<p>이후 상대경로 (../../src/file.ts) 사용하는 대신 alias (@src/file.ts) 로 import 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ubuntu 20.04 Docker 설치]]></title>
            <link>https://velog.io/@dev_sprinkler/Ubuntu-20.04-Docker-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@dev_sprinkler/Ubuntu-20.04-Docker-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Tue, 12 Oct 2021 00:31:46 GMT</pubDate>
            <description><![CDATA[<p>아래 명령어 차례로 입력</p>
<pre><code class="language-bash">sudo apt update
sudo apt upgrade -y
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;
sudo apt install docker-ce</code></pre>
<p>도커 설치 이후 실행 확인</p>
<pre><code class="language-bash">sudo service docker status</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[helmet.js 모듈]]></title>
            <link>https://velog.io/@dev_sprinkler/helmet.js-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@dev_sprinkler/helmet.js-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Wed, 29 Sep 2021 03:47:54 GMT</pubDate>
            <description><![CDATA[<h1 id="helmetjs">helmet.js?</h1>
<blockquote>
<p>HTTP 헤더 설정을 자동으로 바꾸어 몇가지 잘 알려진 취약점으로 부터 앱을 보호할 수 있는 패키지</p>
</blockquote>
<h2 id="설치-방법">설치 방법</h2>
<pre><code class="language-bash">$ yarn add helmet @types/helmet
또는
$ npm install helmet @types/helmet</code></pre>
<h2 id="사용-예시">사용 예시</h2>
<pre><code class="language-bash">import helmet from &#39;helmet&#39;;

// 기본 11개 미들웨어 사용
app.use(helmet());

// helmet.frameguard를 제외한 기본 10개 미들웨어 사용
app.use(
  helmet({
    frameguard: false,
  })
);</code></pre>
<h2 id="기본으로-사용하는-미들웨어">기본으로 사용하는 미들웨어</h2>
<ul>
<li>crossOriginEmbedderPolicy, crossOriginOpenerPolicy, crossOriginResourcePolicy, originAgentCluster 네가지를 제외한 11개 미들웨어를 기본으로 사용함</li>
</ul>
<h2 id="사용가능한-미들웨어">사용가능한 미들웨어</h2>
<p>helmet.contentSecurityPolicy(options) : XSS(Cross site scripting) 공격 및 기타 교차 사이트 인젝션 예방</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Content Security Policy (CSP) - HTTP | MDN</a></li>
</ul>
<p>helmet.crossOriginEmbedderPolicy() : Cross-Origin-Embedder-Policy 헤더를 require-corp로 설정</p>
<ul>
<li><a href="https://developer.cdn.mozilla.net/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">Cross-Origin-Embedder-Policy - HTTP | MDN</a></li>
</ul>
<p>helmet.crossOriginOpenerPolicy() : Cross-Origin-Opener-Policy 헤더를 설정</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">Cross-Origin-Opener-Policy - HTTP | MDN</a></li>
</ul>
<p>helmet.crossOriginResourcePolicy() : Cross-Origin-Resource-Policy 헤더를 설정</p>
<ul>
<li><a href="https://resourcepolicy.fyi/">Consider deploying Cross-Origin Resource Policy.</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">Cross-Origin-Resource-Policy - HTTP | MDN</a></li>
</ul>
<p>helmet.expectCt(options) : Expect-CT 헤더를 설정하여 SSL 인증서 오발급을 예방</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Security/Certificate_Transparency">Certificate Transparency - Web security | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT">Expect-CT - HTTP | MDN</a></li>
</ul>
<p>helmet.referrerPolicy(options) : Referrer-Policy 헤더를 설정하여 민감한 정보 유출 예방</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer">Referer - HTTP | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns">Referer header: privacy and security concerns - Web security | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy">Referrer-Policy - HTTP | MDN</a></li>
</ul>
<p>helmet.hsts(options) : Strict-Transport-Security 헤더를 설정하여 보안연결(HTTPS) 강제</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security">Strict-Transport-Security - HTTP | MDN</a></li>
</ul>
<p>helmet.noSniff() : X-Content-Type-Options 헤더를 nosniff로 설정하여 MIME 스니핑 예방</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing">MIME types (IANA media types) - HTTP | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options">X-Content-Type-Options - HTTP | MDN</a></li>
</ul>
<p>helmet.originAgentCluster() : Origin-Agent-Cluster 헤더를 설정하여 오리진간 문서를 별도 에이전트 클러스터로 분리</p>
<ul>
<li><a href="https://whatpr.org/html/6214/origin.html#origin-keyed-agent-clusters">HTML Standard</a></li>
</ul>
<p>helmet.dnsPrefetchControl(options) : X-DNS-Prefetch-Control 헤더를 설정하여 DNS 프리패칭을 조절</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control">X-DNS-Prefetch-Control - HTTP | MDN</a></li>
</ul>
<p>helmet.ieNoOpen() : X-Download-Options 헤더를 설정하여 ie8 이상에서만 사용할 수 있도록 함</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/archive/blogs/ie/ie8-security-part-v-comprehensive-protection">IE8 Security Part V: Comprehensive Protection</a></li>
</ul>
<p>helmet.frameguard(options) : X-Frame-Options 헤더를 설정하여 clickjacking 공격을 예방</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Clickjacking">Clickjacking</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors">CSP: frame-ancestors - HTTP | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options">X-Frame-Options - HTTP | MDN</a></li>
</ul>
<p>helmet.permittedCrossDomainPolicies(options) : X-Permitted-Cross-Domain-Policies 헤더를 설정하여 크로스도메인 컨텐츠 정책을 설정</p>
<ul>
<li><a href="https://owasp.org/www-project-secure-headers/">OWASP Secure Headers Project</a></li>
</ul>
<p>helmet.hidePoweredBy() : X-Powered-By 헤더를 제거하여 웹앱의 프레임워크를 특정할 수 없도록 함</p>
<ul>
<li><a href="https://github.com/expressjs/express/pull/2813#issuecomment-159270428">Security improvement: don&#39;t reveal powered-by by madarche · Pull Request #2813 · expressjs/express</a></li>
</ul>
<p>helmet.xssFilter() : X-XSS-Protection 헤더를 0으로 설정하여 크로스사이트 스크립트를 이용한 공격을 예방</p>
<ul>
<li><a href="https://github.com/helmetjs/helmet/issues/230">X-XSS-Protection: header should be disabled by default · Issue #230 · helmetjs/helmet</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection">X-XSS-Protection - HTTP | MDN</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[REST API 부하테스트 툴 - K6]]></title>
            <link>https://velog.io/@dev_sprinkler/REST-API-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%88%B4-K6</link>
            <guid>https://velog.io/@dev_sprinkler/REST-API-%EB%B6%80%ED%95%98%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%88%B4-K6</guid>
            <pubDate>Thu, 23 Sep 2021 07:09:45 GMT</pubDate>
            <description><![CDATA[<p>공식 홈페이지: <a href="https://k6.io/">https://k6.io/</a></p>
<h2 id="설치-macos">설치 (MacOS)</h2>
<pre><code class="language-bash">$ brew install k6</code></pre>
<h2 id="사용법">사용법</h2>
<ul>
<li>스크립트 작성</li>
</ul>
<pre><code class="language-javascript">// script.js
import http from &#39;k6/http&#39;;
import { sleep } from &#39;k6&#39;;

export default function() {
  http.get(URI HERE); // get request to input URI
  sleep(1); // sleep while a second
}</code></pre>
<ul>
<li>쉘 명령어 입력</li>
</ul>
<pre><code class="language-bash">$ k6 run --vus 10 --duration 30s --out json=out.json script.js</code></pre>
<p>vus: 동시 작업자 수</p>
<p>duration: 부하 테스트 시간</p>
<p>out: 출력 포맷 및 파일명</p>
<p>script.js: 실행할 스크립트</p>
<ul>
<li>POST 요청 등을 할 때 request body를 사용하기 위해서 json body를 문자열으로 변환하고 헤더를 추가해야함</li>
</ul>
<pre><code class="language-javascript">import http from &#39;k6/http&#39;;
import { sleep, check } from &#39;k6&#39;;
import { uuidv4 } from &quot;https://jslib.k6.io/k6-utils/1.1.0/index.js&quot;;


export default function () {
  const body = { deviceId: `${uuidv4()}` };
  const params = { headers: {&#39;Content-Type&#39;: &#39;application/json&#39;} };
  const res = http.post(&#39;https://api.avatarstudio.me/app/v1/session/guest&#39;, JSON.stringify(body), params);

  check(res, {
    &#39;response code was 200&#39;: (res) =&gt; JSON.parse(res.body).errorCode === 200,
  });

  sleep(0);
}</code></pre>
<h2 id="실제-테스트-화면-캡처">실제 테스트 화면 캡처</h2>
<p><img src="https://images.velog.io/images/dev_sprinkler/post/69f9d26f-98f6-4f8c-81c6-23dca0badcc7/1f2e372b-8315-4080-979b-ce77599993a9.png" alt=""></p>
<ul>
<li>동시 작업자 100개로 5초간 부하테스트한 결과 화면</li>
</ul>
<h2 id="check-함수">check 함수</h2>
<p>api 레퍼런스: <a href="https://k6.io/docs/javascript-api/k6/check-val-sets-tags/">https://k6.io/docs/javascript-api/k6/check-val-sets-tags/</a></p>
<p>요청의 결과값이 적절한지 확인하는 함수</p>
<pre><code class="language-javascript">import http from &#39;k6/http&#39;;
import { check } from &#39;k6&#39;;

export default function () {
  let res = http.get(&#39;http://httpbin.org&#39;);
  check(res, {
    &#39;response code was 200&#39;: (res) =&gt; res.status == 200,
    &#39;body size was 1234 bytes&#39;: (res) =&gt; res.body.length == 1234,
  });
}</code></pre>
<p>위 코드와 같이 작성하여 결과값이 적절한지 검사 (response body가 JSON 오브젝트가 아니라 문자열 형태로 올 수 있으므로 그런 경우에는 JSON.parse() 함수를 사용하여 파싱해야 결과 비교를 제대로 할 수 있음)</p>
<h2 id="uuidv4-함수">uuidv4 함수</h2>
<p>api 레퍼런스: <a href="https://k6.io/docs/javascript-api/jslib/utils/uuidv4/">https://k6.io/docs/javascript-api/jslib/utils/uuidv4/</a></p>
<p>무작위 uuid를 생성하는 함수</p>
<pre><code class="language-javascript">import { uuidv4 } from &quot;https://jslib.k6.io/k6-utils/1.1.0/index.js&quot;;

export default function() {
  let randomUUID = uuidv4();
  console.log(randomUUID); // 35acae14-f7cb-468a-9866-1fc45713149a
}</code></pre>
<p>k6 스크립트를 작성할 때는 미리 정의된 함수만 사용할 수 있음</p>
<p>API 호출에 uuid 값 등이 필요한 경우 위와 같이 미리 정의된 함수를 사용하여 필요한 값을 얻을 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Go ]]></title>
            <link>https://velog.io/@dev_sprinkler/Go%EC%9D%98%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@dev_sprinkler/Go%EC%9D%98%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Wed, 11 Aug 2021 06:38:25 GMT</pubDate>
            <description><![CDATA[<h2 id="go의-특징">Go의 특징</h2>
<h3 id="간결한-문법">간결한 문법</h3>
<p>Class를 배제하여 복잡한 객체지향적 개념을 줄였으며 루프문은 for문 하나뿐이다.
단 25개 키워드만 사용하고 파이썬처럼 슬라이스 타입도 사용할 수 있다.</p>
<pre><code class="language-go">break, default, func, interface, select, case, defer, go, map,
struct, chan, else, goto, package, switch, const, fallthrough,
if, range, type, continue, for, import, return, var</code></pre>
<h3 id="손쉬운-패키지-관리">손쉬운 패키지 관리</h3>
<p>자바의 Maven, 자바스크립트의 NPM, 그 외 여러 언어와는 달리 패키지를 레지스트리에 등록하지 않고 깃에 코드를 올리기만 해도 프로그램에 포함하여 사용할 수 있다.</p>
<p>별도의 패키지 매니저를 사용하지 않고 go get 명령어만으로 패키지를 다운로드 받을 수 있다.</p>
<pre><code class="language-bash">$ go get github.com/golang/protobuf</code></pre>
<h3 id="고루틴코루틴을-사용한-동시성-제어">고루틴(코루틴)을 사용한 동시성 제어</h3>
<p>고루틴은 OS 스레드보다 가벼운 경량 스레드다. 함수를 호출할 때 go 키워드를 붙이기만 하면 해당 함수를 고루틴으로 동작시킬 수 있다.
고루틴은 OS 스레드와 N:M 매핑되어 OS에서 조율하여 병렬실행을 처리해준다.</p>
<h3 id="가비지-컬렉션과-제네릭-타입">가비지 컬렉션과 제네릭 타입</h3>
<p>가비지 컬렉션 지원으로 메모리 관리 편의성 추구
제네릭 타입은 추후 지원 예정</p>
<h3 id="정적-언어">정적 언어</h3>
<p>컴파일 타임에 타입이 결정되는 정적 언어다. 그러나 동적 언어처럼 타입을 직접 명시하지 않아도 된다. (자동추론)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[iptables 사용하기]]></title>
            <link>https://velog.io/@dev_sprinkler/iptables-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev_sprinkler/iptables-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Aug 2021 01:10:05 GMT</pubDate>
            <description><![CDATA[<h2 id="iptables">iptables?</h2>
<p>리눅스 커널 방화벽이 제공하는 테이블과 이를 저장하는 체인, 규칙들을 구성할 수 있게 하는 응용 프로그램</p>
<h3 id="테이블">테이블</h3>
<ul>
<li>filter, nat, mangle, raw 네가지가 있으며, default는 filter임</li>
</ul>
<h3 id="체인">체인</h3>
<ul>
<li>PREROUTING : 패킷이 라우팅되기 이전에 거쳐가는 체인</li>
<li>INPUT : 로컬에서 로컬으로 향하는 모든 패킷이 거쳐가는 체인</li>
<li>FORWARD : 라우팅이후 로컬이 아닌 모든 패킷이 거쳐가는 체인</li>
<li>OUTPUT : 외부로 나가는 패킷이 거쳐가는 체인</li>
<li>POSTROUTING : 시스템에서 외부로 보내지기 직전에 거쳐가는 체인</li>
</ul>
<h3 id="액션">액션</h3>
<ul>
<li>A(APPEND) : 정책 추가</li>
<li>I(INSERT) : 정책 삽입</li>
<li>D(DELETE) : 정책 삭제</li>
<li>R(REPLACE) : 정책 교체</li>
<li>F(FLUSH) : 모든 정책 삭제</li>
<li>P(POLICY) : 기본 정책 설정</li>
<li>L(LIST) : 정책 나열</li>
</ul>
<h3 id="매치">매치</h3>
<ul>
<li>s : 출발지 (도메인, IP주소, 넷마스크 값을 이용하여 표기)</li>
<li>d : 목적지</li>
<li>p : 프로토콜 (tcp, udp, icmp... 대소문자 구분하지 않음)</li>
<li>i : 입력 인터페이스</li>
<li>o : 출력 인터페이스</li>
<li>sport : 출발지 포트</li>
<li>dport : 목적지 포트</li>
<li>j : 매치된 패킷을 처리할 규칙 지정</li>
</ul>
<h3 id="타겟">타겟</h3>
<ul>
<li>ACCEPT : 패킷을 허용</li>
<li>DROP : 패킷을 버림</li>
<li>REJECT : 패킷을 버리고 이와 동시에 적절한 응답 패킷을 전송 (icmp-port-unreachable)</li>
<li>LOG : 패킷을 syslog에 기록한다.</li>
<li>SNAT --to (주소) : 소스 IP를 변환 (NAT)</li>
<li>DNAT --to (주소) : 목적지 IP를 변환 (NAT)</li>
<li>RETURN : 호출 체인 내에서 패킷 처리를 계속함</li>
<li>REDIRECT --to (주소) / --to-port (포트번호) : 리디렉션</li>
</ul>
<blockquote>
<p>80 포트로 들어오는 연결을 8080포트로 리디렉션</p>
</blockquote>
<pre><code>sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080</code></pre>]]></description>
        </item>
    </channel>
</rss>