<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>woody_notes.log</title>
        <link>https://velog.io/</link>
        <description>개발자로 매일 한 걸음</description>
        <lastBuildDate>Wed, 06 Mar 2024 07:50:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>woody_notes.log</title>
            <url>https://images.velog.io/images/woody_/profile/65a8ab0f-15b9-4e66-b504-43f0f69f9cbe/Screenshot_20210926-161729_Gallery.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. woody_notes.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/woody_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HTTP VS WebSocket]]></title>
            <link>https://velog.io/@woody_/HTTP-VS-WebSocket</link>
            <guid>https://velog.io/@woody_/HTTP-VS-WebSocket</guid>
            <pubDate>Wed, 06 Mar 2024 07:50:54 GMT</pubDate>
            <description><![CDATA[<h3 id="websocket">WebSocket</h3>
<ul>
<li>Socket Connection을 유지한 채로 실시간으로 양방향 통신 혹은 데이터 전송이 가능한 프로토콜이다.</li>
<li>채팅 어플리케이션, SNS, 구글 Docs 등 많은 분야에서 사용되고 있다.</li>
</ul>
<h3 id="htttp와의-차이">HTTTP와의 차이</h3>
<ul>
<li>HTTP는 단방향 통신이였다. 클라이언트에서 서버로 Request를 보내면 서버는 클라이언트로 Response를 보내는 방식으로 동작한다. 또한 HTTP는 기본적으로 무상태(Stateless)이므로 상태를 저장하지 않는다.</li>
<li>하지만 웹소켓은 양방향 통신으로 연결이 이루어지면 클라이언트가 요청하지 않아도 서버에서 일방적으로 클라이언트로 데이터를 보낼 수 있다.</li>
<li>웹소켓은 HTTP와 다르게 상태(Stateful) 프로토콜이다. 즉, 클라이언트와 서버가 한 번 연결되면 같은 연결을 이용해 통신하므로 TCP 커넥션 비용을 아낄 수 있다.</li>
<li>Connection을 유지하고 있는 동안 request-response 방식의 통신이 아닌 양방향의 실시간 데이터 통신이 가능하다.</li>
</ul>
<h3 id="websocket의-동작-방식">WebSocket의 동작 방식</h3>
<p><img src="https://velog.velcdn.com/images/woody_/post/c57c611a-fe90-4c3b-88fe-f3d4a46bf77f/image.png" alt=""></p>
<ul>
<li>WebSocket은 HTTP 포트 80, HTTPS포트 443 위에서 동작한다.</li>
<li>즉, 최초 접속시에는 HTTP 프로토콜을 이용한 핸드셰이킹을 통하여 연결을 맺는다. 이때 HTTP 업그레이드 헤더를 사용하여 HTTP 프로토콜에서 WebSocket 프로토콜로 변경한다.</li>
<li>이후 연결이 맺어지면 어느 한쪽이 연결을 끊지 않는 이상 영구적인(persistent) 동일한 채널이 맺어지고, HTTP 프로토콜이 WebSocket 프로토콜로 변경된다.</li>
<li>이때 데이터를 암호화하기 위해 WSS 프로토콜 등을 이용할 수도 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NestJS] 파일 업로드]]></title>
            <link>https://velog.io/@woody_/NestJS-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C</link>
            <guid>https://velog.io/@woody_/NestJS-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C</guid>
            <pubDate>Sun, 31 Dec 2023 14:01:57 GMT</pubDate>
            <description><![CDATA[<p>package 설치</p>
<pre><code>npm i -D @types/multer</code></pre><h2 id="단일-파일-업로드">단일 파일 업로드</h2>
<p><code>FileInterceptor()</code> 는 두개의 인자를 가진다.</p>
<ul>
<li><code>fieldName</code><ul>
<li>파일을 담고 있는 HTML form의 필드 이름을 제공하는 문자열.</li>
<li><code>multipart/form-data</code> 에서 <code>name</code>에 해당하는 값</li>
</ul>
</li>
<li><code>options</code> : (option)  <code>MulterOptions</code> 타입의 객체 [<a href="https://github.com/expressjs/multer#multeropts">참조</a>]</li>
</ul>
<pre><code class="language-jsx">import { Controller, Post, UploadedFile, UseInterceptors} from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { FileInterceptor } from &#39;@nestjs/platform-express&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(FileInterceptor(&#39;file&#39;))
  upload(@UploadedFile() file: Express.Multer.File) {
    console.log(file);
  }
}</code></pre>
<h2 id="multeroptions">MulterOptions</h2>
<p>Option을 생략하면 파일은 디스크가 아니라 메모리에 저장되며 기본적으로 <code>Multer</code>는 이름의 중복을 방지하기 위해 내부적으로 임의로 이름을 생성하며 확장자는 붙어있지 않게 된다.</p>
<ul>
<li><code>dest</code> : 파일 저장 위치 설정. 지정한 path에 디렉토리가 존재하지 않으면 자동적으로 생성되고 확장자가 없는 상태에서 <code>Multer</code> 내부에서 임의로 생성한 이름으로 저장된다.</li>
<li><code>storage</code>  : <code>dest</code> 보다 세밀하게 upload를 제어하고 싶을때 사용. <code>DiskStorage</code> , <code>MemoryStorage</code>를 제공<ul>
<li><code>DiskStorage</code> 기준로 설명. <code>destination</code> 과 <code>filename</code>을 설정할 수 있다.<ul>
<li><code>destination</code> :  파일 저장 위치 설정. 문자열이나 함수로 설정할 수 있다. 문자열로 설정할 경우 해당 디렉토리가 존재 하지 않으면 자동적으로 생성된다. 문자열이나 함수를 전달하지 않으면 기본적으로 <code>os.tmpdir()</code> 로 설정된다.</li>
<li><code>filename</code> :  업로드한 파일의 이름을 설정하는 함수. 설정하지않으면 <code>Multer</code>는 확장자가 없는 32자의 난수 16진수 문자열을 생성한다.</li>
</ul>
</li>
</ul>
</li>
<li><code>fileFilter</code> : 업로드할 파일을 필터링하는 함수. 원하는 파일 형식만 허용하거나 거부할 수 있다.</li>
<li><code>limits</code> : 업로드할 파일의 크기 및 파일 개수 제한</li>
</ul>
<pre><code>| Key | Description | Default |
| --- | --- | --- |
| fieldNameSize | 최대 필드 이름 크기 | 100 bytes |
| fieldSize | 최대 필드 값 크기(바이트) | 1MB |
| fields | 비파일 필드의 최대 개수 | Infinity |
| fileSize | multipart forms의 최대 파일 크기(바이트) | Infinity |
| files | multipart forms의 최대 파일 필드의 수 | Infinity |
| parts | multipart/form-data의 파일과 필드의 개수 | Infinity |
| headerPairs | multipart form에서 파싱할 헤더의 key-value 쌍의 최대 개수 | 2000 |</code></pre><ul>
<li><code>preservePath</code> : 파일의 경로를 보존할지 여부 설정. 기본값은 false이며, true로 설정하면 클라이언트가 제공한 파일 경로를 그대로 사용한다.</li>
</ul>
<br/>    
코드 예시

<pre><code class="language-jsx">import { Controller, Post, UploadedFile, UseInterceptors } from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { FileInterceptor } from &#39;@nestjs/platform-express&#39;;
import { diskStorage } from &#39;multer&#39;;
import { extname } from &#39;path&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(
    FileInterceptor(&#39;image&#39;, {
      storage: diskStorage({
        destination: &#39;./uploads&#39;,
        filename(_, file, cb): void {
          const randomName = Array(32)
            .fill(null)
            .map(() =&gt; Math.round(Math.random() * 16).toString(16))
            .join(&#39;&#39;);
          return cb(null, `${randomName}${extname(file.originalname)}`);
        },
      }),
      fileFilter(req, file, callback) {
        if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
          return callback(new Error(&#39;Only .jpg, .jpeg, .png format allowed!&#39;), false);
        }
        callback(null, true);
      },
      limits: {
        fileSize: 1024 * 1024 * 5,
      },
    }),
  )
  upload(@UploadedFile() files: Express.Multer.File) {
    console.log(files);
  }
}</code></pre>
<h3 id="custom-interceptor를-사용하여-분리">Custom Interceptor를 사용하여 분리</h3>
<p>upload-interceoptor.ts</p>
<pre><code class="language-jsx">import { Injectable, NestInterceptor } from &#39;@nestjs/common&#39;;
import { CallHandler, ExecutionContext, RequestHandler } from &#39;@nestjs/common/interfaces&#39;;
import * as multer from &#39;multer&#39;;
import { diskStorage } from &#39;multer&#39;;
import { extname } from &#39;path&#39;;
import { Observable } from &#39;rxjs&#39;;

@Injectable()
export class UploadInterceptor implements NestInterceptor {
  private upload: RequestHandler;

  constructor() {
    this.upload = multer({
      storage: diskStorage({
        destination: &#39;./uploads&#39;,
        filename(_, file, cb): void {
          const randomName = Array(32)
            .fill(null)
            .map(() =&gt; Math.round(Math.random() * 16).toString(16))
            .join(&#39;&#39;);
          return cb(null, `${randomName}${extname(file.originalname)}`);
        },
      }),
      fileFilter(req, file, callback) {
        if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
          return callback(new Error(&#39;Only .jpg, .jpeg, .png format allowed!&#39;));
        }
        callback(null, true);
      },
      limits: {
        fileSize: 1024 * 1024 * 5,
      },
    }).single(&#39;image&#39;);
  }
  intercept(context: ExecutionContext, next: CallHandler&lt;any&gt;): Observable&lt;any&gt; | Promise&lt;Observable&lt;any&gt;&gt; {
    const req = context.switchToHttp().getRequest();
    return new Observable((observer) =&gt; {
      this.upload(req, null, async (err: any) =&gt; {
        if (err) observer.error(err);
        else next.handle().subscribe(observer);
      });
    });
  }
}</code></pre>
<p>앞선 컨트롤러 단에서 FileInterceptor를 사용하였을때는 해당 인터셉터들 사용하여 <code>multer</code> 함수를 따로 만들어 줄 필요이 제공받을 수 있는 반면 커스텀 인터셉터의 경우 <code>constructor</code> 내부에 <code>multer</code> 함수를 직접 정의한다.
<br/></p>
<p>file.controller.ts</p>
<pre><code class="language-jsx">import { Controller, Post, UploadedFile, UseInterceptors } from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { UploadInterceptor } from &#39;src/interceptor/upload-interceptor&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(UploadInterceptor)
  upload(@UploadedFile() files: Express.Multer.File) {
    console.log(files);
  }
}</code></pre>
<h2 id="file-validation">File validation</h2>
<ul>
<li>파일 크기나 파일의 mime-type과 같은 파일 메타 데이터의 유효성을 검사하는 기능</li>
<li><code>ParseFilePipeBuilder</code> 클래스를 사용하여 아래와 같이 각 유효성 검사기의 수동 인스턴스화를 피하고 옵션을 직접 전달할 수 있다.</li>
</ul>
<pre><code class="language-jsx">@Post(&#39;upload&#39;)
@UseInterceptors(FileInterceptor(&#39;file&#39;))
upload(
  @UploadedFile(
    new ParseFilePipeBuilder()
      .addFileTypeValidator({ fileType: /(mp4|png)$/ })
      .addMaxSizeValidator({ maxSize: 1000 * 1024 * 1024 })
      .build({ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }),
  )
  file: Express.Multer.File,
) {
  console.log(file);
}</code></pre>
<ul>
<li><code>addFileTypeValidator()</code> 의 <code>fileType</code>은 문자열 또는 정규식 타입으로 설정</li>
<li><code>addMaxSizeValidator()</code> 의 <code>maxSize</code> 와 <code>message</code> 설정<ul>
<li><code>maxSize</code> : 바이트 기준</li>
<li><code>message</code> : 오류 발생 메시지 설정</li>
</ul>
</li>
<li><code>build</code> 의 <code>errorHttpStatusCode</code> 는 오류 코드 설정</li>
</ul>
<h2 id="멀티-파일-업로드">멀티 파일 업로드</h2>
<h3 id="단일-field-name파일-배열으로-여러-파일-업로드">단일 field name(파일 배열)으로 여러 파일 업로드</h3>
<p><code>FilesInterceptor()</code> 는 세개의 인자를 가진다.</p>
<ul>
<li><code>fieldName</code><ul>
<li>파일을 담고 있는 HTML form의 필드 이름을 제공하는 문자열.</li>
<li><code>multipart/form-data</code> 에서 <code>name</code>에 해당하는 값</li>
</ul>
</li>
<li><code>maxCount</code> : (option) 최대 파일 수를 설정</li>
<li><code>options</code> : (option)  <code>MulterOptions</code> 타입의 객체 [<a href="https://github.com/expressjs/multer#multeropts">참조</a>]</li>
</ul>
<p><code>FilesInterceptor()</code> 를 사용하는 경우 <code>@UploadedFiles()</code> 데코레이터를 사용하여 request에서 파일을 추출한다.</p>
<pre><code class="language-jsx">import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFiles,
} from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { FilesInterceptor } from &#39;@nestjs/platform-express&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(FilesInterceptor(&#39;files&#39;))
  upload(
    @UploadedFiles()
    files: Array&lt;Express.Multer.File&gt;,
  ) {
    console.log(files);
  }
}</code></pre>
<h3 id="field-name이-각기-다른-여러-파일-업로드">field name이 각기 다른 여러 파일 업로드</h3>
<ul>
<li><code>FileFieldsInterceptor()</code> 데코레이터 이용하고, 이 데코레이터는 2개의 인자를 받는다.<ul>
<li><code>uploadedFields</code> : 객체의 배열로, 각 객체는 2개의 속성을 설정할 수 있다.<ul>
<li><code>name</code> 속성을 통해 필드 이름을 문자열 값으로 설정</li>
<li><code>maxCount</code> : (option) 최대 파일 수를 설정</li>
</ul>
</li>
<li><code>options</code> :  (option)  <code>MulterOptions</code> 타입의 객체 [<a href="https://github.com/expressjs/multer#multeropts">참조</a>]</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFiles,
} from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { FileFieldsInterceptor } from &#39;@nestjs/platform-express&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(
    FileFieldsInterceptor([
      { name: &#39;image&#39;, maxCount: 1 },
      { name: &#39;video&#39;, maxCount: 1 },
    ]),
  )
  upload(
    @UploadedFiles()
    files: {
      image?: Express.Multer.File[];
      video?: Express.Multer.File[];
    },
  ) {
    console.log(files);
  }
}</code></pre>
<h2 id="파일과-데이터-한번에-처리하기">파일과 데이터 한번에 처리하기</h2>
<p>file.controller.ts</p>
<pre><code class="language-jsx">import { Body, Controller, Post, UploadedFile, UseInterceptors } from &#39;@nestjs/common&#39;;
import { FileService } from &#39;./file.service&#39;;
import { UploadInterceptor } from &#39;src/interceptor/upload-interceptor&#39;;
import { CreateFileDto } from &#39;./dto/create-file.dto&#39;;

@Controller(&#39;file&#39;)
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @Post(&#39;upload&#39;)
  @UseInterceptors(UploadInterceptor)
  upload(@UploadedFile() files: Express.Multer.File, @Body() createFileDto: CreateFileDto) {
    console.log(files);
        console.log(createFileDto);
  }
}</code></pre>
<p>create-file.dto.ts</p>
<pre><code class="language-jsx">export class CreateFileDto {
  title: string;
  content: string;
  image: any;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/woody_/post/4e8fcc11-f0a3-4f01-b2f0-c29675b23841/image.png" alt=""></p>
<p>console.log</p>
<pre><code class="language-jsx">{
  fieldname: &#39;image&#39;,
  originalname: &#39;테스트 이미지.png&#39;,
  encoding: &#39;7bit&#39;,
  mimetype: &#39;image/png&#39;,
  destination: &#39;./uploads&#39;,
  filename: &#39;0fc031767a579eee74fce9d0d49e161f.png&#39;,
  path: &#39;uploads/0fc031767a579eee74fce9d0d49e161f.png&#39;,
  size: 20751
}
[Object: null prototype] { title: &#39;제목이요!&#39;, content: &#39;컨텐츠&#39; }</code></pre>
<p>프로트단에서는 <code>formdata.append()</code> 를 이용하여 key-value 형태로 데이터를 추가하면 된다.</p>
<hr>
<br/>
참고

<p><a href="https://docs.nestjs.com/techniques/file-upload#any-files">Documentation | NestJS - A progressive Node.js framework</a></p>
<p><a href="https://github.com/expressjs/multer#multeropts">GitHub - expressjs/multer: Node.js middleware for handling <code>multipart/form-data</code>.</a></p>
<p><a href="https://velog.io/@from_numpy/NestJS-File-upload-Use-of-Interceptor-feat.-nest%EB%A5%BC-%EB%B0%94%EB%9D%BC%EB%B3%B4%EB%8A%94-%EC%8B%9C%EA%B0%81">[NestJS] File-upload &amp; Use of Interceptor (feat. nest를 바라보는 시각..)</a></p>
<p><a href="https://velog.io/@mintmin0320/Nest.js%EC%97%90%EC%84%9C-FormData%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%9B%EA%B8%B0">[Nest] 이미지와 데이터 처리하기 (with. FormData)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NestJS] Rate limit]]></title>
            <link>https://velog.io/@woody_/NestJS-Rate-limit</link>
            <guid>https://velog.io/@woody_/NestJS-Rate-limit</guid>
            <pubDate>Sat, 16 Dec 2023 16:47:46 GMT</pubDate>
            <description><![CDATA[<h3 id="무차별-대입-공격brute-force-attack">무차별 대입 공격(brute-force attack)</h3>
<ul>
<li>인증 정보(사용자 이름과 비밀번호)를 알아내기 위해 공격자가 반복적, 체계적으로 매번 다른 사용자 이름과 비밀번호를 입력하는 방식의 공격</li>
<li>단순하지만 리소스를 많이 소비하는 시행착오 기반의 접근 방식으로, 보통 자동화된 툴이나 스크립트 또는 봇을 사용해 액세스 권한을 획득할 때까지 가능한 모든 조합을 대입한다.</li>
</ul>
<h2 id="rate-limiting">Rate Limiting</h2>
<ul>
<li>무차별 대입 공격으로부터 애플리케이션을 보호하는 일반적인 기법</li>
</ul>
<p>package 설치</p>
<pre><code class="language-bash">npm i @nestjs/throttler</code></pre>
<pre><code class="language-jsx">import { ThrottlerModule } from &#39;@nestjs/throttler&#39;;

@Module({
  imports: [
    ThrottlerModule.forRoot([
      // 60초동안 최대 요청 개수 10으로 제한
      {
        ttl: 6000,
        limit: 10,
      },
    ]),

    ...

  ],
})
export class AppModule {}
}</code></pre>
<h3 id="전역-설정">전역 설정</h3>
<pre><code class="language-jsx">import { ThrottlerModule } from &#39;@nestjs/throttler&#39;;

@Module({
  imports: [
    ThrottlerModule.forRoot([
      // 60초동안 최대 요청 개수 10으로 제한
      {
        ttl: 6000,
        limit: 10,
      },
    ]),

    ...

  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}
}</code></pre>
<h3 id="비활성화">비활성화</h3>
<p><code>@SkipThrottle()</code> 를 이용하여 전체 class 또는 단일 route에 Throttel 설정을 비활성화 할 수 있음</p>
<pre><code class="language-jsx">import { Controller } from &#39;@nestjs/common&#39;;
import { SkipThrottle } from &#39;@nestjs/throttler&#39;;

@SkipThrottle()
@Controller(&#39;api/user&#39;)
export class UserController {}</code></pre>
<p>비활성화한 class에서 특정 route를 비활성 무효 처리 할 수 도 있음</p>
<pre><code class="language-jsx">import { Controller } from &#39;@nestjs/common&#39;;
import { SkipThrottle } from &#39;@nestjs/throttler&#39;;

@SkipThrottle()
@Controller(&#39;api/user&#39;)
export class UserController {

    @SkipThrottle({default: false})
    dontSkip(){
        return &#39;Rate Limiting 설정 됨&#39;
    }

    doSkip(){
        return &#39;Rate Limiting 비활성화 됨&#39;
    }
}</code></pre>
<h3 id="override">Override</h3>
<ul>
<li><code>@Trottle()</code> 을 이용하여 글로벌로 설정된 limit, ttl 을 재정의하여 특정 class 또는 route에 설정할 수 있음</li>
</ul>
<pre><code class="language-jsx">@Throttle({ default: { limit: 3, ttl: 60000 } })
@Get()
findAll() {
  return &quot;Custom Rate Limiting 설정&quot;;
}</code></pre>
<h3 id="proxies">Proxies</h3>
<ul>
<li>애플리케이션이 프록시 서버 뒤에서 실행되는 경우 특정 HTTP 어댑터 옵션(express 및 fastify)에서 <code>trust proxy</code> 옵션을 확인하고 이를 활성화하세요.</li>
<li>이렇게 하면 <code>X-Forwarded-For</code> 헤더에서 원래 IP 주소를 가져올 수 있으며, <code>getTracker()</code> 메서드를 재정의하여 <code>req.ip</code>가 아닌 헤더에서 값을 가져올 수 있습니다.</li>
</ul>
<p>src/common/guard/throttler-behind-proxy.guard.ts</p>
<pre><code class="language-jsx">import { Injectable } from &#39;@nestjs/common&#39;;
import { ThrottlerGuard } from &#39;@nestjs/throttler&#39;;

@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
  // proxy 뒤에 서버가 있더라도 실제 클라이언트의 IP를 빼내는 방법
  protected getTracker(req: Record&lt;string, any&gt;): Promise&lt;string&gt; {
    // 배열이면 첫번째 원소가 클라이언트 IP
    return req.ips.length ? req.ips[0] : req.ip;
  }
}</code></pre>
<pre><code class="language-jsx">import { Controller, UseGuards } from &#39;@nestjs/common&#39;;
import { ThrottlerBehindProxyGuard } from &#39;src/common/guard/throttler-behind-proxy.guard&#39;;

@UseGuards(ThrottlerBehindProxyGuard)
@Controller(&#39;api/user&#39;)
export class UserController {}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker로 MySQL 설치]]></title>
            <link>https://velog.io/@woody_/Docker%EB%A1%9C-MySQL-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@woody_/Docker%EB%A1%9C-MySQL-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Wed, 05 Apr 2023 10:21:56 GMT</pubDate>
            <description><![CDATA[<h3 id="1-mysql-image-다운받기">1. MySQL Image 다운받기</h3>
<ul>
<li>명령어 : <code>docker pull mysql:버전</code><ul>
<li>버전 생략시 최신 버전으로 설치한다.
<img src="https://velog.velcdn.com/images/woody_/post/775762e6-faf2-4fee-9440-811ce3237f80/image.png" alt=""></li>
</ul>
</li>
<li>이미지 확인 명령어 : <code>docker images</code>
  <img src="https://velog.velcdn.com/images/woody_/post/f409cdcd-efcf-4cd6-ba05-63b920c2ff33/image.png" alt=""></li>
</ul>
<br>

<h3 id="2-다운-받은-image로-container-생성">2. 다운 받은 Image로 Container 생성</h3>
<p>명령어 : <code>docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=test1234 -e MYSQL_DATABASE=testDB -p 3306:3306 -v mysql-volume:/var/lib/mysql --restart=always --cap-add=sys_nice -d mysql</code></p>
<blockquote>
<p>💡 옵션</p>
</blockquote>
<ul>
<li><code>--name</code> : 컨테이너 이름 설정</li>
<li><code>-e</code> : 환경변수(PASSWORD, DATEBASE) 설정</li>
<li><code>-p</code> : 포트 설정(<code>외부포트: Docker 내부포트</code>)</li>
<li><code>-v</code> : volume 설정</li>
<li><code>--restart</code><ul>
<li><code>no</code> : container를 재시작 시키지 않는다. (default)</li>
<li><code>on-failure[:max-retries]</code> : container가 정상적으로 종료되지 않은 경우(exit code가 0이 아님)에만 재시작 시킨다. <code>max-retries</code>도 함께 주면 재시작 최대 시도횟수를 지정할 수 있고, 테스트 서버 등과 같은 리모트에 설정하면 좋을 것 같다.</li>
<li><code>always</code> : container를 항상 재시작시킨다. exit code 상관 없이 항상 재시작 된다.</li>
<li><code>unless-stopped</code> : container를 <code>stop</code>시키기 전 까지 항상 재시작 시킨다.</li>
</ul>
</li>
<li><code>--cap-add=sys_nice</code> : 컨테이너가 프로세스 나이스 값을 올리고, 실시간 스케줄링 정책을 설정하고, CPU 선호도 설정 및 기타 작업을 수행할 수 있도록 컨테이너에 CAP_SYS_NICE 기능을 부여한다.</li>
<li><code>-d</code> : Dispatch mode(백그라운드에서 실행)</li>
<li>마지막의 mysql은 이미지 이름</li>
</ul>
<br>

<ul>
<li>운영 중인 컨테이너 확인 : <code>docker ps</code>
<img src="https://velog.velcdn.com/images/woody_/post/3aac7760-17e3-4788-984c-001c7e76ad61/image.png" alt=""></li>
</ul>
<br>

<h3 id="3-container에-접속하기">3. Container에 접속하기</h3>
<ul>
<li><p>Container 내부 접속 명령어 : <code>docker exec -it mysql-container bash</code></p>
</li>
<li><p>Mysql 관리자로 접속</p>
<ul>
<li><code>mysql -u root -p</code></li>
<li>비밀번호 입력
<img src="https://velog.velcdn.com/images/woody_/post/ba604672-9b94-4721-b107-19531fcefdc6/image.png" alt=""></li>
</ul>
</li>
<li><p>Database 확인</p>
<ul>
<li><code>SHOW DATABASES;</code>
<img src="https://velog.velcdn.com/images/woody_/post/b0bc9aaa-e1a7-42b8-a45e-30d5ccaac24c/image.png" alt=""></li>
</ul>
</li>
<li><p>컨테이너 실행 시에 등록한 testDB를 확인할 수 있다.</p>
</li>
<li><p>추가로 Database 생성 : <code>CREATE DATABASE 이름</code></p>
</li>
</ul>
<br>

<h3 id="4-mysql-gui-관리도구로-열기">4. MySQL GUI 관리도구로 열기</h3>
<ul>
<li>보통 MySQL Workbench를 많이 사용하나 HeidiSQL로 열겠다.
<img src="https://velog.velcdn.com/images/woody_/post/c99feed2-7e1d-4f48-bb8d-8049c1930c61/image.png" alt="">
<img src="https://velog.velcdn.com/images/woody_/post/afa89fa0-4d6f-45de-a6cb-f1464ae9b8a9/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker 설치(Window 11)]]></title>
            <link>https://velog.io/@woody_/Docker-%EC%84%A4%EC%B9%98Window-11</link>
            <guid>https://velog.io/@woody_/Docker-%EC%84%A4%EC%B9%98Window-11</guid>
            <pubDate>Wed, 05 Apr 2023 10:14:26 GMT</pubDate>
            <description><![CDATA[<h2 id="1-wsl2-활성화">1. WSL2 활성화</h2>
<ul>
<li>Docker는 리눅스를 기반으로 동작한다. 따라서 윈도우 환경에서 리눅스를 사용할 수 있도록 도와주는 WSL2를 활성화 해야한다.</li>
<li>WSL(Windows Subsystem for Linux)는 VM과 같은 도구 없이 윈도우 환경에서 Linux를 사용할 수 있도록 도와준다.</li>
<li>PowerShell을 Bash처럼 사용하고, Linux 명령어(sed, awk, vim, apt 등)를 사용할 수 있을 뿐만 아니라 Linux 커널도 사용이 가능하다.</li>
</ul>
<ol>
<li><p>관리자 권한으로 Windows PowerShell 실행</p>
<pre><code class="language-bash"> # Windows SubSystem Linux를 활성화시키는 명령어
 &gt; dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

 # VirtualMachinePlatform 기능을 활성화시키는 명령어 : WSL2 버전에 필요한 명령어
 &gt; dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart</code></pre>
<p> <img src="https://velog.velcdn.com/images/woody_/post/5ab9432a-87a6-4b71-8aa8-216913575123/image.png" alt=""></p>
</li>
</ol>
<ol start="2">
<li><p>재부팅 후  Windows PowerShell 실행 후 wsl 명령어 실행해 설치 되었는지 확인. 아래와 같이 나오면 성공
 <img src="https://velog.velcdn.com/images/woody_/post/7c3e478e-ecf3-4ef4-92e7-d0ba2484b12e/image.png" alt=""></p>
</li>
<li><p>Microsoft Store를 통해서 Ubuntu를 설치
 <img src="https://velog.velcdn.com/images/woody_/post/a1e33e71-f025-47db-90fb-fc76ae464d9c/image.png" alt=""></p>
</li>
<li><p>WSL2 리눅스 커널 업데이트</p>
<ul>
<li><strong><a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi">https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi</a></strong></li>
<li>다운로드 후 설치</li>
</ul>
</li>
<li><p>Ubuntu 실행하여 username, password 설정
 <img src="https://velog.velcdn.com/images/woody_/post/bdd30274-ba7b-4031-a5ef-bf49aef36237/image.png" alt=""></p>
</li>
<li><p>Windows PowerShell에서 ubuntu가 정상적으로 설치되었는지 확인</p>
<pre><code class="language-bash"> wsl -l -v
 # -l : 현재 설치된 리눅스 배포판 명령어 확인
 # -v : 버전 확인</code></pre>
<p> <img src="https://velog.velcdn.com/images/woody_/post/82373c16-acba-4c79-823b-a3173009be90/image.png" alt=""></p>
<ul>
<li><p>VERSION이 1인 경우</p>
<ul>
<li><p>wsl 버전 변경 :  <code>wsl --set-version Ubuntu-버전</code>
<img src="https://velog.velcdn.com/images/woody_/post/bb6b2d5e-e140-4007-a8f8-deedccd1b387/image.png" alt=""></p>
</li>
<li><p>모든 WSL이 기본적으로 WSL2를 사용하도록 설정 : <code>wsl --set-default-version 2</code>
<img src="https://velog.velcdn.com/images/woody_/post/c4a055b8-7d40-40f8-a57d-d027d5e7d0cb/image.png" alt="">        </p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-docker-설치">2. Docker 설치</h2>
<ol>
<li><p>Docker Desktop for Windows 실행파일을 다운 및 설치</p>
<ul>
<li><a href="https://docs.docker.com/desktop/install/windows-install/">https://docs.docker.com/desktop/install/windows-install/</a>
<img src="https://velog.velcdn.com/images/woody_/post/ac6e5690-ad8c-4923-b4f0-6eb72bc555eb/image.png" alt="">
<img src="https://velog.velcdn.com/images/woody_/post/851924ab-4c76-480e-9d81-03f6a4194bab/image.png" alt="">    </li>
<li>실행
  <img src="https://velog.velcdn.com/images/woody_/post/929e8794-7273-4eaa-9cfd-bcf9a061f69b/image.png" alt="">        </li>
<li>Accept
  <img src="https://velog.velcdn.com/images/woody_/post/65b23228-aec2-43d7-ba99-aacc27699d0e/image.png" alt="">        </li>
</ul>
</li>
<li><p>설치 확인</p>
<ul>
<li>Windows PowerShell 실행</li>
<li>docker 버전 확인 : <code>docker -v</code> 명령어 입력
<img src="https://velog.velcdn.com/images/woody_/post/b9667f7b-ca59-42cb-9729-794edd996404/image.png" alt=""></li>
</ul>
</li>
</ol>
<hr>
<p>참조</p>
<p><a href="https://axce.tistory.com/110?category=1030982">WSL2 사용 설정(윈도우에서 Ubuntu 사용하는 방법)</a></p>
<p><a href="https://axce.tistory.com/121">[Docker] 윈도우 도커 설치방법(window 11)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[var, let, const(변수 선언 방식, scope, 호이스팅)]]></title>
            <link>https://velog.io/@woody_/var-let-const</link>
            <guid>https://velog.io/@woody_/var-let-const</guid>
            <pubDate>Sat, 01 Apr 2023 11:15:15 GMT</pubDate>
            <description><![CDATA[<h2 id="변수-선언-방식-요약">변수 선언 방식 요약</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>중복 선언</th>
<th>재할당</th>
<th>scope</th>
<th>호이스팅</th>
</tr>
</thead>
<tbody><tr>
<td>var</td>
<td>O</td>
<td>O</td>
<td>함수 레벨 스코프 (function-level scope)</td>
<td>초기 값 undefined 할당한 것을 호이스팅한 후 해당 코드에서 작성한 값 할당</td>
</tr>
<tr>
<td>let (ES6)</td>
<td>X</td>
<td>O</td>
<td>블록 레벨 스코프 (block-level scope)</td>
<td>호이스팅시 초기값 할당 X</td>
</tr>
<tr>
<td>const (ES6)</td>
<td>X</td>
<td>X</td>
<td>블록 레벨 스코프(block-level scope)</td>
<td>호이스팅시 초기값 할당 X</td>
</tr>
</tbody></table>
<h3 id="var">var</h3>
<ul>
<li>중복 선언과 재할당이 가능하며 마지막에 할당된 값이 변수에 저장된다.</li>
<li>이런 특징으로  자율성이 생기만 어떤 부분에서 값이 변경되었는지 파악하기 힘들어지게 될 수있다.</li>
</ul>
<pre><code class="language-jsx">    var greeting = &#39;hello&#39;;
    console.log(greeting);  // hello

    var greeting = &#39;hi&#39;;
    console.log(greeting);  // hi

    greeting = &#39;how are you?&#39;;
    console.log(greeting);  // how are you?</code></pre>
<ul>
<li>function-level scope<ul>
<li>함수 내에서 선언된 변수는 함수 내에서만 유효하다.</li>
<li>함수 내에서는 블록 내외부에 관계 없이 유효하다.</li>
<li>함수 외부에서는 참조 할 수 없다.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">    function func() {
      if (true) {
        var a = &#39;a&#39;;
        console.log(a); // a
      }
      console.log(a); // a
    }

    func();

    // console.log(a); // ReferenceError: a is not defined</code></pre>
<h3 id="let">let</h3>
<ul>
<li>중복 선언 불가, 재할당 가능</li>
<li>block-level scope<ul>
<li>함수 , if문, for문 등의 모든 코드 블록 내부에서 선언된 코드 블록 내에서만 유효</li>
<li>코드 블록 외부에서는 참조할 수 없다.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// 중복 선언 불가, 재할당 가능
let greeting = &#39;hello&#39;;
console.log(greeting);  // hello

// let greeting = &#39;hi&#39;;  // 오류 발생 : 중복 선언 불가

greeting = &#39;how are you?&#39;;
console.log(greeting);  // how are you?</code></pre>
<pre><code class="language-jsx">function func() {
  if (true) {
    let a = &#39;a&#39;;
    console.log(a); // a
  }
  // console.log(a); // ReferenceError: a is not defineds
}

func();

// console.log(a); // ReferenceError: a is not defined</code></pre>
<h3 id="const">const</h3>
<ul>
<li>중복 선언 불가, 재할당 가능</li>
<li>block-level scope</li>
</ul>
<pre><code class="language-jsx">// 중복 선언 불가, 재할당 불가
const greeting = &#39;hello&#39;;
console.log(greeting);  // hello

const greeting = &#39;hi&#39;;  // 오류 발생 : 중복 선언 불가

greeting = &#39;how are you?&#39;;  // 오류 발생 : 재할당 불가
console.log(greeting);  // how are you?</code></pre>
<h2 id="호이스팅hoisting">호이스팅(Hoisting)</h2>
<ul>
<li><p>호이스트(Hoist)의 뜻은 무언가를 들어 올리거나 끌어 올리는 동작을 설명합니다.</p>
</li>
<li><p>자동차를 강에서 올린다(Hoist)라고 할 수 있습니다.</p>
</li>
<li><p>JS에서 호이스팅은 코드가 실행되기 전에 변수 및 함수 선언(이름)이 로컬 범위(유효 범위)의 맨 위로 들어올려지거나 끌어올려지는 경우를 설명한다.</p>
</li>
</ul>
<h3 id="var-선언문-호이스팅">var 선언문 호이스팅</h3>
<ul>
<li>위로  호이스팅되고, undefined 값 할당</li>
</ul>
<pre><code class="language-jsx">console.log(greeting);  // undefined
var greeting = &#39;hello&#39;;</code></pre>
<ul>
<li>위의 예에서 콘솔로그에 undefined가 반환되는 이유는 호이스팅 때문이다.</li>
<li>JS 인터프리터는 변수 생성의 <strong>선언(var greeting) 단계</strong> 및 <strong>할당(= ‘hello’) 단계</strong>를 분할한다.</li>
<li><strong>선언 부분</strong>은 코드가 실행 되기 전에 현재 범위의 맨 위로 호이스팅되고 초기에 <strong>undefined 값이 할당</strong> 된다.</li>
<li>즉, 초기화 되기 전에 greeting 변수를 사용할 수 있다.</li>
</ul>
<h3 id="함수-선언문-호이스팅">함수 선언문 호이스팅</h3>
<ul>
<li>함수 선언전에 호출하면 올바로 출력이 된다.</li>
</ul>
<pre><code class="language-jsx">func(); // hoisting test

function func() {
  console.log(&#39;hoisting test&#39;);
}</code></pre>
<h3 id="let--const-선언문-호이스팅">let / const 선언문 호이스팅</h3>
<ul>
<li>let 또는 const로 변수를 선언하면 실제로 변수는 여전히 호이스팅 된다.</li>
<li>var 와 차이점은 var는 실제 할당 값이 할당되기 전까지 undefined 값이 할당 된다.</li>
<li>하지만 <strong>let / const 는 변수 초기에 어떤 값도 할당되지 않는다.</strong><ul>
<li><strong>초기화 되지 않음</strong></li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">console.log(greeting);  // ReferenceError: Cannot access &#39;greeting&#39; before initialization
let greeting = &#39;hello&#39;;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] n^2 배열 자르기 (JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-n2-%EB%B0%B0%EC%97%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-JS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-n2-%EB%B0%B0%EC%97%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0-JS</guid>
            <pubDate>Sat, 25 Mar 2023 10:48:10 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/87390">코딩테스트 연습 &gt; 월간 코드 챌린지 시즌3 &gt; n^2 배열 자르기
</a></p>
<hr>
<p>문제 설명 </p>
<p>정수 n, left, right가 주어집니다. 다음 과정을 거쳐서 1차원 배열을 만들고자 합니다.</p>
<p>n행 n열 크기의 비어있는 2차원 배열을 만듭니다.
i = 1, 2, 3, ..., n에 대해서, 다음 과정을 반복합니다.
1행 1열부터 i행 i열까지의 영역 내의 모든 빈 칸을 숫자 i로 채웁니다.
1행, 2행, ..., n행을 잘라내어 모두 이어붙인 새로운 1차원 배열을 만듭니다.
새로운 1차원 배열을 arr이라 할 때, arr[left], arr[left+1], ..., arr[right]만 남기고 나머지는 지웁니다.
정수 n, left, right가 매개변수로 주어집니다. 주어진 과정대로 만들어진 1차원 배열을 return 하도록 solution 함수를 완성해주세요.</p>
<hr>
<p>제한사항</p>
<ul>
<li>1 ≤ n ≤ 107</li>
<li>0 ≤ left ≤ right &lt; n2</li>
<li>right - left &lt; 105</li>
</ul>
<hr>
<p>입출력 예</p>
<table>
<thead>
<tr>
<th align="center">n</th>
<th align="center">left</th>
<th align="center">right</th>
<th align="center">result</th>
</tr>
</thead>
<tbody><tr>
<td align="center">3</td>
<td align="center">2</td>
<td align="center">5</td>
<td align="center">[3,2,2,3]</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">7</td>
<td align="center">14</td>
<td align="center">[4,3,3,3,4,4,4,4]</td>
</tr>
</tbody></table>
<hr>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1-2차원-배열에-값이-채워지는-규칙">1. 2차원 배열에 값이 채워지는 규칙</h3>
<ul>
<li><p>n = 3, left = 2, right = 5로 생성되는 배열이 아래와 같고 좌표는 (x, y)라고 하겠습니다.</p>
<pre><code class="language-jsx">  [
      [1, 2, 3],
      [2, 2, 3],
      [3, 3, 3],
  ]</code></pre>
<table>
<thead>
<tr>
<th>x</th>
<th>y</th>
<th>값</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>0</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>1</td>
<td>0</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>2</td>
<td>0</td>
<td>3</td>
</tr>
<tr>
<td>2</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
<td>3</td>
</tr>
</tbody></table>
</li>
<li><p>규칙이 보이시나요?</p>
</li>
<li><p>바로 바로 x와 y 중 큰 수에 1을 더한 것이 값이 됩니다.</p>
</li>
<li><p>코드로는 <code>Max(x, y) + 1</code> 게 되죠.</p>
</li>
</ul>
<h3 id="2-결과-배열-생성">2. 결과 배열 생성</h3>
<ul>
<li><p>제한사항으로 n이 10^7 까지 될 수 있기 때문에 2차원 배열 전체를 만들어 놓고 left, right로 추출하는 것은 효율성이 좋지 않아요.</p>
</li>
<li><p>그럼 left ~ right 범위에 해당하는 값만 생성하는 방법은 없을까?</p>
</li>
<li><p>좌표를 통해서 값을 생성하는 것은 위에서 알아 보았으니 범위내에서 좌표를 찾을 수 있다면 완성되겟죠?</p>
</li>
<li><p>위의 예시에서  범위는 left(2) ~ right(5)가 되고 n = 3일 때 우리가 찾아야 하는 좌표는 [0, 2], [1, 0], [1, 1], [1, 2]이 됩니다.</p>
</li>
<li><p>범위내의 값을 i라고 할때</p>
<table>
<thead>
<tr>
<th>i</th>
<th>n</th>
<th>몫</th>
<th>나머지</th>
</tr>
</thead>
<tbody><tr>
<td>2</td>
<td>3</td>
<td>0</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>4</td>
<td>3</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>5</td>
<td>3</td>
<td>1</td>
<td>2</td>
</tr>
</tbody></table>
</li>
<li><p>규칙이 보이시나요?</p>
</li>
<li><p>x = Math.floor(left / n)</p>
</li>
<li><p>y = left % n</p>
</li>
</ul>
<h3 id="코드">코드</h3>
<pre><code class="language-jsx">function solution(n, left, right) {
    var answer = [];

    for (let i = left; i &lt;= right; i++) {
        answer.push(Math.max(Math.floor(i / n), i % n) + 1)
    }

    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 위장 (JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%9C%84%EC%9E%A5-JS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%9C%84%EC%9E%A5-JS</guid>
            <pubDate>Wed, 01 Mar 2023 09:51:19 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42578">코딩테스트 연습&gt;해시&gt;위장</a></p>
<hr>
<br>

<h3 id="제한사항">제한사항</h3>
<ul>
<li><span style = "color:indianred">clothes : [의상 이름, 의상 종류]로 이루어진 2차원 배열</span></li>
<li>스파이가 가진 의상의 수는 1개 이상 30개 이하입니다.</li>
<li>같은 이름을 가진 의상은 존재하지 않습니다.</li>
<li>clothes의 모든 원소는 문자열로 이루어져 있습니다.</li>
<li>모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 &#39;_&#39; 로만 이루어져 있습니다.</li>
<li><span style = "color:indianred">return : 서로 다른 옷의 조합의 수</span></li>
<li><span style = "color:indianred">최소 하루에 한 개의 의상 입는다.</span></li>
</ul>
<br>


<h3 id="풀이">풀이</h3>
<blockquote>
<p>EX)
[[&quot;yellow_hat&quot;, &quot;headgear&quot;], [&quot;blue_sunglasses&quot;, &quot;eyewear&quot;], [&quot;green_turban&quot;, &quot;headgear&quot;]]</p>
</blockquote>
<ol>
<li>각 의상 종류 중 1개 선택할 수 있는 경우의 수 + 1(선택 안할 경우)<ul>
<li>headgear : 2 + 1 / eyewear : 1 + 1</li>
</ul>
</li>
<li>각 의상 종류의 경우의 수 곱한다.<ul>
<li>3 * 2 = 6</li>
</ul>
</li>
<li>전부 선택 하지 않을 경우 빼기<ul>
<li>6 -1 = 5</li>
</ul>
</li>
</ol>
<br>


<h3 id="코드">코드</h3>
<pre><code class="language-javascript">function solution(clothes) {
    let answer = 1;
    let obj = {}
    clothes.forEach(cloth =&gt; obj[cloth[1]] = (obj[cloth[1]] | 0) + 1)

    let values = Object.values(obj);

    // 각 의상 종류중 1개 선택할 수 있는 경우의 수 + 1(선택 안할 경우)
    values.forEach(value =&gt; answer *= (value + 1))

    // 모두 선택 안할 경우 빼기
    return answer - 1;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 [1차] 캐시
(JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-1%EC%B0%A8-%EC%BA%90%EC%8B%9CJS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-1%EC%B0%A8-%EC%BA%90%EC%8B%9CJS</guid>
            <pubDate>Mon, 27 Feb 2023 11:31:28 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/17680">코딩테스트 연습&gt;2018 KAKAO BLIND RECRUITMENT&gt;[1차] 캐시</a></p>
<hr>
<br>

<h3 id="입력-형식">입력 형식</h3>
<ul>
<li><code>cacheSize</code> : 캐시 크기</li>
<li>정수, 범위 0 ≤ <code>cacheSize</code> ≤30</li>
<li><code>cities</code> : 도시 이름으로 이뤄진 문자열 배열<ul>
<li>길이 : 100,000</li>
<li>원소 : 공백, 숫자, 특수 문자 등이 없는 영문자. 대소문 구분 X, 최대 20자</li>
</ul>
</li>
</ul>
<h3 id="출력-형식">출력 형식</h3>
<ul>
<li>입력된 도시이름을 배열 순서대로 처리할 때, “총 실행시간” 출력</li>
</ul>
<h3 id="조건">조건</h3>
<ul>
<li><code>LRU(Least Recently Used)</code> 사용</li>
<li><code>cache hit</code> : 실행 시간 <code>1</code></li>
<li><code>cache miss</code> : 실행 시간 <code>5</code></li>
</ul>
<br>

<h3 id="lruleast-recently-used">LRU(Least Recently Used)</h3>
<ul>
<li><strong>가장 오랫동안 참조되지 않은 페이지를 교체하는 방식</strong></li>
<li>캐시가 가득 찻을때 가장 오랫동안 참조 되지 않는 페이지 제거</li>
<li>새로 참조 할때 마다 리스트 맨 앞에 페이지 번호 추가<ul>
<li>리스트에 해당 페이지 번호가 없다면 맨 앞에 페이지 번호 추가 → <code>Miss</code></li>
<li>리스트에 해당 페이지 번호가 있다면 기존 것 제거하고 맨 앞에 페이지 번호 추가 → <code>Hit</code></li>
<li>캐시가 3이라고할때, 리스트 길이가 4이상이 되면 맨마지막 요소 제거</li>
</ul>
</li>
</ul>
<blockquote>
<p>Cache Hit : CPU가 참조하고자하는 메모리가 캐시에 존재하는 경우
Cache Miss  : CPU가 참조하고자하는 메모리가 캐시에 존재하지 않은 경우</p>
</blockquote>
<blockquote>
<p>Hit Ratio : Cache Hit / (Cache Hit + Cache Miss)
Miss Ratio : Cache Miss / (Cache Hit + Cache Miss)
<strong>※ 위의 예시 : Hit Ratio = 3/8, Miss Ratio = 5/8</strong></p>
</blockquote>
<br>

<h3 id="코드">코드</h3>
<h4 id="배열로-구현">배열로 구현</h4>
<pre><code class="language-javascript">function solution(cacheSize, cities) {
    let answer = 0;
    let LUR = [];
    for (let city of cities) {
        city = city.toLowerCase();
        let i = LUR.indexOf(city);
        if (i !== -1) {
            LUR.splice(i, 1);
            answer += 1;
        } else {
            answer += 5;
        }
        LUR.push(city);
        if (LUR.length &gt; cacheSize) LUR.shift();
    }
    return answer;
}</code></pre>
<h4 id="이중-연결-리스트doubly-linked-list로-구현">이중 연결 리스트(Doubly Linked List)로 구현</h4>
<pre><code class="language-javascript">function solution(cacheSize, cities) {
    let answer = 0;
    let dll = new DLL();

    for (let city of cities) {
        city = city.toLowerCase();
        let isNode = dll.get(city);
        if (isNode) {
            if (isNode.index !== 0) {
                dll.remove(isNode.node);
                dll.unshift(city);
            }
            answer += 1;
        } else {
            answer += 5;
            dll.unshift(city);
        }

        if (dll.length &gt; cacheSize) dll.pop();
    }

    return answer;
}

class Node {
    constructor(val) {
        this.val = val;
        this.prev = null;
        this.next = null;
    }
}

class DLL {
    constructor() {
        this.head = null;
        this.tail = null;
        this.length = 0;
    }
    pop() {
        let node = this.tail;
        if (!this.head) return undefined;
        if (this.length === 1) {
            this.head = null;
            this.tail = null;
        } else {
            node.prev.next = null;
            this.tail = node.prev;
            node.prev = null;
        }
        this.length--;
        return node;
    }

    unshift(val) {
        let newNode = new Node(val);
        if (this.length === 0) {
            this.head = newNode;
            this.tail = newNode;
        } else {
            this.head.prev = newNode;
            newNode.next = this.head;
            this.head = newNode;
        }
        this.length++;
        return this;
    }

    get(val) {
        let index = 0;
        let cur = this.head;
        while (index &lt; this.length) {
            if (cur.val === val) return { &#39;node&#39;: cur, &#39;index&#39;: index };
            cur = cur.next;
            index++;
        }
        return null;
    }

    remove(node) {
        if (this.length === 0) return undefined;
        if (node.next === null) {
            this.pop();
        } else {
            let removedNode = node;
            let prevNode = node.prev;
            let nextNode = node.next;
            prevNode.next = nextNode;
            nextNode.prev = prevNode;

            removedNode.next = null;
            removedNode.prev = null;
            this.length--;
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 H-Index(JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-H-IndexJS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-H-IndexJS</guid>
            <pubDate>Sat, 25 Feb 2023 18:49:06 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42747#fn1">코딩테스트 연습&gt;정렬&gt;H-Index</a></p>
<hr>
<h2 id="h-index">H-Index</h2>
<ul>
<li>과학자의 생산성과 영향력을 나타내는 지표</li>
<li>n편 중, <code>h번 이상 인용된 논문</code>이 <code>h편 이상</code>이고</li>
<li><code>나머지 논문</code>이 h번 이하 인용되었다면</li>
<li><code>h의 최댓값</code>이 이 과학자의 H-Index입니다.</li>
</ul>
<br>

<h3 id="예시">예시</h3>
<table>
<thead>
<tr>
<th align="center">No.</th>
<th align="center">피인용 수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="center">77</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center">6</td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">5</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">5</td>
</tr>
<tr>
<td align="center">5</td>
<td align="center">4</td>
</tr>
<tr>
<td align="center">6</td>
<td align="center">2</td>
</tr>
</tbody></table>
<p>조건 : <code>h번 이상 인용된 논문</code>이 <code>h편 이상</code> 일때 <code>h의 최대값</code></p>
<ol>
<li>1번 이상 인용 : 6개</li>
<li>2번 이상 인용 : 6개</li>
<li>3번 이상 인용 : 5개</li>
<li>4번 이상 인용 : 5개 → <code>h의 최대값</code> = 4</li>
<li>5번 이상 인용 : 4개</li>
<li>6번 이상 인용 : 2개</li>
</ol>
<br>


<h3 id="규칙">규칙</h3>
<ol>
<li>피인용 수를 내림차순으로 정렬하고 순번(No.)를 준다.</li>
<li>h = <code>피인용 수</code>가 <code>순번</code> 보다 <strong>크거나 같은 것 중 가장 큰 순번</strong></li>
</ol>
<br>


<h3 id="코드">코드</h3>
<pre><code class="language-javascript">function solution(citationsn) {
    citationsn.sort((a, b) =&gt; b - a);
    return citationsn.filter((e, i) =&gt; e &gt;= i + 1).length
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 점프와 순간 이동(JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%A0%90%ED%94%84%EC%99%80-%EC%88%9C%EA%B0%84-%EC%9D%B4%EB%8F%99JS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%A0%90%ED%94%84%EC%99%80-%EC%88%9C%EA%B0%84-%EC%9D%B4%EB%8F%99JS</guid>
            <pubDate>Fri, 24 Feb 2023 12:55:33 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12980">코딩테스트 연습&gt;Summer/Winter Coding(~2018)&gt;점프와 순간 이동</a></p>
<h3 id="조건">조건</h3>
<ul>
<li><code>점프</code> : K 칸 점프 -&gt; <code>건전지 K만큼 소모O</code></li>
<li><code>순간이동</code> : (현재까지 온 거리) x 2 에 해당하는 위치 이동 -&gt; <code>건전지 소모X</code></li>
<li>이동하려는 거리 N이 주어졌을 때 → return 건전지 사용량의 최솟값<ul>
<li>정확히 N 까지 가야한다.</li>
</ul>
</li>
</ul>
<br>

<h3 id="규칙">규칙</h3>
<table>
<thead>
<tr>
<th align="center">N</th>
<th align="center">건전지 사용량의 최솟값</th>
<th align="center">n / 2</th>
<th align="center">나머지</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="center">1</td>
<td align="center">1 /2 = 0</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center">1</td>
<td align="center">2 /2 = 0</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">2</td>
<td align="center">3 /2 = 1</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">1</td>
<td align="center">4 / 2 = 2</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">5</td>
<td align="center">2</td>
<td align="center">5 / 2 = 2</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">6</td>
<td align="center">2</td>
<td align="center">6 / 2 = 3</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">7</td>
<td align="center">3</td>
<td align="center">7 / 2 = 3</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">8</td>
<td align="center">1</td>
<td align="center">8 / 2 = 4</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">9</td>
<td align="center">2</td>
<td align="center">9 / 2 = 4</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">10</td>
<td align="center">2</td>
<td align="center">10 / 2 = 5</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">11</td>
<td align="center">3</td>
<td align="center">11 / 2 = 5</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">12</td>
<td align="center">2</td>
<td align="center">12 / 2 = 6</td>
<td align="center">0</td>
</tr>
</tbody></table>
<ul>
<li><span style = "color:indianred"><strong>나머지가 0이면 <code>n이 몫인 건전지 사용량의 최솟값</code></strong></span></li>
<li><span style = "color:indianred"><strong>아니면 <code>n이 몫인 건전지 사용량의 최솟값 + 1(나머지)</code></strong></span></li>
</ul>
<p>ex)
<img src="https://velog.velcdn.com/images/woody_/post/ed38fe71-9483-4d69-aef3-58343cd93595/image.png" alt="예시"></p>
<br>

<h3 id="코드">코드</h3>
<pre><code class="language-javascript">function solution(n) {
  let div = Math.floor(n / 2);

  if (div === 0 &amp;&amp; n % 2 === 0) return 0;
  if (div === 0 &amp;&amp; n % 2 !== 0) return 1;

  if (n % 2 === 0) return solution(div);
  else return solution(div) + 1;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 멀리 뛰기(JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EB%A9%80%EB%A6%AC-%EB%9B%B0%EA%B8%B0JS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EB%A9%80%EB%A6%AC-%EB%9B%B0%EA%B8%B0JS</guid>
            <pubDate>Fri, 24 Feb 2023 01:34:02 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12914">코딩테스트 연습&gt;연습문제&gt;멀리 뛰기</a></p>
<hr>
<br>

<h3 id="조건">조건</h3>
<ul>
<li>1칸 또는 2칸 뛸 수 있다.</li>
<li>멀리뛰기 할 칸의 수 n</li>
<li>return <code>n까지 가는 경우의 수 % 1234567</code></li>
</ul>
<br>

<h3 id="패턴">패턴</h3>
<ul>
<li><p>n = 1 → return 1</p>
<ul>
<li>1</li>
</ul>
</li>
<li><p>n =2 → return 2</p>
<ul>
<li>1 1</li>
<li>2</li>
</ul>
</li>
<li><p>n = 3 → return 3</p>
<ul>
<li>1 1 1</li>
<li>2 1</li>
<li>1 2</li>
</ul>
</li>
<li><p>n = 4 → return 5</p>
<ul>
<li>1 1 1 1</li>
<li>1 2 1</li>
<li>1 1 2</li>
<li>2 1 1</li>
<li>2 2</li>
</ul>
</li>
<li><p>n = 5 → return 8</p>
<ul>
<li>1 1 1 1 1</li>
<li>2 1 1 1</li>
<li>1 2 1 1</li>
<li>1 1 2 1</li>
<li>1 1 1 2</li>
<li>2 2 1</li>
<li>2 1 2</li>
<li>1 2 2</li>
</ul>
</li>
<li><p>n = 6 → return 13</p>
<ul>
<li>1 1 1 1 1 1</li>
<li>2 1 1 1 1</li>
<li>1 2 1 1 1</li>
<li>1 1 2 1 1</li>
<li>1 1 1 2 1</li>
<li>1 1 1 1 2</li>
<li>2 2 1 1</li>
<li>2 1 2 1</li>
<li>2 1 1 2</li>
<li>1 2 2 1</li>
<li>1 2 1 2</li>
<li>1 1 2 2</li>
<li>2 2 2</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><p><span style = "color:indianred">요약하자면 피보나치 수열의 패턴을 가지고 있다.</span></p>
<table>
<thead>
<tr>
<th>n</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>5</td>
<td>8</td>
</tr>
<tr>
<td>6</td>
<td>13</td>
</tr>
</tbody></table>
</li>
<li><p>피보나치 수열의 공식이 <code>F(n) = F(n-1) + F(n-2)</code> 일때</p>
</li>
<li><p><code>F(n) % 1234567</code>  =  <code>(F(n-1) + F(n-2)) % 1234567</code><br>= <code>(F(n-1) % 1234567 + F(n-2) % 1234567) % 1234567</code></p>
</li>
</ul>
<br>

<h3 id="코드">코드</h3>
<pre><code class="language-javascript">function solution(n) {
  let arr = [0, 1]
  for (let i = 1; i &lt;= n; i++) {
    arr.push(arr[arr.length - 1] + arr[arr.length - 1 - 1] % 1234567)
  }
  return arr[arr.length - 1]
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] level 2 예상 대진표(JS)]]></title>
            <link>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9CJS</link>
            <guid>https://velog.io/@woody_/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-level-2-%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9CJS</guid>
            <pubDate>Tue, 21 Feb 2023 16:15:55 GMT</pubDate>
            <description><![CDATA[<p>문제 링크 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/12985">코딩테스트 연습&gt;2017 팁스타운&gt;예상 대진표</a></p>
<hr>
<br>

<h2 id="접근-방법">접근 방법</h2>
<ul>
<li>1 ~ N 차례로 배정</li>
<li>1&lt;-&gt;2, 3&lt;-&gt;4 .. 승부</li>
<li>이기면 1 ~ N/2번으로 차례대로 재배정</li>
<li>return  A, B가 만나는 라운드</li>
<li>A, B 는 무조건 이김</li>
</ul>
<h3 id="규칙1">규칙1</h3>
<table>
<thead>
<tr>
<th>번호</th>
<th>몫</th>
<th>나머지</th>
<th>합</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>1 / 2 = 0</td>
<td>1 % 2 = 1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>2 / 2 = 1</td>
<td>2 % 2 = 0</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>3 / 2 = 1</td>
<td>3 % 2 = 1</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>4 / 2 = 2</td>
<td>4 % 2 = 0</td>
<td>2</td>
</tr>
<tr>
<td>5</td>
<td>5 / 2 = 2</td>
<td>5 % 2 = 1</td>
<td>3</td>
</tr>
<tr>
<td>6</td>
<td>6 / 2 = 3</td>
<td>6 % 2 = 0</td>
<td>3</td>
</tr>
<tr>
<td>7</td>
<td>7 / 2 = 3</td>
<td>7 % 2 = 1</td>
<td>4</td>
</tr>
<tr>
<td>8</td>
<td>8 / 2 = 4</td>
<td>8 % 2 = 0</td>
<td>4</td>
</tr>
</tbody></table>
<ul>
<li>몫과 나머지의 합으로 다음 차례를 알 수 있다.</li>
</ul>
<h3 id="코드1">코드1</h3>
<pre><code class="language-javascript">function solution(n, a, b) {
    let count = 0;
    while (a !== b) {
        a = Math.floor(a / 2) + a % 2
        b = Math.floor(b / 2) + b % 2
        count++
    }
    return count;
}</code></pre>
<hr>
<br>

<h3 id="규칙2">규칙2</h3>
<table>
<thead>
<tr>
<th>번호</th>
<th>몫</th>
<th>올림</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>1 / 2 = 0.5</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>2 / 2 = 1</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>3 / 2 = 1.5</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>4 / 2 = 2</td>
<td>2</td>
</tr>
<tr>
<td>5</td>
<td>5 / 2 = 2.5</td>
<td>3</td>
</tr>
<tr>
<td>6</td>
<td>6 / 2 = 3</td>
<td>3</td>
</tr>
<tr>
<td>7</td>
<td>7 / 2 = 3.5</td>
<td>4</td>
</tr>
<tr>
<td>8</td>
<td>8 / 2 = 4</td>
<td>4</td>
</tr>
</tbody></table>
<ul>
<li>규칙1을 다시 생각해 보면 한번에 올림을 해줘도 같은 결과를 찾을 수 있다.</li>
</ul>
<h3 id="코드2">코드2</h3>
<pre><code class="language-javascript">function solution(n, a, b) {
    let count = 0;
    while (a !== b) {
        a = Math.ceil(a / 2)
        b = Math.ceil(b / 2)
        count++
    }
    return count;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 유클리드 거리, 맨하탄 거리 구하기]]></title>
            <link>https://velog.io/@woody_/Temp-Title</link>
            <guid>https://velog.io/@woody_/Temp-Title</guid>
            <pubDate>Wed, 15 Feb 2023 12:06:57 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/woody_/post/9e4dbef8-2252-4929-9dc6-6bba566242af/image.jpeg" alt=""></p>
<h2 id="유클리드-거리euclidean-distance">유클리드 거리(Euclidean Distance)</h2>
<ul>
<li><p>L2 Distance라고 불린다.</p>
</li>
<li><p><code>초록색 선</code> : 두점사이의 직선 거리(최단 거리)</p>
</li>
<li><p>공식
<img src="https://velog.velcdn.com/images/woody_/post/452f5294-6452-4798-a12b-838f392cd353/image.png" alt=""></p>
<ul>
<li>각 차원의 차를 제곱해서 모두 더한 값의 제곱근</li>
</ul>
</li>
<li><p>n차원에서 두 점 사이의 거리
<img src="https://velog.velcdn.com/images/woody_/post/0072b763-431b-4c6c-8db2-af39ef08df22/image.png" alt=""></p>
</li>
<li><p>JS 코드</p>
<pre><code class="language-jsx">  let a = [0, 4];
  let b = [3, 0];

  function euclideanDistance(a, b) {
      let distance = 0;
      for (let i = 0; i &lt; a.length; i++) {
          distance += Math.pow(Math.abs(a[i] - b[i]), 2)
      }
      return Math.sqrt(distance)
  }

  euclideanDistance(a, b)  // 5</code></pre>
</li>
</ul>
<br>

<h2 id="맨하탄-거리manhattan-distance">맨하탄 거리(Manhattan Distance)</h2>
<ul>
<li>L1 Distance라고 불린다.</li>
<li>도시의 골목길(블록)을 걸을 때와 모습이 유사하여 이름이 붙었다.</li>
<li><code>빨강색</code> , <code>파랑색</code>, <code>노랑색</code> 선 : 거리 동일하다.</li>
<li>공식
<img src="https://velog.velcdn.com/images/woody_/post/7077545d-2775-4f30-b689-ca4af19eaa81/image.png" alt=""><ul>
<li>각 차원의 절대값 차의 합</li>
</ul>
</li>
<li>n차원에서 두 점 사이의 거리
<img src="https://velog.velcdn.com/images/woody_/post/d3f27b68-f1eb-4860-8572-b81a864131fb/image.png" alt=""></li>
<li>JS 코드</li>
</ul>
<pre><code class="language-jsx">    let a = [0, 4];
    let b = [3, 0];

    function manhattanDistance(a, b) {
        let distance = 0;
        for (let i = 0; i &lt; a.length; i++) {
            distance += Math.abs(a[i] - b[i])
        }
        return distance
    }

    manhattanDistance(a, b)  // 7</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS]  에라토스테네스의 체 - 소수 판별]]></title>
            <link>https://velog.io/@woody_/JS-%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4-%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</link>
            <guid>https://velog.io/@woody_/JS-%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98-%EC%B2%B4-%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</guid>
            <pubDate>Sun, 12 Feb 2023 16:11:49 GMT</pubDate>
            <description><![CDATA[<h4 id="소수">소수</h4>
<ul>
<li>자신 보다 작은 두개의 자연수를 곱하여 만들 수 없는 1보다 큰 자연수</li>
<li>1과 자기 자신만을 약수로 갖는 수</li>
<li>1과 자신으로만 나누어 지는 수</li>
<li>ex) 2, 3, 5, 7…</li>
</ul>
<br>

<h3 id="에라토스테네스의-체">에라토스테네스의 체</h3>
<p><img src="https://velog.velcdn.com/images/woody_/post/75b134af-c6ac-48c4-bbe4-4beed3e24503/image.gif" alt=""></p>
<ul>
<li><p>알고리즘</p>
<ol>
<li><p>2부터 구하구자 하는 모든 수 나열</p>
</li>
<li><p>2는 소수이므로 오른쪽에 2쓴다.</p>
</li>
<li><p>자신을 제외한 2의 배수 모두 지운다.</p>
</li>
<li><p>남은 수 가운데 3은 소수이므로 오른쪽에 쓴다.</p>
</li>
<li><p>자신을 제외한 3의 배수 모두 지운다.</p>
</li>
<li><p>남은 수 가운데 5은 소수이므로 오른쪽에 쓴다.</p>
</li>
<li><p>자신을 제외한 5의 배수 모두 지운다.</p>
</li>
<li><p>남은 수 가운데 7은 소수이므로 오른쪽에 쓴다.</p>
</li>
<li><p>자신을 제외한 7의 배수 모두 지운다.</p>
</li>
<li><p>위의 과정 반복하면 구하는 구간의 모든 소수가 남는다.</p>
</li>
</ol>
</li>
<li><p>위 그림의 경우, 11^2 &gt; 120이므로 11보다 작은 수의 배수들만 지워도 충분한다. 즉 120보다 작거나 같은 수 가운데 2, 3, 4, 5의 배수를 지우고 남는 수는 모두 소수이다.</p>
</li>
</ul>
<br>

<ul>
<li>알고리즘 요약<blockquote>
<ul>
<li>1보다 큰 모든 자연수는 소수의 곱으로 이루워졌다.</li>
<li>자연수 n이 √n보다 작은 소수들로 나눠떨어지지 않으면 자연수 n은 소수이다.</li>
</ul>
</blockquote>
</li>
</ul>
<br>

<h4 id="js로-에라토스테네스의-체-구현">JS로 에라토스테네스의 체 구현</h4>
<pre><code class="language-jsx">function solution(n) {
  // idx 0, 1은 false / 나머지는 true인 길이가 n(idx : 0 ~ n)인 배열 생성
  let arr = Array(n + 1).fill(true).fill(false, 0, 2)

  // i : n의 제곱근까지 loop
  for (let i = 2; i &lt;= Math.sqrt(n); i++) {
    if (arr[i]) {
      // i의 배수 false로 변경
      for (let j = i * i; j &lt;= n; j += i) {
        arr[j] = false;
      }
    }
  }
  return arr
}</code></pre>
<ul>
<li>소수개수 및 소수 반환하기</li>
</ul>
<pre><code class="language-jsx">let isPrime = solution(120)
// 소수 개수 구하기
let countPrime = isPrime.filter(e =&gt; e).length  // 30
// 소수 구하기
let prime = isPrime.map((v, i) =&gt; v ? i : 0).filter(e =&gt; e)
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113</code></pre>
<br>

<hr>
<ul>
<li><p>참조</p>
<ul>
<li><p><a href="https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4">에라토스테네스의 체 - 위키백과, 우리 모두의 백과사전</a></p>
</li>
<li><p><a href="https://mine-it-record.tistory.com/507">[알고리즘] 소수 찾기 - 에라토스테네스의 체 by javascript</a></p>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 숫자를 배열로 변경]]></title>
            <link>https://velog.io/@woody_/JS-%EC%88%AB%EC%9E%90%EB%A5%BC-%EB%B0%B0%EC%97%B4%EB%A1%9C-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@woody_/JS-%EC%88%AB%EC%9E%90%EB%A5%BC-%EB%B0%B0%EC%97%B4%EB%A1%9C-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Wed, 08 Feb 2023 11:41:01 GMT</pubDate>
            <description><![CDATA[<h3 id="1--arrayfrom">1.  <code>Array.from()</code></h3>
<ul>
<li>유사배열객체(array-like object), 반복가능객체(iterable object)를 얕은 복사(shallow copy)하여 새로운 배열 객체를 만들어 준다.</li>
</ul>
<br>

<ul>
<li>예제 1( <code>정수</code> → <code>문자열 배열</code>)</li>
</ul>
<pre><code class="language-jsx">const num = 1234;
// 정수 -&gt; 문자열로 변경
const str = String(num);
// 문자열 -&gt; 배열로 변경
const newArr = Array.from(str)
console.log(newArr);  // [ &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39; ]</code></pre>
<br>


<ul>
<li>예제 2 (<code>정수</code> → <code>정수 배열</code>)</li>
</ul>
<pre><code class="language-jsx">const num = 1234;
// 정수 -&gt; 문자열로 변경
const str = String(num);
// element를 문자열에서 정수로 변경
const mapfn = (arg) =&gt; Number(arg)
// 문자열 -&gt; 배열로 변경
const newArr = Array.from(str, mapfn)
console.log(newArr);  // [ 1, 2, 3, 4 ]</code></pre>
<br>

<h3 id="2-split-map">2. <code>split()</code>, <code>map()</code></h3>
<ul>
<li><p><code>split(&#39;&#39;)</code> : 문자열을 잘라서 배열로 반환</p>
</li>
<li><p><code>map(callback)</code> : 각 element를 callback 함수에 적용한 값을 배열로 반환</p>
<br>
</li>
<li><p>예제 1 (<code>정수</code> → <code>정수 배열</code>)</p>
<pre><code class="language-jsx">const num = 1234;
// 정수 -&gt; 문자열로 변경
const str = String(num);
// element를 문자열에서 정수로 변경
const mapfn = (arg) =&gt; Number(arg)
// 문자열 -&gt; 배열로 변경
const newArr = str.split(&#39;&#39;).map(mapfn)
console.log(newArr);  // [ 1, 2, 3, 4 ]</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Set 객체 배열로 변환]]></title>
            <link>https://velog.io/@woody_/JS-Set-%EA%B0%9D%EC%B2%B4-%EB%B0%B0%EC%97%B4%EB%A1%9C-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@woody_/JS-Set-%EA%B0%9D%EC%B2%B4-%EB%B0%B0%EC%97%B4%EB%A1%9C-%EB%B3%80%ED%99%98</guid>
            <pubDate>Wed, 08 Feb 2023 10:26:27 GMT</pubDate>
            <description><![CDATA[<h3 id="1-arrayfrom">1. <code>Array.from()</code></h3>
<ul>
<li>유사배열객체(array-like object), 반복가능객체(iterable object)를 얕은 복사(shallow copy)하여 새로운 배열 객체를 만들어 준다.</li>
</ul>
<pre><code class="language-jsx">const set = new Set([1, 2, 3]); // Set(3) { 1, 2, 3 }
const arr = Array.from(set)     // [ 1, 2, 3 ]</code></pre>
<br>

<h3 id="2-spread-operator-전개-연산자">2. <code>Spread Operator (전개 연산자)</code></h3>
<ul>
<li>배열이나 문자열과 같이 반복 가능한 객체를 하나씩 펼처서 리턴한다.</li>
</ul>
<pre><code class="language-jsx">const set = new Set([1, 2, 3]); // Set(3) { 1, 2, 3 }
const arr = [...set]     // [ 1, 2, 3 ]</code></pre>
<br>


<h3 id="3-foreach">3. <code>forEach()</code></h3>
<ul>
<li>반복문 활용</li>
</ul>
<pre><code class="language-jsx">const set = new Set([1, 2, 3]); // Set(3) { 1, 2, 3 }
const arr = []
set.forEach(ele =&gt; {
  arr.push(ele)
})
console.log(arr);  // [ 1, 2, 3 ]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 정규 표현식]]></title>
            <link>https://velog.io/@woody_/JS-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@woody_/JS-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Wed, 08 Feb 2023 09:59:26 GMT</pubDate>
            <description><![CDATA[<ul>
<li>문자열에서 특정 문자 조합을 찾기 위한 패턴</li>
<li>조건문 없이 패턴을 정의 하고 테스트 할 수 있다.</li>
</ul>
<h2 id="생성하는-2가지-방법">생성하는 2가지 방법</h2>
<ol>
<li><p>정규식을 리터털로 선언</p>
<pre><code class="language-jsx"> cosnt regex = /abc/;</code></pre>
<ul>
<li>스크립트가 로드될 때 컴파일되므로, 정규식 문자열이 변하지 않는다면 리터럴 방식으로 선언하는 것이 성능상 조금 더 이점이 있다.</li>
</ul>
</li>
<li><p>정규식 생성자 RegExp를 이용해서 생성</p>
<pre><code class="language-jsx"> cosnt regex = new RegExp(&quot;abc&quot;);</code></pre>
<ul>
<li>정규식이 실행 시점에 컴파일되기 때문에 정규식을 동적으로 변화시켜야 할 때 유용</li>
</ul>
</li>
</ol>
<br>

<h2 id="특수문자-이스케이프escape">특수문자 이스케이프(Escape)</h2>
<ul>
<li>몇몇 특수문자들은 특수한 용도로 사용되는 예억어이기 때문에 백슬래시(<code>\</code>)를 문자 앞에 붙여 문자 그대로 해석되도록한다.</li>
</ul>
<pre><code class="language-jsx">const regex1 = /\*/;
const regex2 = /\?/;
const regex3 = /\./;
const regex4 = /\\/;</code></pre>
<br>


<h2 id="플래그flags">플래그(Flags)</h2>
<ul>
<li>고급 검색을 위한 옵션 설정</li>
<li>플래그는 리터럴 방식인지 생성자 방식인지에 따라 설정하는 방식이 다르다.</li>
</ul>
<pre><code class="language-jsx">const regex1 = /패턴/flags;
const regex2 = new Regex(/패턴/, flags);</code></pre>
<ul>
<li><p><code>g</code> (Global)</p>
<ul>
<li><p>패턴과 일치하는 모든 것을 검색. 해당 플래그가 없으면 일치하는 첫 번째 결과만 반환</p>
<pre><code class="language-jsx">const str = &#39;banana&#39;;

// g 플래그 없는 경우 : 최초에 발견되 문자만 반환
str.match(/a/)  // [ &#39;a&#39;, index: 1, input: &#39;banana&#39;, groups: undefined ]
// g 플래그 있는 경우 : 모든 결과 배열로 반환
str.match(/a/g)  // [ &#39;a&#39;, &#39;a&#39;, &#39;a&#39; ]</code></pre>
</li>
</ul>
</li>
<li><p><code>m</code> (Multiline)</p>
<ul>
<li>문자열의 행이 바뀌더라도 계속 패턴 검색</li>
</ul>
</li>
<li><p><code>i</code> (Case Insensitive)</p>
<ul>
<li><p>대, 소문자 구별 없이 검색</p>
<pre><code class="language-jsx">const str = &#39;banAna&#39;
str.match(/a/gi) // [ &#39;a&#39;, &#39;A&#39;, &#39;a&#39; ]</code></pre>
</li>
</ul>
</li>
</ul>
<br>


<h2 id="메타-문자meta-charactor">메타 문자(Meta Charactor)</h2>
<h3 id="문자-그룹charactor-set---">문자 그룹(Charactor Set, <code>[]</code> , <code>[^]</code>)</h3>
<ul>
<li>대괄호(<code>[]</code>)로 묶인 긍정 문자 그룹은 대괄호 내부의 문자열 중 하나라도 일치하는 경우를 의미</li>
</ul>
<pre><code class="language-jsx">const str = &#39;gray and grey&#39;

str.match(/gr[ae]y/g);   // [ &#39;gray&#39;, &#39;grey&#39; ]</code></pre>
<br>


<ul>
<li>대괄호 내부에 들어가는 특수문자는 메타 문자로 취급되지 않기 때문에 별도로 이스케이프 하지 않아도 된다.</li>
</ul>
<pre><code class="language-jsx">const str = &#39;!@#$%^&amp;*.&#39;

// [] 안에 있는 특수문자는 리터럴 특수문자 취급된다.
str.match(/[#.]/g);   // [ &#39;#&#39;, &#39;.&#39; ]</code></pre>
<br>


<ul>
<li>범위 지정 구문(dash, <code>-</code> )를 이용하여 간략하게 표기할 수 있다.</li>
</ul>
<pre><code class="language-jsx">const regex1 = /[A-Z]/; // === /[ABCDEFGHIJKLMNOPQRSTUVWXYZ]/
const regex2 = /[0-9]/; // === /[0123456789]/
const regex3 = /[가-힣]/;</code></pre>
<br>


<ul>
<li><code>[^]</code> 부정 문자 그룹(Negative Character Set)을 이용하여 대괄호에 해당하지 않는 문자열을 매칭할 수 있다.</li>
</ul>
<pre><code class="language-jsx">const str = &#39;2023 09 14 Monday&#39;;
// 숫자만 찾기
str.match(/[0-9]/g)  // [&#39;2&#39;, &#39;0&#39;, &#39;2&#39;, &#39;3&#39;, &#39;0&#39;, &#39;9&#39;, &#39;1&#39;, &#39;4&#39;]
// 숫자 제외하고 찾기
str.match(/[^0-9]/g) // [&#39; &#39;, &#39; &#39;, &#39; &#39;, &#39;M&#39;, &#39;o&#39;, &#39;n&#39;, &#39;d&#39;, &#39;a&#39;, &#39;y&#39;]</code></pre>
<br>


<h3 id="10진수-문자digit-character-d-d">10진수 문자(Digit Character, <code>\d</code>, <code>\D</code>)</h3>
<ul>
<li><code>\d</code> : <code>[0-9]</code> 와 동일</li>
<li><code>\D</code> : <code>[^0-9]</code> 와 동일</li>
</ul>
<pre><code class="language-jsx">const str = &#39;2023 09 14 Monday&#39;;
// 숫자만 찾기
str.match(/\d/g)  // [&#39;2&#39;, &#39;0&#39;, &#39;2&#39;, &#39;3&#39;, &#39;0&#39;, &#39;9&#39;, &#39;1&#39;, &#39;4&#39;]
// 숫자 제외하고 찾기
str.match(/\D/g) // [&#39; &#39;, &#39; &#39;, &#39; &#39;, &#39;M&#39;, &#39;o&#39;, &#39;n&#39;, &#39;d&#39;, &#39;a&#39;, &#39;y&#39;]</code></pre>
<br>


<h3 id="단어-문자word-character--w-w">단어 문자(Word Character , <code>\w</code>, <code>\W</code>)</h3>
<ul>
<li><code>\w</code> : <code>[0-9a-zA-z_]</code> 와 동일(숫자, 영어 대소문자, 언더스코어(<code>_</code>)를 포함하는 그룹</li>
<li><code>\W</code> : <code>[^0-9a-zA-z_]</code></li>
</ul>
<pre><code class="language-jsx">const str = &#39;2023 09 14 Monday&#39;;
// 단어만 찾기
str.match(/\w/g)  // [&#39;2&#39;, &#39;0&#39;, &#39;2&#39;, &#39;3&#39;, &#39;0&#39;, &#39;9&#39;, &#39;1&#39;, &#39;4&#39;, &#39;M&#39;, &#39;o&#39;, &#39;n&#39;, &#39;d&#39;, &#39;a&#39;, &#39;y&#39;]
// 단어 아닌 것만 찾기
str.match(/\W/g)  // [&#39; &#39;, &#39; &#39;, &#39; &#39;]</code></pre>
<br>


<h3 id="공백-문자whitespace-character-s-s">공백 문자(Whitespace Character, <code>\s</code>, <code>\S</code>)</h3>
<ul>
<li><code>\s</code> : 스페이스, 탭, 폼피드, 줄 바꿈 문자 등을 포함한 하나의 공백 검색</li>
<li><code>\S</code> : <code>\s</code>의 역집합</li>
</ul>
<pre><code class="language-jsx">const str = &#39;2023 09 14 Monday&#39;;
// 공백만 찾기
str.match(/\s/g)  // [&#39; &#39;, &#39; &#39;, &#39; &#39;]
// 공백이 아닌것만 찾기
str.match(/\S/g)  // [&#39;2&#39;, &#39;0&#39;, &#39;2&#39;, &#39;3&#39;, &#39;0&#39;, &#39;9&#39;, &#39;1&#39;, &#39;4&#39;, &#39;M&#39;, &#39;o&#39;, &#39;n&#39;, &#39;d&#39;, &#39;a&#39;, &#39;y&#39;]</code></pre>
<br>


<h3 id="임의의-문자any-character-">임의의 문자(Any Character, <code>.</code>)</h3>
<p><code>.</code> : 개행 문자를 제외한 모든 단일 문자에 해당</p>
<pre><code class="language-jsx">const str = &#39;2023 09 14 Monday&#39;;

str.match(/./g)  
// [&#39;2&#39;, &#39;0&#39;, &#39;2&#39;, &#39;3&#39;, &#39; &#39;, &#39;0&#39;, &#39;9&#39;, &#39; &#39;, &#39;1&#39;, &#39;4&#39;, &#39; &#39;, &#39;M&#39;, &#39;o&#39;, &#39;n&#39;, &#39;d&#39;, &#39;a&#39;, &#39;y&#39;]

str.match(/../g)
// [&#39;20&#39;, &#39;23&#39;, &#39; 0&#39;, &#39;9 &#39;, &#39;14&#39;, &#39; M&#39;, &#39;on&#39;, &#39;da&#39;]</code></pre>
<br>


<h2 id="앵커anchor---">앵커(Anchor, <code>^</code>, <code>$</code> )</h2>
<ul>
<li>메타 문자들은 어떤 문자열을 일치 시킬 것인지에 관한 것이였다면</li>
<li>앵커는 입력된 정규식이 <strong>어떤 특정 위치에서 동작할지를 제한하는 역할</strong>의 문자</li>
<li>즉, 위치만 제한할 뿐 검색 결과에는 포함되지 않는다.</li>
<li><code>^</code> : 패턴 시작 앵커<ul>
<li>해당 정규식이 줄의 시작 부분인지 확인하는 역할</li>
<li><strong>문자 그룹(<code>[]</code>)에서의 부정 그룹을 나타내기 위한 캐럿(<code>^</code>)과는 다른 용법!</strong></li>
</ul>
</li>
<li><code>$</code> : 패턴 종료 앵커<ul>
<li>해당 정규식이 줄의 마지막 부분인지 확인하는 역할</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">const str1 = &#39;aaa&#39;;
const str2 = &#39;bab&#39;;

// str의 시작이 a 인지 검색
console.log(str1.match(/^a/))  // [ &#39;a&#39;, index: 0, input: &#39;aaa&#39;, groups: undefined ]
console.log(str2.match(/^a/))  // null
// str의 마지막이이 a 인지 검색
console.log(str1.match(/a$/))  // [ &#39;a&#39;, index: 2, input: &#39;aaa&#39;, groups: undefined ]
console.log(str2.match(/a$/))  // null</code></pre>
<br>


<h2 id="단어-경계word-boundaries-b-b">단어 경계(Word Boundaries, <code>\b</code>, <code>\B</code>)</h2>
<ul>
<li><code>\b</code> : 다른 단어 문자(<code>\w</code>)가 앞이나 뒤에 등장하지 않는 위치임을 나타내는 문자</li>
<li><code>\B</code> : <code>\b</code> 의 역집합</li>
</ul>
<pre><code class="language-jsx">const str = &quot; react and act&quot;;

// act 양옆에 단어 문자가 없는 것만 찾기
str.match(/\bact\b/); // [ &#39;act&#39;, index: 11, input: &#39; react and act&#39;, groups: undefined ]
// act 좌측에 단어 문자가 있는 것 찾기
str.match(/\Bact/);   // [ &#39;act&#39;, index: 3, input: &#39; react and act&#39;, groups: undefined ]</code></pre>
<br>


<h2 id="교체-구문alternation-">교체 구문(Alternation, <code>|</code>)</h2>
<ul>
<li>OR 연산자와 동일한 역할</li>
<li>연산자 우선 순위가 가장 낮다.</li>
</ul>
<pre><code class="language-jsx">const str = &quot; react or act or enact&quot;;

str.match(/react|enact/g); // [ &#39;react&#39;, &#39;enact&#39; ]</code></pre>
<br>


<h2 id="수량자quantifier">수량자(Quantifier)</h2>
<ul>
<li>메타 문자들이 N회 반복됨을 나타내기 위한 문자</li>
<li>적용하고자하는 문자 오른쪽에 붙인다.</li>
<li>기본적으로 정규식은 탐용적이기 때문에, 수량자에 다양한 경우의 패턴이 매칭될 때에는 <strong>가능한 긴 패턴을 반환</strong>다.</li>
</ul>
<pre><code class="language-jsx">const str = &quot;abc&quot;;

// ab 또는 abc가 있는 찾기
// 반환 값으로 ab, abc 중 더 긴 abc 반환
str.match(/abc?/); // [ &#39;abc&#39;, index: 0, input: &#39;abc ab&#39;, groups: undefined ]</code></pre>
<ul>
<li>반환 값으로 ab도 일치하고 abc도 일치하지만 정규식은 탐욕적이기 때문에 더 길고 많은 값을 반환한다.</li>
</ul>
<br>


<h3 id="게으른-수량자lazy-quantifier">게으른 수량자(Lazy Quantifier)</h3>
<pre><code class="language-jsx">const str = &quot;abc&quot;;

// ab 또는 abc가 있는 찾기
str.match(/abc??/); // [ &#39;ab&#39;, index: 0, input: &#39;abc&#39;, groups: undefined ]</code></pre>
<ul>
<li>반환 값으로 ab도 일치하고 abc도 일치하지만 <code>?</code>를 다시 붙여 게으르게 만듦으로서 가능한 더 적고 짧은 값을 반환</li>
</ul>
<br>


<h3 id="우선-범위-수량자---정확이-n-번-일치-n">우선 범위 수량자 - 정확이 <code>n</code> 번 일치 (<code>{n}</code>)</h3>
<ul>
<li><code>{m,n}</code> : 최소 m번, 최대 n번 반복</li>
</ul>
<pre><code class="language-jsx">const str = &quot;xxx&quot;;

str.match(/x{1,3}/);   // [ &#39;xxx&#39;, index: 0, input: &#39;xxx&#39;, groups: undefined ]
str.match(/x{1,3}?/);  // [ &#39;x&#39;, index: 0, input: &#39;xxx&#39;, groups: undefined ]</code></pre>
<br>


<ul>
<li><code>{n}</code> : 앞선 패턴이 n번 반복 === <code>{n,n}</code></li>
</ul>
<pre><code class="language-jsx">const str = &quot;zxxzzxz&quot;;

str.match(/x{1}z/); // [ &#39;xz&#39;, index: 2, input: &#39;zxxzzxz&#39;, groups: undefined ]
str.match(/x{2}z/); // [ &#39;xxz&#39;, index: 1, input: &#39;zxxzzxz&#39;, groups: undefined ]</code></pre>
<br>


<ul>
<li><code>{n,}</code> : 앞선 패턴이 최소 n번 이상 반복</li>
</ul>
<pre><code class="language-jsx">const str = &quot;zxxzzxz&quot;;

str.match(/x{1,}/)  // [ &#39;xx&#39;, index: 1, input: &#39;zxxzzxz&#39;, groups: undefined ]
str.match(/x{1,}?/) // [ &#39;x&#39;, index: 1, input: &#39;zxxzzxz&#39;, groups: undefined ]</code></pre>
<br>


<ul>
<li><code>+</code> : 앞선 패턴이 한번 이상 반복 === <code>{1,}</code></li>
</ul>
<pre><code class="language-jsx">const str = &quot;xxxx&quot;;

str.match(/xx+/)  // [ &#39;xxxx&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]
str.match(/xx+?/) // [ &#39;xx&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]</code></pre>
<br>


<ul>
<li><code>?</code> , <code>??</code> : 앞선 패턴이 1번 또는 0번 일치</li>
</ul>
<pre><code class="language-jsx">const str = &quot;xxxx&quot;;

str.match(/xx?/)  // [ &#39;xx&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]
str.match(/xx??/) // [ &#39;x&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]</code></pre>
<br>


<ul>
<li><code>*</code> : 0번 이상 일치</li>
</ul>
<pre><code class="language-jsx">const str = &quot;xxxx&quot;;

str.match(/x*/)  // [ &#39;xxxx&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]
str.match(/x*?/) // [ &#39;&#39;, index: 0, input: &#39;xxxx&#39;, groups: undefined ]</code></pre>
<hr>
<br>

<p>참조</p>
<p><a href="https://wormwlrm.github.io/2020/07/19/Regular-Expressions-Tutorial.html">정규표현식 완전정복 - 재그지그의 개발 블로그</a></p>
<p><a href="https://velog.io/@hustle-dev/JavaScript-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D">[JavaScript] - 정규표현식</a></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_Expressions">정규 표현식 - JavaScript | MDN</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 특정 문자열 바꾸기]]></title>
            <link>https://velog.io/@woody_/JS-%ED%8A%B9%EC%A0%95-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B0%94%EA%BE%B8%EA%B8%B0</link>
            <guid>https://velog.io/@woody_/JS-%ED%8A%B9%EC%A0%95-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B0%94%EA%BE%B8%EA%B8%B0</guid>
            <pubDate>Tue, 07 Feb 2023 12:24:32 GMT</pubDate>
            <description><![CDATA[<h3 id="1-split-join-활용">1. <code>split</code>, <code>join</code> 활용</h3>
<pre><code class="language-jsx">let str = &quot;1zero2zero3&quot;
let parm = &#39;zero&#39;

let arr = str.split(parm)
console.log(arr) // [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]
str= arr.join(&#39;0&#39;)
console.log(str) // 10203</code></pre>
<ul>
<li><code>split()</code> 을 이용하여 특정문자(parm=<code>zero</code>)를 구분자로 잘라서 배열 생성</li>
<li><code>join()</code> 을 이용하여 배열의 모든 요소를 대체하려는 문자열(<code>&#39;0&#39;</code>) 으로 연결하여 하나의 문자열 생성</li>
</ul>
<br>

<hr>
<h3 id="2-replace-정규식-활용">2. <code>replace</code>, <code>정규식</code> 활용</h3>
<pre><code class="language-jsx">let str = &quot;1zero2zero3&quot;
let parm = &#39;zero&#39;

let reg = new RegExp(parm,&#39;g&#39;)
console.log(reg) // /zero/g
str= str.replace(reg, 0)
console.log(str) // 10203</code></pre>
<ul>
<li><code>new RegExp()</code>로 변수에 담긴 특정문자로 정규식 생성</li>
<li><code>replac()</code>로 정규식에 해당하는 문자열 0으로 대체</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 문자열 역순으로 뒤집기]]></title>
            <link>https://velog.io/@woody_/JS-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%97%AD%EC%88%9C%EC%9C%BC%EB%A1%9C-%EB%92%A4%EC%A7%91%EA%B8%B0</link>
            <guid>https://velog.io/@woody_/JS-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%97%AD%EC%88%9C%EC%9C%BC%EB%A1%9C-%EB%92%A4%EC%A7%91%EA%B8%B0</guid>
            <pubDate>Tue, 07 Feb 2023 12:21:25 GMT</pubDate>
            <description><![CDATA[<h4 id="요약">요약</h4>
<ul>
<li><code>split()</code> 메서드로 문자열을 문자 배열로 반환</li>
<li><code>reverse()</code> 메서드로 배열 순서 뒤집기</li>
<li><code>join()</code> 메서드로 배열의 원소 문자열로 결합</li>
</ul>
<br>

<pre><code class="language-javascript">function reverseString(str) {
  // 1. split() 메서드로 string -&gt; 배열로 반환
  let splitString = str.split(&quot;&quot;);  // [ &#39;b&#39;, &#39;a&#39;, &#39;n&#39;, &#39;n&#39;, &#39;a&#39; ]

  // 2. reverse() 메서드를 배열의 순서를 뒤집기
  let reverseArray = splitString.reverse(); // [ &#39;a&#39;, &#39;n&#39;, &#39;n&#39;, &#39;a&#39;, &#39;b&#39; ]

  // 3. join() 메서드로 배열의 모든 요소를 문자열로 결합
  let joinArray = reverseArray.join(&quot;&quot;); // annab

  // 4. 반전된 문자열을 반환
  return joinArray; // annab
}

console.log(reverseString(&quot;banna&quot;));</code></pre>
<br>

<hr>
<h4 id="위의-코드를-한줄로-요약하면-아래와-같다">위의 코드를 한줄로 요약하면 아래와 같다.</h4>
<pre><code class="language-javascript">function reverseString(str) {
  return str.split(&quot;&quot;).reverse().join(&quot;&quot;);  // annab
}

console.log(reverseString(&quot;banna&quot;));</code></pre>
]]></description>
        </item>
    </channel>
</rss>