<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>주니어 개발자 학습 일기</title>
        <link>https://velog.io/</link>
        <description>학습 velog</description>
        <lastBuildDate>Mon, 17 Jun 2024 15:27:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>주니어 개발자 학습 일기</title>
            <url>https://velog.velcdn.com/images/paduck-96/profile/c7b63b59-6266-49e6-b9ca-e61ccc21eab1/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 주니어 개발자 학습 일기. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/paduck-96" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[NestJS Database]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Database</link>
            <guid>https://velog.io/@paduck-96/NestJS-Database</guid>
            <pubDate>Mon, 17 Jun 2024 15:27:59 GMT</pubDate>
            <description><![CDATA[<p>적절한 Node.js 드라이버를 로드하여 Nest를 데이터베이스에 연결</p>
<p>일반적인 Node.js 데이터베이스 통합 라이브러리 또는 ORM을 직접 사용</p>
<p>NestJS 에서는 TypeormModule 을 예로 드나,
실 사용 환경과는 맞지 않아 정리하지 않음</p>
<p>테스트는 모의 레포지토리를 만들어서 해결할 수 있음</p>
<p><code>forRootAsync()</code> 메서드로 비동기 호출 가능</p>
<ul>
<li>팩토리 함수</li>
<li>useClass 를 통해 DB Module Factory 인터페이스를 구현해 적용 가능</li>
<li>useExisting 구문을 통해 기생성된 provider 참조 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Configuration]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Configuration</link>
            <guid>https://velog.io/@paduck-96/NestJS-Configuration</guid>
            <pubDate>Sun, 16 Jun 2024 14:56:58 GMT</pubDate>
            <description><![CDATA[<p>환경 변수를 해당 환경에 저장하는 것이 제일 중요</p>
<p>일반적으로는 각 환경에 맞는 .env 파일에 따라 정의</p>
<ul>
<li>.env 파일을 로드하는 <code>ConfigService</code>를 노출하는 <code>ConfigModule</code>을 만드는 것</li>
</ul>
<p>기본적으로는 <code>forRoot()</code> 메서드 활용</p>
<ul>
<li>.env 파일에 대한 경로는 <code>envFilePath</code> 속성 적용</li>
</ul>
<p>전역으로 사용하려면 import 하거나 <code>isGlobal</code> 속성 사용</p>
<p>커스텀 구성 파일을 활용해 복잡한 config 환경 조성 가능</p>
<pre><code class="language-typescript">export default () =&gt; ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST,
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432
  }
});
</code></pre>
<ul>
<li>해당 내용을 <code>load</code> 속성을 사용해서 적용</li>
</ul>
<p>차후에, 환경 번수에 접근하려면 <code>configService</code>를 활용하는데,</p>
<ul>
<li><code>configService.get()</code> 메서드를 사용</li>
<li>typescript 의 인터페이스를 활용해 type 도움을 사용 가능</li>
</ul>
<p><code>registreAs</code>를 통해 그룹화된 config 환경을 제공할 수도 있음
<code>cache</code> 속성을 통해 환경 변수 caching 가능</p>
<p>기능 모듈 내에서 <code>forFeature</code> 메서드를 통해 부분 등록이 가능함</p>
<p><code>Joi</code> 패키지, custom <code>validate()</code> 를 통해 환경 변수에 대한 검증도 가능</p>
<ul>
<li><code>validationSchema</code> 속성</li>
</ul>
<p>custom getter 함수를 만들어 더 효율적인 방식 동작 가능</p>
<blockquote>
<p>.env 파일이 로드되기 전에 process.env에 접근되는 것을 방지하기 위해 <code>ConfigModule.envVariablesLoaded</code> 속성에 사용이 가능하다.</p>
</blockquote>
<p><code>ConditionalModule</code> 을 통해 조건부 환경 구성이 가능</p>
<ul>
<li>ConfigModule이 애플리케이션에 우선 로드</li>
<li>ConfigModule.envVariablesLoaded 사용</li>
</ul>
<p><code>${}</code> 문법을 통해 config 에 변수 적용 가능
또, <code>expandVariables</code> 옵션을 통해 환경 변수 확장 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Test]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Test-2</link>
            <guid>https://velog.io/@paduck-96/NestJS-Test-2</guid>
            <pubDate>Sun, 16 Jun 2024 12:36:53 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-bash">$ npm i --save-dev @nestjs/testing</code></pre>
<p>Jest 가 기본 테스트 프레임워크로 제공됨</p>
<blockquote>
<p>테스트 파일은 테스트 하려는 클래스와 가까운 위치에 두고, .spec, .test 접미사를 가짐</p>
</blockquote>
<p>기본적으로 테스트에 의존성 주입을 적용하지는 않음</p>
<ul>
<li>격리된 테스트 환경</li>
</ul>
<p><code>Test</code> 클래스를 통해 mocking 한 애플리케이션 실행 컨텍스트 제공</p>
<ul>
<li><p>@Module() 데코레이터에 전달하는 모듈 메타데이터 객체를 인수</p>
</li>
<li><p>모듈과 그 의존성을 부트스트랩</p>
<ul>
<li><code>get()</code> 메서드를 사용하여 선언된 정적 인스턴스를 검색</li>
<li><code>resolve()</code> 메서드를 사용하여 범위가 지정된 프로바이더(일시적 또는 요청 범위)를 동적으로 해결</li>
</ul>
</li>
<li><p>실제 provider 가 아닌 custom provider 로 대체 가능</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Platform agnosticism]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Platform-agnosticism-a0tdxhmb</link>
            <guid>https://velog.io/@paduck-96/NestJS-Platform-agnosticism-a0tdxhmb</guid>
            <pubDate>Sun, 16 Jun 2024 12:36:19 GMT</pubDate>
            <description><![CDATA[<p>플랫폼 독립적인 프레임워크</p>
<p><strong>재사용 가능한 논리적 부분을 개발</strong>하여 여러 유형에서 사용</p>
<ul>
<li>Express/Fastify</li>
<li>Http/WebSocket/Micro</li>
<li>etc</li>
</ul>
<blockquote>
<p>한 번 빌드해서 어디서나 사용하게 하는 걸 목표로 함</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Lifecycle events]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Lifecycle-events-2</link>
            <guid>https://velog.io/@paduck-96/NestJS-Lifecycle-events-2</guid>
            <pubDate>Sun, 16 Jun 2024 12:35:53 GMT</pubDate>
            <description><![CDATA[<p><strong>라이프사이클 훅</strong>을 제공</p>
<ul>
<li>중요한 라이프사이클 이벤트에 대한 가시성을 제공</li>
<li>이벤트가 발생할 때 모듈, providers 또는 컨트롤러에서 등록된 코드를 실행</li>
</ul>
<figure><img src="https://docs.nestjs.com/assets/lifecycle-events.png" /></figure>

<p>애플리케이션 bootstrap 및 종료  동안 발생</p>
<ul>
<li>각 라이플사이클 이벤트에서 등록된 라이프사이클 훅 메서드 호출</li>
</ul>
<table>
<thead>
<tr>
<th>라이프사이클 훅 메서드</th>
<th>훅 메서드 호출을 트리거하는 라이프사이클 이벤트</th>
</tr>
</thead>
<tbody><tr>
<td><code>onModuleInit()</code></td>
<td>호스트 모듈의 종속성이 해결된 후 호출됩니다.</td>
</tr>
<tr>
<td><code>onApplicationBootstrap()</code></td>
<td>모든 모듈이 초기화된 후, 연결을 수신하기 전에 호출됩니다.</td>
</tr>
<tr>
<td><code>onModuleDestroy()</code>*</td>
<td>종료 신호(예: <code>SIGTERM</code>)를 받은 후 호출됩니다.</td>
</tr>
<tr>
<td><code>beforeApplicationShutdown()</code>*</td>
<td>모든 <code>onModuleDestroy()</code> 핸들러가 완료된 후(프로미스가 해결되거나 거부됨); 완료된 후(프로미스가 해결되거나 거부됨) 기존 연결이 모두 닫히고(<code>app.close()</code> 호출됨) 호출됩니다.</td>
</tr>
<tr>
<td><code>onApplicationShutdown()</code>*</td>
<td>연결이 닫힌 후(<code>app.close()</code> 해결됨) 호출됩니다.</td>
</tr>
<tr>
<td>- onModuleDestroy, beforeApplicationShutdown 및 onApplicationShutdown</td>
<td></td>
</tr>
</tbody></table>
<ul>
<li>app.close()를 명시적으로 호출</li>
<li>프로세스가 특별한 시스템 신호(예: SIGTERM)를 받을 때만 트리거</li>
<li>애플리케이션 부트스트랩 시 enableShutdownHooks를 올바르게 호출</li>
</ul>
<ul>
<li>이러한 이벤트의 경우, app.close()를 명시적으로 호출하지 않으면 시스템 신호(Such as SIGTERM)와 함께 작동하도록 선택</li>
</ul>
<p>라이프사이클 훅을 사용하기 위해 typescript 의 <code>interface</code> 를 활용
훅에 async/await 적용 가능
대체로 종료 관련 훅은 컨테이너 수명을 관리하기 위해 적용</p>
<ul>
<li>이 경우 시스템 리소스를 사용학 ㅔ됌</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Execution context]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Execution-context-8p1bok4t</link>
            <guid>https://velog.io/@paduck-96/NestJS-Execution-context-8p1bok4t</guid>
            <pubDate>Sun, 16 Jun 2024 12:35:02 GMT</pubDate>
            <description><![CDATA[<p>코드 실행 환경에 대한 다양한 정보 및 유틸리티 함수를 제공</p>
<ol>
<li><p>ArgumentHost
핸들러에 전달되는 인수를 검색하는 메서드 제공</p>
<ul>
<li>host 매개변수로 참조</li>
</ul>
<p>단순히 핸들러의 인수에 대한 추상화를 제공</p>
</li>
<li><p>ExecutionContext
ArgumentsHost를 확장하여 현재 실행 프로세스에 대한 추가 세부 정보를 제공</p>
<ul>
<li>대체로 핸들러 자체와 클래스 자체에 대한 정보 반환</li>
</ul>
<ul>
<li>Refelction
사용자 정의 메타데이터 생성 및 엑세스 가능
여러 레벨에서 메타데이터를 제공할 수 있으므로, 이를 추출하고 병합할 수 있는 내부 함수가 있음</li>
<li>MetaData
내장된 <code>@SetMetadata()</code> 로 메타데이터 접근 가능</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[NextJS Lazy-loading modules]]></title>
            <link>https://velog.io/@paduck-96/NextJS-Lazy-loading-modules</link>
            <guid>https://velog.io/@paduck-96/NextJS-Lazy-loading-modules</guid>
            <pubDate>Sun, 16 Jun 2024 12:33:43 GMT</pubDate>
            <description><![CDATA[<p>서버리스 환경(등에서) cold start 가 중요한 병목 현상의 원인이 될 수 있음</p>
<ul>
<li>특정 서버리스 함수 호출에 필요한 모듈만 로드하여 부트스트랩 시간을 단축</li>
</ul>
<p>lazyModule 임을 명시하여 지연 로딩 처리 가능</p>
<ul>
<li>class 내에서<pre><code class="language-typescript">import { Injectable, LazyModuleLoader } from &#39;@nestjs/common&#39;;
</code></pre>
</li>
</ul>
<p>@Injectable()
export class CatsService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}
}</p>
<pre><code>- app 인스턴스에서
```typescript
const lazyModuleLoader = app.get(LazyModuleLoader);</code></pre><p><code>lazymoduleloader</code> 의 <code>load()</code> 를 통해 얻은 moduleRef 클래스로 접근 가능</p>
<pre><code class="language-typescript">const { LazyModule } = await import(&#39;./lazy.module&#39;);
const moduleRef = await this.lazyModuleLoader.load(() =&gt; LazyModule);

const { LazyService } = await import(&#39;./lazy.service&#39;);
const lazyService = moduleRef.get(LazyService);</code></pre>
<p>Controller 는 지연 로딩 대상이 될 수 없음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Module Reference]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Test</link>
            <guid>https://velog.io/@paduck-96/NestJS-Test</guid>
            <pubDate>Sat, 15 Jun 2024 09:50:27 GMT</pubDate>
            <description><![CDATA[<p><code>ModuleRef</code> 클래스</p>
<ol>
<li>내부 provider 목록을 탐색
<code>get()</code> 메서드를 통해 현재 모듈에 존재하는 provider, controller 또는 injectable 을 주입 토큰/클래스 이름을 사용하여 검색</li>
</ol>
<ul>
<li>global 컨텍스트에서 검색하려면 <code>strict</code> 속성을 false 로 넘겨줘야 함</li>
</ul>
<ol start="2">
<li><p>주입 토큰을 조회 키로 사용하여 모든 provider에 대한 참조를 얻을 수 있음</p>
</li>
<li><p>정적 및 스코프가 지정된 provider를 동적으로 인스턴스화할 수 있는 방법도 제공</p>
</li>
</ol>
<ul>
<li><p>provider의 주입 토큰을 인수로 전달하여 <code>resolve()</code> 메서드</p>
<ul>
<li>DI 컨테이너 서브 트리에서 provider의 고유한 인스턴스를 반환</li>
</ul>
</li>
<li><p>ContextIdFactory 클래스를 사용하여 컨텍스트 식별자를 생성해 전달하면
여러 <code>resolve()</code> 의 호출에도 동일한 인스턴스를 사용할 수 있음</p>
<ul>
<li>수동으로 생성된 context 는 Nest DI 에 자동으로 관리되지 않음</li>
</ul>
</li>
<li><p>현재 컨텍스트 식별자를 얻으려면 @Inject() 데코레이터를 사용하여 요청 객체를 주입</p>
</li>
<li><p><code>create()</code> 를 통해 동적으로 인스턴스화 해, 프레임워크 컨테이너 외부에서 다양한 클래스를 조건부로 인스턴스화</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Circular Dependency]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Platform-agnosticism</link>
            <guid>https://velog.io/@paduck-96/NestJS-Platform-agnosticism</guid>
            <pubDate>Sat, 15 Jun 2024 09:01:02 GMT</pubDate>
            <description><![CDATA[<p>두 클래스가 서로에게 의존할 때 발생되는 문제</p>
<ul>
<li>모듈 과 provider 간에 발생할 수 있음</li>
</ul>
<ol>
<li>forward referencing을 사용하는 방법
<code>forwardRef()</code> 함수를 통해 정의되지 않은 클래스에 접근 가능</li>
</ol>
<ul>
<li><code>@Inject()</code>와 <code>forwardRef()</code> 를 사용하여 순환 종속성을 해결</li>
<li>인스턴스화 순서는 <strong>불확정적</strong>이므로 코드가 어느 생성자가 먼저 호출되는지에 의존하지 않아야 함</li>
</ul>
<ol start="2">
<li>ModuleRef 클래스를 사용하여 DI 컨테이너에서 provider 인스턴스를 검색하는 방법</li>
</ol>
<ul>
<li>module 에서 <code>forwardRef()</code> 를 사용해 참조</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Injection Scopes]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Lifecycle-Events</link>
            <guid>https://velog.io/@paduck-96/NestJS-Lifecycle-Events</guid>
            <pubDate>Sat, 15 Jun 2024 08:57:01 GMT</pubDate>
            <description><![CDATA[<p>거의 모든 것이 요청 간에 공유</p>
<ul>
<li>각 요청이 별도의 스레드에 의해 처리되는 요청/응답 멀티 스레드 무상태 모델을 따르지 않음</li>
<li>싱글톤 인스턴스 사용에 무리가 없음</li>
</ul>
<table>
  <tr>
    <td><code>DEFAULT</code></td>
    <td>애플리케이션 전체에서 provider의 단일 인스턴스가 공유됩니다. 인스턴스 수명은 애플리케이션 수명 주기와 직접적으로 연결됩니다. 애플리케이션이 부트스트랩된 후 모든 싱글톤 provider가 인스턴스화됩니다. 싱글톤 스코프는 기본적으로 사용됩니다.</td>
  </tr>
  <tr>
    <td><code>REQUEST</code></td>
    <td>들어오는 각 <strong>요청</strong>마다 provider의 새 인스턴스가 생성됩니다. 요청이 처리된 후 인스턴스는 가비지 컬렉션됩니다.</td>
  </tr>
  <tr>
    <td><code>TRANSIENT</code></td>
    <td>트랜지언트 provider는 소비자 간에 공유되지 않습니다. 트랜지언트 provider를 주입하는 각 소비자는 새로 전용 인스턴스를 받게 됩니다.</td>
  </tr>
</table>


<p><code>@Injectable()</code> 데코레이터에 scope 속성을 전달해 정의 가능</p>
<ul>
<li>custom provider 에서도 마찬가지</li>
</ul>
<ol start="2">
<li><code>REQUEST</code> Scope</li>
</ol>
<p>자체적으로 요청 스코프</p>
<ul>
<li><p>의존성 여부에 따라 스코프가 전달될 수 있음</p>
</li>
<li><p>성능에 굉장한 영향을 미침</p>
<ul>
<li>Durable provider 를 통해 매 요청에 따른 생성 해결
<code>durable</code> 속성을 전달해야 하며,
요청에 상응하는 하위 구조에 전략을 구현해야 함</li>
</ul>
</li>
</ul>
<ol start="3">
<li><code>TRANSIENT</code> Scope
요청에 따른 새로운 인스턴스를 반환받기 때문에 전달되거나 종속되지 않음</li>
</ol>
<p><code>INQUIRER</code> 토큰을 주입해 provider 가 생성된 클래스를 알 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS dynamic modules]]></title>
            <link>https://velog.io/@paduck-96/NestJS-dynamic-modules</link>
            <guid>https://velog.io/@paduck-96/NestJS-dynamic-modules</guid>
            <pubDate>Thu, 13 Jun 2024 14:13:25 GMT</pubDate>
            <description><![CDATA[<p>일반적으로는 정적 모듈을 사용함</p>
<ul>
<li>동작 모듈은 <code>플러그인</code> 개념</li>
</ul>
<p>런타임에 생성된 모듈로, <code>module</code>이라는 추가 속성만 가짐</p>
<ul>
<li>다른 속성은 필수가 아닐 수 있으나 <code>module</code> 속성은 필수</li>
</ul>
<pre><code class="language-typescript">@Module({})
export class ConfigModule {
  static register(): DynamicModule {
    return {
      module: ConfigModule,
      providers: [ConfigService],
      exports: [ConfigService],
    };
  }
}</code></pre>
<p>직접 생성이 어려우므로 <code>ConfigurableModuleBuilder</code> 제공</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Asynchronous-providers]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Asynchronous-providers</link>
            <guid>https://velog.io/@paduck-96/NestJS-Asynchronous-providers</guid>
            <pubDate>Thu, 13 Jun 2024 13:37:41 GMT</pubDate>
            <description><![CDATA[<p>하나 이상의 비동기 작업이 완료될 때까지 지연하는 경우:</p>
<pre><code class="language-typescript">{
  provide: &#39;ASYNC_CONNECTION&#39;,
  useFactory: async () =&gt; {
    const connection = await createConnection(options);
    return connection;
  },
}</code></pre>
<ul>
<li>provider를 주입하는 클래스의 인스턴스를 생성하기 전에 promise의 해결을 기다림</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Custom providers]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Custom-providers</link>
            <guid>https://velog.io/@paduck-96/NestJS-Custom-providers</guid>
            <pubDate>Thu, 13 Jun 2024 13:35:21 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-typescript">providers: [
  {
    provide: CatsService,
    useClass: CatsService,
  },
];</code></pre>
<p>providers 에서 IoC 하는 과정은 실제로 구체적으로 보면 이렇게 됌</p>
<ul>
<li>토큰(service 클래스 명) 과 실제 클래스를 연결하는 것</li>
</ul>
<p>기본 provider 의 기능 범위를 넘어서는 일에 custom provider 동작</p>
<ul>
<li>Nest가 클래스를 인스턴스화하는 대신 사용자 정의 인스턴스를 만들고 싶을 때</li>
<li>기존 클래스를 두 번째 의존성에서 재사용하고 싶을 때</li>
<li>테스트를 위해 클래스를 모의 버전으로 재정의하고 싶을 때</li>
</ul>
<ol>
<li><code>useValue</code></li>
</ol>
<ul>
<li>상수 값을 주입하거나 
외부 라이브러리를 Nest 컨테이너에 넣거나 
실제 구현을 모의 객체로 교체하는 데 유용</li>
</ul>
<pre><code class="language-typescript">import { CatsService } from &#39;./cats.service&#39;;

const mockCatsService = {
  /* mock implementation
  ...
  */
};

@Module({
  imports: [CatsModule],
  providers: [
    {
      provide: CatsService,
      useValue: mockCatsService,
    },
  ],
})
export class AppModule {}</code></pre>
<ul>
<li>DI 토큰으로 문자열이나 심볼을 사용하는 유연성<pre><code class="language-typescript">import { connection } from &#39;./connection&#39;;
</code></pre>
</li>
</ul>
<p>@Module({
  providers: [
    {
      provide: &#39;CONNECTION&#39;,
      useValue: connection,
    },
  ],
})
export class AppModule {}</p>
<pre><code>- `Inject()` 데코레이터로 주입 가능
```typescript
@Injectable()
export class CatsRepository {
  constructor(@Inject(&#39;CONNECTION&#39;) connection: Connection) {}
}</code></pre><ol start="2">
<li><code>useClass</code></li>
</ol>
<p>토큰이 해결해야 하는 클래스를 동적으로 결정</p>
<pre><code class="language-typescript">const configServiceProvider = {
  provide: ConfigService,
  useClass:
    process.env.NODE_ENV === &#39;development&#39;
      ? DevelopmentConfigService
      : ProductionConfigService,
};

@Module({
  providers: [configServiceProvider],
})
export class AppModule {}</code></pre>
<ul>
<li>여기서는 다시 또 class 이름을 토큰으로 사용</li>
</ul>
<ol start="3">
<li><code>useFactory</code>
제공자를 동적으로 생성</li>
</ol>
<ul>
<li>팩토리 함수에서 반환된 값에 의해 제공<pre><code class="language-typescript">const connectionProvider = {
provide: &#39;CONNECTION&#39;,
useFactory: (optionsProvider: OptionsProvider, optionalProvider?: string) =&gt; {
  const options = optionsProvider.get();
  return new DatabaseConnection(options);
},
inject: [OptionsProvider, { token: &#39;SomeOptionalProvider&#39;, optional: true }],
};
</code></pre>
</li>
</ul>
<p>@Module({
  providers: [
    connectionProvider,
    OptionsProvider,
  ],
})
export class AppModule {}</p>
<pre><code>
4. `useExisting`
기존 제공자에 대한 별칭
```typescript
@Injectable()
class LoggerService {
  /* implementation details */
}

const loggerAliasProvider = {
  provide: &#39;AliasedLoggerService&#39;,
  useExisting: LoggerService,
};

@Module({
  providers: [LoggerService, loggerAliasProvider],
})
export class AppModule {}</code></pre><p>서비스가 아닌 <strong>모든 기능</strong> 을 provider 로 처리할 수 있음</p>
<ul>
<li>환경 구성 등<pre><code class="language-typescript">const configFactory = {
provide: &#39;CONFIG&#39;,
useFactory: () =&gt; {
  return process.env.NODE_ENV === &#39;development&#39; ? devConfig : prodConfig;
},
};
</code></pre>
</li>
</ul>
<p>@Module({
  providers: [configFactory],
})
export class AppModule {}</p>
<pre><code>
export 는 token 이나 provider 객체 자체로도 가능
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Custom decorators]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Custom-decorators</link>
            <guid>https://velog.io/@paduck-96/NestJS-Custom-decorators</guid>
            <pubDate>Thu, 13 Jun 2024 13:17:24 GMT</pubDate>
            <description><![CDATA[<p>애초에 데코레이터라는 언어 기능을 중심으로 구축</p>
<blockquote>
<p>ES2016 데코레이터는 함수(타겟, 이름, 속성 기술자)를 인수로 받아서 반환하는 표현식입니다. 데코레이터 앞에 @ 문자를 붙이고 데코레이터할 대상의 맨 위에 배치하여 적용합니다. 데코레이터는 클래스, 메서드 또는 속성에 대해 정의될 수 있습니다.</p>
</blockquote>
<p>이러한 기본 구조:</p>
<pre><code class="language-typescript">export const Test = createParamDecorator(
  (data: string, ctx: ExecutionContext) =&gt; {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    return data ? user?.[data] : user;
  },
);</code></pre>
<p>데코레이터에 값을 넘겨주면서 실제 data 에 접근한 로직 구현 가능</p>
<p>커스텀 파라미터 decorator 도 내장된 decorator 와 동일하게 취급</p>
<pre><code class="language-typescript">@Get()
async findOne(
  // 해당 옵션 필수
  @User(new ValidationPipe({ validateCustomDecorators: true }))
  user: UserEntity,
) {
  console.log(user);
}</code></pre>
<ul>
<li>custom annotated parameters 에 대해서도 실행</li>
<li>커스텀 데코레이터에 직접 적용</li>
</ul>
<p>데코레이터들끼리 조합도 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Interceptors]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Interceptors-olqsnac4</link>
            <guid>https://velog.io/@paduck-96/NestJS-Interceptors-olqsnac4</guid>
            <pubDate>Thu, 13 Jun 2024 12:44:02 GMT</pubDate>
            <description><![CDATA[<figure><img src="https://docs.nestjs.com/assets/Interceptors_1.png" /></figure>

<p>NestInterceptor 인터페이스를 구현해야 함</p>
<p>AOP 에 영향을 받아</p>
<ul>
<li>메서드 실행 전/후에 추가 로직 바인딩</li>
<li>함수에서 반환된 결과 변환</li>
<li>함수에서 발생한 예외 변환</li>
<li>기본 함수 동작 확장</li>
<li>특정 조건에 따라 함수를 완전히 재정의 (예: 캐싱 목적)</li>
</ul>
<p>두 가지 인수를 받는데:</p>
<ol>
<li><code>ArgumentHost</code> 를 상속하는 <code>ExecutionContext</code></li>
<li><code>handle()</code> 함수를 구현하는 <code>CallHandler</code> 인터페이스</li>
</ol>
<ul>
<li>원하는 시점에 라우트 핸들러 함수 호출</li>
<li>호출하지 않으면 라우트 핸들러 전혀 실행되지 않음
즉, 라우트 핸들러 실행 전/후 에 원하는 로직 구현 가능</li>
<li>Pointcut 이라고도 하며</li>
<li>RxJS 연산자의 <code>Observable</code>를 통해 응답 조작 가능</li>
</ul>
<p><strong>전체에 걸쳐 발생하는 요구 사항에 대한 재사용 가능한 솔루션 만드는 용도</strong></p>
<blockquote>
<p>생각외로 <code>RxJS</code> 연산자가 많이 사용되므로 고려</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Guards]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Guards</link>
            <guid>https://velog.io/@paduck-96/NestJS-Guards</guid>
            <pubDate>Wed, 12 Jun 2024 16:25:29 GMT</pubDate>
            <description><![CDATA[<p><code>@Injectable()</code> 데코레이터가 달린 클래스
<code>CanActivate</code> 인터페이스를 구현</p>
<figure><img src="https://docs.nestjs.com/assets/Guards_1.png" /></figure>

<p><strong>단일 책임</strong> 원칙
<strong>인증</strong></p>
<ul>
<li>특정 조건(권한, 역할, ACL 등)에 따라 주어진 요청이 라우트 핸들러에 의한 처리 여부를 결정</li>
</ul>
<p>인증/권한은 전통적인 Express 애플리케이션에서 종종 <a href="https://docs.nestjs.com/middleware">미들웨어</a>에 의해 처리</p>
<ul>
<li>토큰 유효성 검사나 <code>request</code> 객체에 속성을 첨부하는 작업은 특정 라우트 컨텍스트(및 메타데이터)와 강하게 연결되지 않기 때문</li>
<li>미들웨어는 본질적으로 어떤 핸들러가 <code>next()</code> 함수 호출 후에 실행될지 알 수 없음
반면, <strong>Guard</strong>는 <code>ExecutionContext</code> 인스턴스에 접근하여 이후 실행 대상 확인 가능</li>
</ul>
<p>예외 필터, 파이프, 인터셉터와 마찬가지로 요청/응답 사이클의 정확한 지점에서 처리 로직을 삽입하도록 설계, 선언적으로 수행 가능</p>
<p>가드는 모든 미들웨어가 실행된 <strong>후</strong>, 인터셉터나 파이프가 실행되기 <strong>전에</strong> 실행</p>
<h3 id="authorization-guard">Authorization guard</h3>
<p>충분한 권한을 가지고 있을 때만 사용하는 경우</p>
<ul>
<li>예시로, 토큰 추출, 유효성 검사, 요청이 진행될 수 있는지 여부를 결정</li>
</ul>
<pre><code class="language-typescript">import { Injectable, CanActivate, ExecutionContext } from &#39;@nestjs/common&#39;;
import { Observable } from &#39;rxjs&#39;;

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise&lt;boolean&gt; | Observable&lt;boolean&gt; {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}</code></pre>
<ul>
<li>validateRequest 는 커스텀 함수</li>
</ul>
<p>모든 가드는 <code>canActivate()</code> 함수를 구현해야 함</p>
<ul>
<li><p>현재 요청이 허용되는지 여부를 나타내는 boolean 값 반환</p>
</li>
<li><p>이는 동기적으로 또는 비동기적으로(Promise 또는 Observable을 통해) 응답 반환</p>
<ul>
<li><code>true</code>를 반환하면 요청이 처리</li>
<li><code>false</code>를 반환하면 Nest는 요청을 거부</li>
</ul>
</li>
</ul>
<h3 id="execution-context">Execution context</h3>
<p><code>canActivate()</code> 함수는 단일 인수로 <code>ExecutionContext</code> 인스턴스 받음<code>ExecutionContext</code>는 <code>ArgumentsHost</code>를 상속</p>
<ul>
<li><a href="https://docs.nestjs.com/exception-filters#arguments-host">exception filters</a> 장의 <strong>Arguments host</strong> 섹션</li>
</ul>
<p><code>ArgumentsHost</code>를 확장해 <code>ExecutionContext</code>는 현재 실행 프로세스에 대한 추가 세부 정보를 제공하는 여러 새로운 헬퍼 메서드를 추가</p>
<ul>
<li>다양한 컨트롤러, 메서드 및 실행 컨텍스트에서 동작하게 함</li>
</ul>
<p><a href="https://docs.nestjs.com/fundamentals/execution-context">여기</a></p>
<h3 id="role-based-authentication">Role-based authentication</h3>
<p>모든 역할 허용에서 특정 역할 허용 예시:</p>
<pre><code class="language-typescript">import { Injectable, CanActivate, ExecutionContext } from &#39;@nestjs/common&#39;;
import { Observable } from &#39;rxjs&#39;;

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise&lt;boolean&gt; | Observable&lt;boolean&gt; {
    return true;
  }
}</code></pre>
<h3 id="binding-guards">Binding guards</h3>
<p><strong>컨트롤러 범위</strong>, 메서드 범위 또는 전역 범위로 설정 가능</p>
<ul>
<li><code>@UseGuards()</code> 데코레이터를 사용하여 컨트롤러 범위 가드를 설정</li>
<li>단일-복수 인수 목록을 받음</li>
</ul>
<pre><code class="language-typescript">@Controller(&#39;cats&#39;)
@UseGuards(RolesGuard)
export class CatsController {}</code></pre>
<p>인스턴스가 아닌 클래스로 전달하여 프레임워크가 인스턴스화를 책임지도록 하고 의존성 주입을 활성화:</p>
<pre><code class="language-typescript">@Controller(&#39;cats&#39;)
@UseGuards(new RolesGuard())
export class CatsController {}</code></pre>
<p>단일 메서드에 적용시 <strong>메서드 수준</strong>에서 <code>@UseGuards()</code> 데코레이터를 적용</p>
<p>전역 가드는 Nest 애플리케이션 인스턴스의 <code>useGlobalGuards()</code> 메서드를 사용:</p>
<pre><code class="language-typescript">const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());</code></pre>
<p>의존성 주입 측면에서 전역 가드는 모듈 외부에서 등록될 경우(위 예제에서처럼 <code>useGlobalGuards()</code>를 사용하여) 의존성을 주입할 수 없음</p>
<p><strong>모듈 내부에서 직접 전역 가드</strong>를 설정:</p>
<pre><code class="language-typescript">import { Module } from &#39;@nestjs/common&#39;;
import { APP_GUARD } from &#39;@nestjs/core&#39;;

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}</code></pre>
<h3 id="setting-roles-per-handler">Setting roles per handler</h3>
<p>역할이나 각 핸들러에 대해 허용되는 역할에 대해 알지 못하는 단순한 상태
<strong>커스텀 메타데이터</strong>로 해결</p>
<ul>
<li><a href="https://docs.nestjs.com/fundamentals/execution-context#reflection-and-metadata">여기</a></li>
</ul>
<p><code>Reflector.createDecorator</code> 정적 메서드,
생성된 데코레이터나 내장된 <code>@SetMetadata()</code> 데코레이터를 통해,
라우트 핸들러에 커스텀 <strong>메타데이터</strong>를 첨부 가능</p>
<ul>
<li><code>Reflector</code>는 프레임워크에 의해 제공되며 <code>@nestjs/core</code> 패키지</li>
</ul>
<pre><code class="language-typescript">import { Reflector } from &#39;@nestjs/core&#39;;

export const Roles = Reflector.createDecorator&lt;string[]&gt;();</code></pre>
<ul>
<li><code>Roles</code> 데코레이터는 <code>string[]</code> 타입의 단일 인수를 받는 함수</li>
</ul>
<p>이제 이 데코레이터를 사용하려면 핸들러에 주석을 추가하기만 하면 됩니다:</p>
<pre><code class="language-typescript">@Post()
@Roles([&#39;admin&#39;])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto

);
}</code></pre>
<blockquote>
<p><code>Reflector.createDecorator</code> 메서드를 사용하는 대신 내장된 <code>@SetMetadata()</code> 데코레이터를 사용할 수 있습니다. 자세한 내용은 <a href="https://docs.nestjs.com/fundamentals/execution-context#low-level-approach">여기</a>를 참조하세요.</p>
</blockquote>
<h3 id="putting-it-all-together">Putting it all together</h3>
<p>현재 처리 중인 라우트의 실제 역할과 현재 사용자에게 할당된 <strong>역할을 비교</strong>하여 조건부 처리:</p>
<pre><code class="language-typescript">import { Injectable, CanActivate, ExecutionContext } from &#39;@nestjs/common&#39;;
import { Reflector } from &#39;@nestjs/core&#39;;
import { Roles } from &#39;./roles.decorator&#39;;

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get(Roles, context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return matchRoles(roles, user.roles);
  }
}</code></pre>
<blockquote>
<p><a href="https://docs.nestjs.com/fundamentals/execution-context#reflection-and-metadata">Reflection and metadata</a> 섹션에서 <code>Reflector</code>를 컨텍스트에 맞게 사용하는 방법에 대해 자세히 알아보세요.</p>
</blockquote>
<p>권한이 부족한 경우라면:</p>
<pre><code class="language-json">{
  &quot;statusCode&quot;: 403,
  &quot;message&quot;: &quot;Forbidden resource&quot;,
  &quot;error&quot;: &quot;Forbidden&quot;
}</code></pre>
<ul>
<li><code>false</code>를 반환하면 프레임워크는 <code>ForbiddenException</code> 을 기본 반환</li>
</ul>
<p>특정 예외:</p>
<pre><code class="language-typescript">throw new UnauthorizedException();</code></pre>
<p>가드에서 발생한 모든 예외는 <a href="https://docs.nestjs.com/exception-filters">예외 레이어</a>에 의해 처리</p>
<blockquote>
<p>실제 예제에 대해 자세히 알아보려면 <a href="https://docs.nestjs.com/security/authorization">여기</a>를 참조하세요.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Pipes]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Pipes</link>
            <guid>https://velog.io/@paduck-96/NestJS-Pipes</guid>
            <pubDate>Wed, 12 Jun 2024 07:57:49 GMT</pubDate>
            <description><![CDATA[<p><code>@Injectable()</code> 데코레이터가 달린 클래스이며, implements <code>PipeTransform</code> 인터페이스</p>
<figure>
  <img src="https://docs.nestjs.com/assets/Pipe_1.png" />
</figure>

<p>파이프는 두 가지 주요 용도로 사용</p>
<ul>
<li><strong>변환</strong>: 입력 데이터를 원하는 형태로 변환 (예: 문자열-&gt;정수)</li>
<li><strong>검증</strong>: 입력 데이터를 평가하고 유효하면 그대로 통과시키고, 그렇지 않으면 예외를 던짐</li>
</ul>
<p>두 경우 모두 파이프는 <a href="https://docs.nestjs.com/controllers#route-parameters">컨트롤러 라우트 핸들러</a>에서 처리되는 <code>arguments</code>에 대해 작동</p>
<ul>
<li>메서드가 호출되기 직전에 파이프를 삽입</li>
<li>이후 변환된 인자가 있는 상태로 라우트 핸들러가 호출</li>
</ul>
<blockquote>
<p>파이프는 예외 구역 내에서 실행됩니다. 즉, 파이프가 예외를 던지면 예외 레이어(전역 예외 필터 및 현재 컨텍스트에 적용된 모든 <a href="https://docs.nestjs.com/exception-filters">예외 필터</a>에 의해 처리됩니다. 따라서 파이프에서 예외가 발생하면 컨트롤러 메서드는 실행되지 않습니다. 이는 외부 소스에서 애플리케이션으로 들어오는 데이터를 유효성 검사하는 최선의 방법을 제공합니다.</p>
</blockquote>
<h3 id="built-in-pipes">Built-in pipes</h3>
<p>Nest에는 기본적으로 사용할 수 있는 아홉 가지 파이프가 있습니다:</p>
<ul>
<li><code>ValidationPipe</code></li>
<li><code>ParseIntPipe</code></li>
<li><code>ParseFloatPipe</code></li>
<li><code>ParseBoolPipe</code></li>
<li><code>ParseArrayPipe</code></li>
<li><code>ParseUUIDPipe</code></li>
<li><code>ParseEnumPipe</code></li>
<li><code>DefaultValuePipe</code></li>
<li><code>ParseFilePipe</code></li>
</ul>
<p>이 파이프들은 <code>@nestjs/common</code> 패키지에서 사용</p>
<h3 id="binding-pipes">Binding pipes</h3>
<p>파이프 인스턴스를 적절한 컨텍스트에 바인딩해야 사용 가능</p>
<pre><code class="language-typescript">@Get(&#39;:id&#39;)
async findOne(@Param(&#39;id&#39;, ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}</code></pre>
<ul>
<li>매개 변수가 숫자여야 하고, 메서드 실행 전에 호출</li>
</ul>
<p>다음의 호출에</p>
<pre><code class="language-bash">GET localhost:3000/abc</code></pre>
<p>다음의 예외가 발생</p>
<pre><code class="language-json">{
  &quot;statusCode&quot;: 400,
  &quot;message&quot;: &quot;Validation failed (numeric string is expected)&quot;,
  &quot;error&quot;: &quot;Bad Request&quot;
}</code></pre>
<p>마찬가지로, 클래스를 전달하면 프레임워크가 인스턴스화 책임을 지고 의존성 주입을 활성화</p>
<ul>
<li>혹은, 파이프와 가드는 인라인 인스턴스를 전달 가능 </li>
<li>인라인 인스턴스를 전달하면 내장된 파이프의 동작을 옵션을 통해 커스터마이징 가능</li>
</ul>
<pre><code class="language-typescript">@Get(&#39;:id&#39;)
async findOne(
  @Param(&#39;id&#39;, new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
  id: number,
) {
  return this.catsService.findOne(id);
}</code></pre>
<blockquote>
<p><code>ParseUUIDPipe()</code>를 사용할 때 버전 3, 4 또는 5의 UUID를 파싱합니다. 특정 버전의 UUID만 필요하다면 파이프 옵션에 버전을 전달할 수 있습니다.</p>
</blockquote>
<blockquote>
<p><a href="https://docs.nestjs.com/techniques/validation">유효성 검사 기술</a>에서 검증 파이프의 광범위한 예제를 참조하세요.</p>
</blockquote>
<h3 id="custom-pipes">Custom pipes</h3>
<pre><code class="language-typescript">import { PipeTransform, Injectable, ArgumentMetadata } from &#39;@nestjs/common&#39;;

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}</code></pre>
<blockquote>
<p><code>PipeTransform&lt;T, R&gt;</code>는 모든 파이프가 구현해야 하는 제네릭 인터페이스입니다. 제네릭 인터페이스는 입력 <code>value</code>의 타입을 나타내는 <code>T</code>와 <code>transform()</code> 메서드의 반환 타입을 나타내는 <code>R</code>을 사용합니다.</p>
</blockquote>
<p>모든 파이프는 <code>PipeTransform</code> 를 위해 <code>transform()</code> 메서드를 구현해야 함</p>
<ul>
<li><code>value</code><ul>
<li>현재 처리 중인 메서드 인자(라우트 핸들러 메서드가 받기 전의 인자)</li>
</ul>
</li>
<li><code>metadata</code><ul>
<li>현재 처리 중인 메서드 인자의 메타데이터</li>
</ul>
</li>
</ul>
<pre><code class="language-typescript">export interface ArgumentMetadata {
  type: &#39;body&#39; | &#39;query&#39; | &#39;param&#39; | &#39;custom&#39;;
  metatype?: Type&lt;unknown&gt;;
  data?: string;
}</code></pre>
<ul>
<li>이 속성들은 현재 처리 중인 인자를 설명</li>
</ul>
<table>
  <tr>
    <td>
      <code>type</code>
    </td>
    <td>
      인자가 body <code>@Body()</code>, query <code>@Query()</code>, param <code>@Param()</code> 또는 custom 파라미터인지 여부를 나타냅니다. (자세한 내용은 <a routerLink="https://docs.nestjs.com/custom-decorators">여기</a>를 참조하세요).
    </td>
  </tr>
  <tr>
    <td>
      <code>metatype</code>
    </td>
    <td>
      인자의 메타타입을 제공하며, 예를 들어 <code>String</code>입니다. 주의: 라우트 핸들러 메서드 서명에서 타입 선언을 생략하거나 바닐라 JavaScript를 사용할 경우 값이 <code>undefined</code>입니다.
    </td>
  </tr>
  <tr>
    <td>
      <code>data</code>
    </td>
    <td>
      데코레이터에 전달된 문자열이며, 예를 들어 <code>@
Body('string')</code>입니다. 데코레이터 괄호를 비워두면<code>undefined</code>입니다.
    </td>
  </tr>
</table>

<blockquote>
<p>TypeScript 인터페이스는 트랜스파일 동안 사라집니다. 따라서 메서드 매개변수의 타입이 클래스가 아닌 인터페이스로 선언되면 <code>metatype</code> 값은 <code>Object</code>가 됩니다.</p>
</blockquote>
<h3 id="schema-based-validation">Schema based validation</h3>
<p>post 의 body 객체에 대한 확인 예제;</p>
<pre><code class="language-typescript">@Post()
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}</code></pre>
<pre><code class="language-typescript">export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}</code></pre>
<p><code>create</code> 메서드 요청이 유효한 본문을 포함하고 있는지 확인하려면, dto 객체의 세 가지 멤버를 검증</p>
<ul>
<li>라우트 핸들러 메서드 내부에서 수행 시 <strong>단일 책임 원칙</strong>(SRP) 불충족</li>
</ul>
<p>따라서, <strong>검증 클래스</strong>를 작성 후 검증 작업을 위임</p>
<ul>
<li>각 메서드의 시작 부분에서 호출해야 한다는 단점</li>
</ul>
<p>혹은, 검증 미들웨어</p>
<ul>
<li><strong>실행 컨텍스트</strong>(호출될 핸들러 및 해당 매개변수를 포함하는)를 인식하지 못하는 미들웨어의 특성상 <strong>일반적인 미들웨어</strong>를 작성할 수 없</li>
</ul>
<h3 id="object-schema-validation">Object schema validation</h3>
<p>깔끔하고 <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> 방식으로 수행하는 여러 접근 방식 중, <strong>스키마 기반</strong> 검증이 일반적</p>
<p><a href="https://zod.dev/">Zod</a> 라이브러리는 간단한 API로 스키마를 작성할 수 있</p>
<pre><code class="language-bash">$ npm install --save zod</code></pre>
<ul>
<li><code>schema.parse()</code> 메서드를 적용하여 제공된 스키마에 대해 들어오는 인수를 검증</li>
</ul>
<p>앞서 언급했듯이, <strong>검증 파이프</strong>는 값을 변경하지 않고 반환하거나 예외를 던짐</p>
<pre><code class="language-typescript">import { PipeTransform, ArgumentMetadata, BadRequestException } from &#39;@nestjs/common&#39;;
import { ZodSchema  } from &#39;zod&#39;;

export class ZodValidationPipe implements PipeTransform {
  constructor(private schema: ZodSchema) {}

  transform(value: unknown, metadata: ArgumentMetadata) {
    try {
      const parsedValue = this.schema.parse(value);
      return parsedValue;
    } catch (error) {
      throw new BadRequestException(&#39;Validation failed&#39;);
    }
  }
}</code></pre>
<h3 id="binding-validation-pipes">Binding validation pipes</h3>
<p> <code>ZodValidationPipe</code>를 사용 단계:</p>
<ol>
<li><code>ZodValidationPipe</code> 인스턴스를 생성</li>
<li>파이프의 클래스 생성자에 컨텍스트별 Zod 스키마를 전달</li>
<li>파이프를 메서드에 바인딩</li>
</ol>
<ul>
<li>Zod 스키마 예제:</li>
</ul>
<pre><code class="language-typescript">import { z } from &#39;zod&#39;;

export const createCatSchema = z
  .object({
    name: z.string(),
    age: z.number(),
    breed: z.string(),
  })
  .required();

export type CreateCatDto = z.infer&lt;typeof createCatSchema&gt;;</code></pre>
<p><code>@UsePipes()</code> 데코레이터의 구현:</p>
<pre><code class="language-typescript">@Post()
@UsePipes(new ZodValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}</code></pre>
<blockquote>
<p><code>@UsePipes()</code> 데코레이터는 <code>@nestjs/common</code> 패키지에서 가져옵니다.</p>
</blockquote>
<blockquote>
<p><code>zod</code> 라이브러리는 <code>tsconfig.json</code> 파일에서 <code>strictNullChecks</code> 구성을 활성화해야 합니다.</p>
</blockquote>
<h3 id="class-validator">Class validator</h3>
<blockquote>
<p>이 섹션의 기술은 TypeScript를 필요로 하며 바닐라 JavaScript로 작성된 애플리케이션에서는 사용할 수 없습니다.</p>
</blockquote>
<p><a href="https://github.com/typestack/class-validator">class-validator</a> 라이브러리와 잘 작동</p>
<ul>
<li>데코레이터 기반 검증 가능</li>
<li><code>metatype</code>에 접근할 수 있기에 <strong>Pipe</strong> 기능과 결합할 때 매우 강력</li>
</ul>
<pre><code class="language-bash">$ npm i --save class-validator class-transformer</code></pre>
<p><code>CreateCatDto</code> 클래스가 Post 본문 객체의 단일 진리의 원천으로 유지(별도의 검증 클래스를 작성할 필요가 없음)</p>
<pre><code class="language-typescript">import { IsString, IsInt } from &#39;class-validator&#39;;

export class CreateCatDto {
  @IsString()
  name: string;

  @IsInt()
  age: number;

  @IsString()
  breed: string;
}</code></pre>
<blockquote>
<p>class-validator 데코레이터에 대해 자세히 알아보려면 <a href="https://github.com/typestack/class-validator#usage">여기</a>를 참조하세요.</p>
</blockquote>
<p><code>ValidationPipe</code> 클래스를 작성</p>
<pre><code class="language-typescript">import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from &#39;@nestjs/common&#39;;
import { validate } from &#39;class-validator&#39;;
import { plainToInstance } from &#39;class-transformer&#39;;

@Injectable()
export class ValidationPipe implements PipeTransform&lt;any&gt; {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length &gt; 0) {
      throw new BadRequestException(&#39;Validation failed&#39;);
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}</code></pre>
<blockquote>
<p>직접 일반적인 검증 파이프를 작성할 필요는 없습니다. Nest는 기본적으로 <code>ValidationPipe</code>를 제공합니다. 내장된 <code>ValidationPipe</code>는 우리가 작성한 샘플보다 더 많은 옵션을 제공하며, 자세한 내용과 많은 예제를 <a href="/techniques/validation">여기</a>에서 확인할 수 있습니다.</p>
</blockquote>
<blockquote>
<p>위에서는 <a href="https://github.com/typestack/class-transformer">class-transformer</a> 라이브러리를 사용했습니다. 이 라이브러리는 class-validator 라이브러리와 같은 저자가 작성했기 때문에 상호 호환성이 좋습니다.</p>
</blockquote>
<ol>
<li><code>transform()</code> 메서드가 <code>async</code></li>
</ol>
<ul>
<li>동기 및 <strong>비동기</strong> 파이프가 가능하므로 <code>async</code>를 활용</li>
<li>일부 class-validator 검증이 <a href="https://github.com/typestack/class-validator#custom-validation-classes">비동기</a>일 수 있기 때문(Promise를 활용)</li>
</ul>
<ol start="2">
<li><p>구조 분해 할당으로 <code>ArgumentMetadata</code>에서 <code>metatype</code> 필드 추출</p>
</li>
<li><p>헬퍼 함수 <code>toValidate()</code></p>
</li>
</ol>
<ul>
<li>기본 JavaScript 타입은 검증 단계 생략(기본 JavaScript 타입에는 검증 데코레이터를 붙일 수 없기 때문)</li>
</ul>
<ol start="4">
<li>일반 JavaScript 인수 객체를 <code>plainToInstance()</code> 함수로 타입이 지정된 객체로 변환하여 검증을 수행</li>
</ol>
<ul>
<li>네트워크 요청에서 역직렬화된 들어오는 post 본문 객체는 <strong>타입 정보가 전혀 없음</strong> (Express와 같은 기본 플랫폼의 작동 방식)</li>
<li>class-validator는 우리가 DTO에 대해 이전에 정의한 검증 데코레이터를 사용해야 하므로, 들어오는 본문을 단순한 객체가 아닌 적절한 데코레이터가 적용된 객체로 처리하기 위해 이 변환이 필요</li>
</ul>
<ol start="5">
<li><p><strong>검증 파이프</strong>는 값을 변경하지 않고 반환하거나 예외를 던</p>
</li>
<li><p><code>ValidationPipe</code>를 바인딩</p>
</li>
</ol>
<ul>
<li>매개변수 범위, 메서드 범위, 컨트롤러 범위 또는 전역 범위로 바인딩</li>
</ul>
<p>파이프 인스턴스를 라우트 핸들러 <code>@Body()</code> 데코레이터에 바인딩하여 파이프가 post 본문을 검증하게 함</p>
<pre><code class="language-typescript">@Post()
async create(
  @Body(new ValidationPipe()) createCatDto: CreateCatDto,
) {
  this.catsService.create(createCatDto);
}</code></pre>
<h3 id="global-scoped-pipes">Global scoped pipes</h3>
<p>전역 범위 파이프로 설정하여 애플리케이션 전체의 모든 라우트 핸들러에 적용 가능</p>
<pre><code class="language-typescript">async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();</code></pre>
<blockquote>
<p><a href="https://docs.nestjs.com/faq/hybrid-application">하이브리드 앱</a>의 경우 <code>useGlobalPipes()</code> 메서드는 게이트웨이 및 마이크로서비스에 대해 파이프를 설정하지 않습니다. &quot;표준&quot; (비하이브리드) 마이크로서비스 앱의 경우, <code>useGlobalPipes()</code>는 파이프를 전역적으로 마운트합니다.</p>
</blockquote>
<ul>
<li>전역 파이프는 전체 애플리케이션에 대해 모든 컨트롤러 및 모든 라우트 핸들러에 사용</li>
</ul>
<p>전역 파이프가 모듈 외부에서 등록될 경우(위 예제에서처럼 <code>useGlobalPipes()</code>를 사용하여) 의존성을 주입할 수 없음</p>
<ul>
<li>모듈의 컨텍스트 외부에서 이루어졌기 때문</li>
</ul>
<p><strong>모듈 내부에서 직접 전역 파이프</strong>를 설정하여 해결 가능:</p>
<pre><code class="language-typescript">import { Module } from &#39;@nestjs/common&#39;;
import { APP_PIPE } from &#39;@nestjs/core&#39;;

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe,
    },
  ],
})
export class AppModule {}</code></pre>
<blockquote>
<p>이 방법을 사용하여 파이프에 대해 의존성 주입을 수행할 때, 이 구성이 사용된 모듈에 관계없이 파이프는 실제로 전역적임을 유의하세요. 어디에서 해야 할까요? 파이프(<code>위 예제에서는 ValidationPipe</code>)가 정의된 모듈을 선택하세요. 또한, <code>useClass</code>는 커스텀 제공자 등록을 처리하는 유일한 방법이 아닙니다. 자세한 내용은 <a href="/fundamentals/custom-providers">여기</a>에서 확인하세요.</p>
</blockquote>
<h3 id="the-built-in-validationpipe">The built-in ValidationPipe</h3>
<p>직접 일반적인 검증 파이프를 작성할 필요는 없고,<code>ValidationPipe</code>를 제공
내장된 <code>ValidationPipe</code>는 우리가 작성한 샘플보다 더 많은 옵션을 제공하며, 자세한 내용과 많은 예제를 <a href="/techniques/validation">여기</a>에서 확인 가능</p>
<h3 id="transformation-use-case">Transformation use case</h3>
<p>입력 데이터를 원하는 형식으로 <strong>변환</strong>도 가능
<code>transform</code> 함수에서 반환된 값이 인자의 이전 값을 완전히 덮어쓰므로 가능</p>
<p><strong>변환 파이프</strong>는 타입 변환 혹은 기본값 적용 등의 작업을 수행하여 클라이언트 요청과 요청 핸들러 사이에 처리 함수를 삽입 가능</p>
<pre><code class="language-typescript">import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from &#39;@nestjs/common&#39;;

@Injectable()
export class ParseIntPipe implements PipeTransform&lt;string, number&gt; {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException(&#39;Validation failed&#39;);
    }
    return val;
  }
}</code></pre>
<p>바인딩은:</p>
<pre><code class="language-typescript">@Get(&#39;:id&#39;)
async findOne(@Param(&#39;id&#39;, new ParseIntPipe()) id) {
  return this.catsService.findOne(id);
}</code></pre>
<h3 id="providing-defaults">Providing defaults</h3>
<p><code>Parse*</code> 파이프는 매개변수 값이 정의되어 있을 것으로 예상(기대)</p>
<ul>
<li><code>null</code> 또는 <code>undefined</code> 값은 예외</li>
<li>누락의 경우를 처리하려면 <code>Parse*</code> 파이프가 이러한 값에 대해 작동하기 전에 기본 값을 제공</li>
</ul>
<p><code>DefaultValuePipe</code>를 사용하여, <code>Parse*</code> 파이프가 작동하기 전에 <code>@Query()</code> 데코레이터에서 <code>DefaultValuePipe</code> 인스턴스를 인스턴스:</p>
<pre><code class="language-typescript">@Get()
async findAll(
  @Query(&#39;activeOnly&#39;, new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
  @Query(&#39;page&#39;, new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
  return this.catsService.findAll({ activeOnly, page });
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[NextJS Exception Filters]]></title>
            <link>https://velog.io/@paduck-96/NextJS-Exception-Filters</link>
            <guid>https://velog.io/@paduck-96/NextJS-Exception-Filters</guid>
            <pubDate>Wed, 12 Jun 2024 00:20:18 GMT</pubDate>
            <description><![CDATA[<p>내장된 <strong>예외 처리 레이어</strong>가 존재</p>
<ul>
<li>모든 예외에 적용</li>
<li>다뤄지지 않은 예외는 해당 레이어로 가며, 사용자 친화적인 메시지 응답</li>
</ul>
<figure>
  <img src="https://docs.nestjs.com/assets/Filter_1.png" />
</figure>

<p><code>HttpException</code> 을 내장된 <strong>전역 예외 처리 필터</strong>에서 처리됨</p>
<ul>
<li>해당되는 예외 혹은 하위 예외가 아닐 경우 <code>unrecognized</code> 로 해당 응답 반환<pre><code class="language-typescript">{
&quot;statusCode&quot;: 500,
&quot;message&quot;: &quot;Internal server error&quot;
}</code></pre>
<blockquote>
<p>전역 예외 필터는 <code>http-errors</code> 라이브러리를 부분적으로 지원합니다. <code>statusCode</code> 와 <code>message</code> 를 포함한 에러는 적절하게 합쳐지고 응답(확인되지 않은 <code>InternalServerErrorException</code> 예외 대신에)으로써 반환됩니다</p>
</blockquote>
</li>
</ul>
<h3 id="thorwing-standard-exceptions">Thorwing standard exceptions</h3>
<p>내장된 <code>@nestjs/common</code> 패키지에서 비롯된 <code>HttpException</code> 클래스를 제공</p>
<ul>
<li>특정 HTTP Rest/GraphQL API 에 대해 표준 HTTP 응답을 반환하는 것이 바람직</li>
</ul>
<pre><code class="language-typescript">@Get()
async findAll() {
  throw new HttpException(&#39;Forbidden&#39;, HttpStatus.FORBIDDEN);
}</code></pre>
<blockquote>
<p><code>@nestjs/common</code> 패키지에서 비롯된 helper enum 입니다.</p>
</blockquote>
<ul>
<li><p>이것에 대해</p>
<pre><code class="language-typescript">{
&quot;statusCode&quot;: 403,
&quot;message&quot;: &quot;Forbidden&quot;
}</code></pre>
<p><code>HttpException</code> 생성자는 response 와 status 라는 두 가지 인자를 받음</p>
</li>
<li><p><code>response</code> 은 JSON 응답 본문이며, <code>string</code> 혹은 <code>object</code> 가능</p>
<ul>
<li>object 를 넘겨서 JSON 응답 본문 대체 가능</li>
<li><code>statusCode</code> 은 HTTP status code </li>
<li><code>message</code>는 HTTP error 에 대한 짧은 설명<ul>
<li><code>response</code> 에 문자열을 넘김으로써 대체 가능</li>
</ul>
</li>
</ul>
</li>
<li><p><code>status</code> 는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP Status Code</a> 를 의미</p>
<ul>
<li>올바른 HTTP Status code 가 들어와야하며, <code>@nestjs/common</code> 의 <code>HttpStatus</code> enum 을 쓰는게 제일 바람직</li>
</ul>
</li>
<li><p>error의 <a href="https://nodejs.org/en/blog/release/v16.9.0/#error-cause">이유</a> 에 해당하는 <code>options</code> 속성은 응답 객체에 들어가진 않으나, 로깅에 매우 유용</p>
<ul>
<li>HttpException 가 발생한 내부 사유 확인 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-typescript">@Get()
async findAll() {
  try {
    await this.service.findAll()
  } catch (error) {
    throw new HttpException({
      status: HttpStatus.FORBIDDEN,
      error: &#39;This is a custom message&#39;,
    }, HttpStatus.FORBIDDEN, {
      cause: error
    });
  }
}</code></pre>
<ul>
<li>error 반환이며<pre><code class="language-typescript">{
&quot;status&quot;: 403,
&quot;error&quot;: &quot;This is a custom message&quot;
}</code></pre>
</li>
<li>그에 대한 응답</li>
</ul>
<h3 id="custom-exceptions">Custom exceptions</h3>
<p><code>HttpException</code> 을 상속하는 개인화된 <strong>예외 계층</strong> 만들기</p>
<pre><code class="language-typescript">export class ForbiddenException extends HttpException {
  constructor() {
    super(&#39;Forbidden&#39;, HttpStatus.FORBIDDEN);
  }
}</code></pre>
<pre><code class="language-typescript">@Get()
async findAll() {
  throw new ForbiddenException();
}</code></pre>
<p>일반적인 예외 처리와 동일하게 적용</p>
<h3 id="built-in-http-exceptions">Built-in HTTP exceptions</h3>
<p><code>HttpException</code> 으로부터 상속받고, <code>@nestjs/common</code> 패키지에 비롯</p>
<h3 id="exception-filters">Exception filters</h3>
<p>예외 계층에 대한 완전한 컨트롤</p>
<ul>
<li>사용자에게 반환될 응답의 내용과 예외 flow 에 대해 컨트롤 가능</li>
</ul>
<pre><code class="language-typescript">import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from &#39;@nestjs/common&#39;;
import { Request, Response } from &#39;express&#39;;

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse&lt;Response&gt;();
    const request = ctx.getRequest&lt;Request&gt;();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: request.url,
      });
  }
}</code></pre>
<blockquote>
<p>모든 예외 필터는 일반 <code>ExceptionFilter&lt;T&gt;</code> 인터페이스를 구현해야 합니다. 이를 위해서는 <code>catch(exception: T, host: ArgumentsHost)</code> 함수에 표시된 서명을 제공해야 합니다 . <code>T</code>는 예외 유형을 나타냅니다.
<code>@nestjs/platform-fastify</code> 를 사용하는 경우 <code>response.json()</code> 대신에 <code>response.send()</code> 를 사용할 수 있습니다. <code>fastify</code>를 위해 정확한 타입을 가져오는 걸 명심하세요</p>
</blockquote>
<p><code>@Catch(HttpException)</code> 데코레이터는 <code>HttpException</code>에 해당하는 특정 예외만 본다는 얘기</p>
<ul>
<li>단일-복수 파라미터 가능</li>
</ul>
<h3 id="arguments-host">Arguments host</h3>
<p><code>catch()</code> 함수의 인자</p>
<ul>
<li><code>exception</code> 은 현재 진행 중인 예외 객체</li>
<li><code>host</code> 는 <code>ArgumentsHost</code> 객체<ul>
<li><a href="https://docs.nestjs.com/fundamentals/execution-context">execution context</a></li>
</ul>
</li>
</ul>
<p>Nest <strong>모든 context</strong> 에 있기 때문에 추상화 수준 필요했음</p>
<h3 id="binding-filters">Binding filters</h3>
<pre><code class="language-typescript">@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}</code></pre>
<blockquote>
<p><code>@UseFilters()</code> 데코레이터는 <code>@nestjs/common</code> 패키지에 있습니다.</p>
</blockquote>
<ul>
<li>단일-복수 filter 인스턴스를 인자로 받음</li>
<li>인스턴스가 아닌 클래스 자체를 넘겨, IoC 및 DI 가능</li>
</ul>
<blockquote>
<p>가능할 경우 인스턴스보다 클래스를 통해 필터를 적용허세요. 동일한 클래스의 인스턴스를 재사용할 수 있어 <code>메모리 사용량</code>이 줄어듭니다.</p>
</blockquote>
<p>함수 레벨, 컨트롤러 레벨, 전역 레벨 에서 스코핑 가능</p>
<pre><code class="language-typescript">// 컨트롤러 레벨
@UseFilters(new HttpExceptionFilter())
export class CatsController {}</code></pre>
<pre><code class="language-typescript">// 전역 레벨
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();</code></pre>
<blockquote>
<p><code>useGlobalFilters()</code> 함수는 게이트웨이 나 하이브리드 앱에는 적용되지 않습니다.</p>
</blockquote>
<p><code>useGlobalFilters()</code> 를 사용하는 경우 DI 를 적용할 수 없음</p>
<ul>
<li>어느 module의 context 외부에서 수행되기 때문</li>
</ul>
<pre><code class="language-typescript">// DI 문제를 해결한 방식

import { Module } from &#39;@nestjs/common&#39;;
import { APP_FILTER } from &#39;@nestjs/core&#39;;

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}</code></pre>
<blockquote>
<p>DI 문제를 해결한 방식은 모듈에 상관없이 필터는 전역적입니다. 필터가 적용된 모듈을 선택하세요. 그리고, <code>useClass</code> 만이 커스텀 provider 를 등록하는 방법은 아닙니다. <a href="https://docs.nestjs.com/fundamentals/custom-providers">여기</a></p>
</blockquote>
<h3 id="catch-everything">Catch everything</h3>
<ul>
<li><code>@Catch()</code> 에 변수를 주지 않으면 됌</li>
</ul>
<pre><code class="language-typescript">import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from &#39;@nestjs/common&#39;;
import { HttpAdapterHost } from &#39;@nestjs/core&#39;;

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      statusCode: httpStatus,
      timestamp: new Date().toISOString(),
      path: httpAdapter.getRequestUrl(ctx.getRequest()),
    };

    httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
  }
}</code></pre>
<ul>
<li>플랫폼에 특정화된 객체를 쓰지 않고, <a href="https://docs.nestjs.com/faq/http-adapter">HTTP adapter</a> 를 통해 플랫폼-친화적 응답 전달</li>
</ul>
<blockquote>
<p>모든 것을 포착하는 예외 필터와 특정 유형에 바인딩된 필터를 결합할 때 특정 필터가 바인딩된 유형을 올바르게 처리할 수 있도록 &quot;Catch everything&quot; 필터를 먼저 선언해야 합니다.</p>
</blockquote>
<h3 id="inheritance">Inheritance</h3>
<p>내장된 <strong>전역 예외 필터</strong> 를 확장해 재정의 하고 싶은 경우</p>
<pre><code class="language-typescript">import { Catch, ArgumentsHost } from &#39;@nestjs/common&#39;;
import { BaseExceptionFilter } from &#39;@nestjs/core&#39;;

@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    super.catch(exception, host);
  }
}</code></pre>
<blockquote>
<p><code>BaseExceptionFilter</code> 를 확장하는 함수-레벨, 컨트롤러-레벨 필터는 new 로 인스턴스화 하면 안됩니다. 대신에, 프레임워크가 자동으로 인스턴스화 하게 하세요.</p>
</blockquote>
<p>전역 필터는 기본 필터를 확장할 수 있음</p>
<ol>
<li><p><code>HttpAdapter</code> 주입</p>
<pre><code class="language-typescript">async function bootstrap() {
const app = await NestFactory.create(AppModule);

const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));

await app.listen(3000);
}
bootstrap();</code></pre>
</li>
<li><p><code>APP_FILTER</code>
<a href="https://docs.nestjs.com/exception-filters#binding-filters">여기</a></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Middleware]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Middleware</link>
            <guid>https://velog.io/@paduck-96/NestJS-Middleware</guid>
            <pubDate>Tue, 11 Jun 2024 07:10:23 GMT</pubDate>
            <description><![CDATA[<p>Route 핸들러 이전에 호출되는 함수</p>
<ul>
<li><a href="https://expressjs.com/en/4x/api.html#req">request</a>, <a href="https://expressjs.com/en/4x/api.html#res">response</a> 객체에 접근 가능</li>
<li><code>next()</code> 미들웨어 함수에 접근 가능</li>
</ul>
<figure><img src="https://docs.nestjs.com/assets/Middlewares_1.png" /></figure>

<ul>
<li><a href="https://expressjs.com/en/guide/using-middleware.html">Express</a> 미들웨어와 거의 동일<blockquote>
<p>미들웨어 기능은 다음 작업을 수행할 수 있습니다.</p>
</blockquote>
<ul>
<li>어떤 코드라도 실행하세요.</li>
<li>요청 및 응답 개체를 변경합니다.</li>
<li>요청-응답 주기를 종료합니다.</li>
<li>스택에서 다음 미들웨어 함수를 호출합니다.</li>
<li>현재 미들웨어 기능이 요청-응답 주기를 종료하지 않으면 next()다음 미들웨어 기능으로 제어를 전달하기 위해 호출해야 합니다. 그렇지 않으면 요청이 중단된 상태로 유지됩니다.</li>
</ul>
</li>
</ul>
<p><code>@Injectable()</code> 데코레이터로 middleware Function Or Class 생성</p>
<ul>
<li><code>NestMiddleware</code> 인터페이스 를 implements<blockquote>
<p>Express미들웨어를 다르게 처리 하고 fastify다양한 메소드 서명을 제공합니다. 자세한 내용은 <a href="https://docs.nestjs.com/techniques/performance#middleware">여기</a>를 참조하세요 .</p>
</blockquote>
</li>
</ul>
<pre><code class="language-typescript">import { Injectable, NestMiddleware } from &#39;@nestjs/common&#39;;
import { Request, Response, NextFunction } from &#39;express&#39;;

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(&#39;Request...&#39;);
    next();
  }
}</code></pre>
<h3 id="dependency-injection">Dependency injection</h3>
<p>DI 가능하고,<code>constructor</code> 를 통해 처리</p>
<h3 id="applying-middleware">Applying middleware</h3>
<p>Module 내 <code>configure()</code> 함수를 통해 등록</p>
<ul>
<li><code>NestModule</code> 인터페이스를 implements</li>
</ul>
<pre><code class="language-typescript">import { Module, NestModule, MiddlewareConsumer } from &#39;@nestjs/common&#39;;
import { LoggerMiddleware } from &#39;./common/middleware/logger.middleware&#39;;
import { CatsModule } from &#39;./cats/cats.module&#39;;

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(&#39;cats&#39;);
      // 값 제한
      .forRoutes({ path: &#39;cats&#39;, method: RequestMethod.GET });
  }
}</code></pre>
<blockquote>
<p><code>async/await(예시, &quot;configure()&quot; 함수 내부에서 비동기 작업 처리)</code> 를 통해 <code>configure()</code> 함수가 비동기 함수가 될 수 있습니다.</p>
</blockquote>
<blockquote>
<p>Express 어댑터 사용 시, <code>body-parser</code> 패키지에서 <code>json</code> 과 <code>urlencoded</code> 를 등록합니다. <code>MiddlewareConsumer</code> 로 미들웨어를 customize 할 시, <code>NestFactory.create()</code> 내 <code>bodyParser</code> 를 <code>false</code> 로 설정해 전역 미들웨어 처리를 하지 않아야 합니다.</p>
</blockquote>
<h3 id="route-wildcards">Route wildcards</h3>
<p>경로 상 Pattern 지원</p>
<pre><code class="language-typescript">forRoutes({ path: &#39;ab*cd&#39;, method: RequestMethod.ALL });</code></pre>
<ul>
<li>하이픈(-) 과 점(.) 은 문자열 처리<blockquote>
<p><code>fastify</code> 패키지는 최신의 <code>path-to-regexp</code> 패키지를 쓰고 있는데, 이는 더 이상 * 를 지원하지 않습니다. 대신에, parameters 를 사용해야 합니다(예시, (.<em>) , `:splat</em>`).</p>
</blockquote>
</li>
</ul>
<h3 id="middleware-consumer">Middleware consumer</h3>
<p><code>MiddlewareConsumer</code> 는 Helper 클래스의 일종으로, 미들웨어를 관리하기 위한 내장 함수 제공</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Fluent_interface">fluent style</a> 로 연결됨</li>
<li>예시, <code>forRoutes()</code> 함수에는 단일-복수 문자열, <code>RouteInfo</code> 객체, 단일-복수 controller class 들어옴<pre><code class="language-typescript">import { Module, NestModule, MiddlewareConsumer } from &#39;@nestjs/common&#39;;
import { LoggerMiddleware } from &#39;./common/middleware/logger.middleware&#39;;
import { CatsModule } from &#39;./cats/cats.module&#39;;
import { CatsController } from &#39;./cats/cats.controller&#39;;
</code></pre>
</li>
</ul>
<p>@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController);
  }
}</p>
<pre><code>&gt; `apply()` 함수는 단일 미들웨어와 [복수 미들웨어](https://docs.nestjs.com/middleware#multiple-middleware)를 위한 복수 변수를 인자로 받습니다.

### Excluding routes
미들웨어가 적용되는 route 를 배제하고 싶을 때, `exclude()` 함수 사용
- 단일-복수 문자열, `RouteInfo` 객체 적용
```typescript
consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: &#39;cats&#39;, method: RequestMethod.GET },
    { path: &#39;cats&#39;, method: RequestMethod.POST },
    &#39;cats/(.*)&#39;,
  )
  .forRoutes(CatsController);</code></pre><blockquote>
<p><code>exclude()</code> 함수는 <code>path-to-regexp</code> 패키지에서 지원하는 와일드카드 매개 변수를 지원합니다.</p>
</blockquote>
<h3 id="functional-middleware">Functional middleware</h3>
<p>단일 함수 형태로도 적용 가능</p>
<pre><code class="language-typescript">import { Request, Response, NextFunction } from &#39;express&#39;;

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
};</code></pre>
<pre><code class="language-typescript">consumer
  .apply(logger)
  .forRoutes(CatsController);</code></pre>
<blockquote>
<p>미들웨어에 종속성이 필요하지 않은 경우 <code>함수형 미들웨어</code>를 고려해보세요</p>
</blockquote>
<h3 id="multiple-middleware">Multiple middleware</h3>
<p>순차적으로 적용되는 미들웨어는 단순 comma 로 <code>apply()</code> 함수에 적용 가능</p>
<pre><code class="language-typescript">consumer.apply(cors(),helmet(),logger).forRoutes(CatsController);</code></pre>
<h3 id="global-middleware">Global middleware</h3>
<p><code>INestApplication</code> 인스턴스에서 제공하는 <code>use()</code> 함수를 통해 모든 route 에 적용할 수 있음</p>
<pre><code class="language-typescript">const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);</code></pre>
<blockquote>
<p>글로벌 미드웨어에서는 DI 컨테이너에 접근할 수 없습니다. 이를 위해 <code>app.use()</code> 함수에 <code>함수형 미들웨어</code>를 적용할 수 있습니다. 혹은, 클래스 미들웨어를 <code>AppModule</code>(혹은 다른 모듈) 에서 <code>.forRoutes(&#39;*&#39;)</code> 와 함께 사용할 수 있습니다</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS Modules]]></title>
            <link>https://velog.io/@paduck-96/NestJS-Modules</link>
            <guid>https://velog.io/@paduck-96/NestJS-Modules</guid>
            <pubDate>Tue, 11 Jun 2024 05:33:08 GMT</pubDate>
            <description><![CDATA[<p><code>@Module()</code> 데코레이터로 연결되며
Application 구조를 정의하기 위한 메타데이터를 가지고 있음</p>
<figure><img src="https://docs.nestjs.com//assets/Modules_1.png" /></figure>


<p>Root 모듈(최상단) 을 통해 Nest 구조를 그림</p>
<ul>
<li>Module 과 Provider 의 관계와 의존성을 해결하기 위한 데이터임</li>
<li>Module 을 통해 구성 요소 구현</li>
<li>연관된 기능을 묶음으로 <strong>캡슐화</strong></li>
</ul>
<p><code>Module()</code> 내부 속성</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><code>providers</code></td>
<td>인스턴스화 되어 공유되는 모듈</td>
</tr>
<tr>
<td><code>controllers</code></td>
<td>현재 모듈 내 인스턴스화 되어야 할 controllers 집합</td>
</tr>
<tr>
<td><code>imports</code></td>
<td>현재 모듈 내 필요한, provider 를 export 하는 module</td>
</tr>
<tr>
<td><code>exports</code></td>
<td>현재 모듈에서 제공하는 provider 집합, 다른 모듈에서 현재 모듈의 필요한 부분으로 <strong>provider 자체</strong> 혹은 <strong>token(provide value)</strong></td>
</tr>
</tbody></table>
<ul>
<li>provider 자체적으로 캡슐화함</li>
<li>자체 모듈에 속하지 않거나, import 하지 않은 건 전달할 수 없음</li>
<li>export 을 <strong>interface or API</strong> 로 간주</li>
</ul>
<h3 id="feature-modules">Feature modules</h3>
<p>기능에 연관된 코드들끼리 모아놓는 것</p>
<ul>
<li>복잡성을 줄이고, <a href="https://en.wikipedia.org/wiki/SOLID">SOLID</a> 원칙 유지</li>
</ul>
<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>
<blockquote>
<p>CLI를 사용하여 모듈을 생성하려면 $ nest g module cats명령을 실행하기만 하면 됩니다.</p>
</blockquote>
<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>
<h3 id="shared-modules">Shared modules</h3>
<p>Module 은 기본 singleton 이며,
공통 instance 를 공유할 수 있음</p>
<ul>
<li>자동적으로 Shared modules 임<figure><img src="https://docs.nestjs.com/assets/Shared_Module_1.png" /></figure>
```typescript
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

</li>
</ul>
<p>@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService] // shared modules
})
export class CatsModule {}</p>
<pre><code>
### Module re-exporting

```typescript
@Module({
  imports: [CommonModule],
  exports: [CommonModule],
})
export class CoreModule {}</code></pre><p>이로 인해, CoerModule 을 통해 CommonModule 의 provider 에 접근이 가능</p>
<h3 id="dependency-injection">Dependency injection</h3>
<p>모듈 자체적으로도 provider 를 주입할 수 있음</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 {
  constructor(private catsService: CatsService) {}
}</code></pre>
<p>주로 Config 에 적용되는데, 문자 그대로 해당 Module 이 생성될 때 init 됨</p>
<ul>
<li>다만, <a href="https://docs.nestjs.com/fundamentals/circular-dependency">순환 종속성</a>으로 인해 Module 클래스 자체로 주입할 수 없음</li>
</ul>
<h3 id="global-modules">Global modules</h3>
<p>모듈 범위 내에서 캡슐화되기 때문에, 가져오지 않으면 사용할 수 없음
<code>@Global()</code> 데코레이터로 전역적으로 사용 가능</p>
<pre><code class="language-typescript">import { Module, Global } from &#39;@nestjs/common&#39;;
import { CatsController } from &#39;./cats.controller&#39;;
import { CatsService } from &#39;./cats.service&#39;;

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}</code></pre>
<ul>
<li>root or core 모듈에 <strong>한 번만</strong> 등록되어야 함<blockquote>
<p>모든 것을 글로벌하게 만드는 것은 좋은 디자인 결정이 아닙니다. 불필요한 상용구의 양을 줄이기 위해 전역 모듈을 사용할 수 있습니다. Imports은 일반적으로 소비자가 모듈의 API를 사용할 수 있도록 하는 데 선호되는 방법입니다.</p>
</blockquote>
</li>
</ul>
<h3 id="dynamic-modules">Dynamic modules</h3>
<p>provider 를 동적으로 등록할 수 있게 해주는 custom module</p>
<ul>
<li><a href="https://docs.nestjs.com/fundamentals/dynamic-modules">여기</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>