<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>GUD_JUN.log</title>
        <link>https://velog.io/</link>
        <description>기록을 위한 블로그</description>
        <lastBuildDate>Tue, 18 Mar 2025 14:38:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. GUD_JUN.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gud_wns" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Nest.js 정리 (Pipe & Mapped Types & Serialization)]]></title>
            <link>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Pipe-Mapped-Types-Serialization</link>
            <guid>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Pipe-Mapped-Types-Serialization</guid>
            <pubDate>Tue, 18 Mar 2025 14:38:54 GMT</pubDate>
            <description><![CDATA[<h2 id="pipe">Pipe</h2>
<p>argument데이터들을 가공한 후에 컨트롤러 메소드로 값을 넘겨주는 미들웨어이다. 
컨트롤러 메소드들이 실행되기 전에 pipe가 실행된다고 이해하면 된다.
데이터를 원하는 형태로 변환하고 검증까지 할 수 있다.</p>
<p>글로벌 - 컨트롤러 - 라우트 - 라우트 파라미터에 사용할 수 있다.
그리고 주로 라우트 파라미터를 많이 사용 한다. 
(이전에 이미 main에서 글로벌로 class-validator를 사용해보긴 했다.)</p>
<img src="https://velog.velcdn.com/images/gud_wns/post/128520ba-f0bd-4790-b544-ae2d9e81408b/image.png" width="600px" />
<img src="https://velog.velcdn.com/images/gud_wns/post/05067310-cbc4-487f-9ac1-fe245da4eb01/image.png" width="600px" />

<p>코드를 보면 이해하기 쉬운데 첫번째 이미지가 라우트 파라미터에 적용한 코드이다.
파라미터의 id를 int형으로 변환해주고 검증까지 해준다.
두번째 이미지처럼 test라는 값을 id에 넘길경우 에러를 반환해준다.</p>
<img src="https://velog.velcdn.com/images/gud_wns/post/77ed48f3-6dff-44d6-9bf7-c85ec02c2990/image.png" width="600px" />

<p>또한 이런식으로 에러메세지에 대해 커스텀도 가능하다.</p>
<hr>
<p>custom으로 pipe를 만들 수도 있다.</p>
<pre><code class="language-javascript">import {
  ArgumentMetadata,
  BadRequestException,
  Injectable,
  PipeTransform,
} from &#39;@nestjs/common&#39;;

@Injectable()
export class MovieTitleValidationPipe implements PipeTransform&lt;string, string&gt; {
  transform(value: string, metadata: ArgumentMetadata): string {
    if (!value) {
      return value;
    }

    if (value.length &lt;= 2) {
      throw new BadRequestException(&#39;영화의 제목은 3자 이상 작성해주세요!&#39;);
    }
    return value;
  }
}
</code></pre>
<img src="https://velog.velcdn.com/images/gud_wns/post/2634e0f7-e92e-4fc0-b7be-1c1e92122315/image.png" width="600px" />

<img src="https://velog.velcdn.com/images/gud_wns/post/2314a1a2-bc38-418f-96fa-44cecea703af/image.png" width="600px" />


<p>이렇게 query parameter값의 validation을 체크해줄 수 있다.</p>
<h2 id="mapped-types">Mapped Types</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/f14030e5-4494-4905-bd5f-573b20204639/image.png" alt=""></p>
<p>이미지보면 createDto와 UpdateDto의 각 필드의 class-validator가 isOptional 데코레이터만 제외하고 일치한다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/482ffc3e-48b6-45f0-8fae-fc0ed077121d/image.png" alt="">
이렇게 변경해주면 createDto의 필드들을 nullable로 만들어주는 partial을 사용하면 된다.</p>
<p>이밖에도 pick, omit, intersection, composition등이 있다.
뭔가 TS의 유틸리티타입 같다.</p>
<h2 id="serialization">Serialization</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/7bb79dcf-4078-40d2-a4ba-05cf8b8995de/image.png" alt=""></p>
<p>@Exclude 데코레이션 통해서 프론트엔드로 해당 컬럼은 보내지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nest.js 정리 (transaction)]]></title>
            <link>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-transaction</link>
            <guid>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-transaction</guid>
            <pubDate>Thu, 13 Mar 2025 17:13:33 GMT</pubDate>
            <description><![CDATA[<h2 id="transaction">Transaction</h2>
<p>예를 들어, 은행어플에서 a계좌에서 b계좌로 1000원을 입금하는 과정에서 돈은 빠져나가고 b계좌로 받기 전 에러가 나면 1000원이라는 데이터는 유실된다. 또 a계좌는 1000원이 차감된 잔액으로 계속 유지가 된다. 
그래서 출금과 입금을 transaction을 사용하여 하나로 논리적 작업 단위로 묶을 수 있으며, 따라서 모두 다 실행되거나 롤백이 될 수 있게 작업해야한다. 
nest에서 이런 롤백기능을 편하게 제어할 수 있는 기능을 제공한다.</p>
<img src="https://velog.velcdn.com/images/gud_wns/post/f1fdacd5-f7bf-4b6c-becb-a2c403e0ab36/image.png" width="600" />

<p>createQueryRunner()로 queryRunner를 생성하고 기존 repository방식으로 type orm을 사용하는 부분을 queryRunner.manager로 바꿔주면 되고, 꼭 try, catch문으로 사용해서 에러발생시 catch에 걸려서 rollbackTransaction()이 실행되도록 한다.</p>
<p>또 이미지를 참고하면 의도적으로 코드 중간에 error를 발생시켰는데 이 때 movieDetail에 데이터가 추가되어있지 않는다. 만약 기존 repository방식으로 사용했다면 movieDetail 테이블에 데이터는 추가가 되었을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nest.js 정리 (Constraint, Many To Many와 relationship의 Foreign Key)]]></title>
            <link>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Constraint-Many-To-Many%EC%99%80-relationship%EC%9D%98-Foreign-Key</link>
            <guid>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Constraint-Many-To-Many%EC%99%80-relationship%EC%9D%98-Foreign-Key</guid>
            <pubDate>Wed, 12 Mar 2025 23:08:59 GMT</pubDate>
            <description><![CDATA[<h2 id="unique--nullable-constraint">unique &amp; nullable constraint</h2>
<img src="https://velog.velcdn.com/images/gud_wns/post/33667867-1bbd-44c0-9968-0777c95c17ac/image.png" width=500/>

<p>entity에서 해당테이블의 특정 컬럼을 유니크로 지정할 수 있고, nullable을 통해 null로 수정 못하도록 강제한다.
<img src="https://velog.velcdn.com/images/gud_wns/post/29b4cefd-4c7d-418a-a514-7b89ffa1cd40/image.png" alt="">
DB에서도 지울 수 없다.</p>
<h2 id="many-to-many와-relationship">Many To Many와 relationship</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/8b0ca5c1-18a1-49a3-9522-f7b9d4379210/image.png" alt=""></p>
<p>typeOrm이 제공하는 데코레이터로 지정해서 크게 어려운 건 없지만 foreign key관리하는 방식을 기억해두자.</p>
<ol>
<li>다대다는 두 테이블 사이의 관계를 저장하기 위해 별도의 중간 테이블이 필요하기 때문에 JoinTable이 필요하며 중간테이블이 FK관리.</li>
<li>일대일은 어느 쪽 테이블에서 FK를 관리할지 명시적으로 지정해야 하며, 순전히 개발자의 마음대로 하면 된다.</li>
<li>다대일 관계에서는 자연스럽게 FK가 Many 테이블에 포함된다. (그래서 관계 지정 데코레이터 필요 없음)</li>
</ol>
<h3 id="many-to-many시에-별도의-중간-테이블-생성-상태">Many To Many시에 별도의 중간 테이블 생성 상태</h3>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/d3f2730f-c6b8-4280-b495-92329f2b9599/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nest.js 정리 (Entity & OneToOne & Cascade)]]></title>
            <link>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Entity-OneToOne-Cascade</link>
            <guid>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-Entity-OneToOne-Cascade</guid>
            <pubDate>Sat, 22 Feb 2025 18:41:03 GMT</pubDate>
            <description><![CDATA[<h2 id="entity-관련">Entity 관련</h2>
<h3 id="1-entity-embedding">1. entity embedding</h3>
<pre><code class="language-javascript">export class BaseEntity {
  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @VersionColumn()
  version: number;
}

@Entity()
export class Movie {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  genre: string;

  @Column(() =&gt; BaseEntity)
  base: BaseEntity;
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/2fe33773-bdc1-4b73-a289-c56e83481e73/image.png" alt="">
내가 설정한 객체에 임베딩 (extend와의 차이)</p>
<h3 id="2-entity-extends">2. entity extends</h3>
<pre><code class="language-javascript">@Entity()
export class Movie extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  genre: string;
}</code></pre>
<p>일반적인 상속 방법으로 사용
<img src="https://velog.velcdn.com/images/gud_wns/post/90ba1a0b-9f12-4067-9c6a-d68f5dc42e0a/image.png" alt=""></p>
<h3 id="3-single-table-inheritance">3. single table inheritance</h3>
<pre><code class="language-javascript">@Entity()
@TableInheritance({
  column: {
    name: &#39;type&#39;,
    type: &#39;varchar&#39;,
  },
})
export class Content extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  genre: string;
}

@ChildEntity()
export class Movie extends Content {
  @Column()
  runtime: number;
}

@ChildEntity()
export class Series extends Content {
  @Column()
  seriesCount: number;
}</code></pre>
<img src="https://velog.velcdn.com/images/gud_wns/post/e3ce7dfd-d54e-4aae-9f24-40cbf2926e45/image.png" width="300" alt="이미지 설명">

<p>childEntity에서의 개별적인 컬럼들을 포함하여 하나의 테이블로 생성.
이 때 개별적인 값들은 nullable 인데 로우 생성 시 content테이블에는 다음과 같이 생성된다.
post movie -&gt; seriesCount는 null로 생성
post series -&gt; runtime은 null로 생성</p>
<p>근데 언제 써야할지는 잘 모르겠다..
대신 SQL로 만드는 건 감도잘 안오는데 굉장히 직관적으로 구현할 수 있는 것 같다</p>
<h2 id="relationship">relationship</h2>
<h3 id="onetoone">OneToOne</h3>
<img src="https://velog.velcdn.com/images/gud_wns/post/caf6778c-0b3d-4681-8991-1f54009cdb1f/image.png" width="300"/>

<img src="https://velog.velcdn.com/images/gud_wns/post/c28935be-7e90-40c0-8ebc-7d8a8b7b07cf/image.png" width="300" />
일대일 관계를 지정하여 SQL JOIN 기능을 구현
reference는 누가 들고 있어도 상관없는데 일반적으로 movie가 상세정보를 가지고 있는게 맞다.
그래서 @JoinColumn() 으로 foreignKey를 저장 시켜준다.

<img src="https://velog.velcdn.com/images/gud_wns/post/e25641db-2b2c-4d97-aa31-857edb969787/image.png" width="600"/>
<img src="https://velog.velcdn.com/images/gud_wns/post/085ee7f0-07c6-4957-83c8-d5bcbf61dbd7/image.png" width="400"/>


<h2 id="cascade-옵션">cascade 옵션</h2>
<pre><code class="language-javascript">  async createMovie(createMovieDto: CreateMovieDto) {
    const movieDetail = await this.movieDetailRepository.save({
      detail: createMovieDto.detail,
    });

    const movie = await this.movieRepository.save({
      title: createMovieDto.title,
      genre: createMovieDto.genre,
      detail: movieDetail,
    });

    return movie;
  }</code></pre>
<p>기존 영화 생성코드인데 사실 movieDetail 을 따로 선언해서 사용하지않고 바로 
detail: createMovieDto.detail을 할 수 있으면 편하다.</p>
<p>그러나 바로 아래와 바로 써버리면 영화 조회시에 detailId가 null이고 연결이 안된다.</p>
<pre><code class="language-javascript"> async createMovie(createMovieDto: CreateMovieDto) {
    const movie = await this.movieRepository.save({
      title: createMovieDto.title,
      genre: createMovieDto.genre,
      detail: {
        detail: createMovieDto.detail,
      },
    });

    return movie;
  }</code></pre>
<p>이것을 가능하게 하는 것이 cascade 옵션이다.</p>
<pre><code class="language-javascript">@OneToOne(() =&gt; MovieDetail, (movieDetail) =&gt; movieDetail.id, {
    cascade: true,
  })</code></pre>
<p>Movie Entity에서 relationship 관련 부분에
cascade를 true로 해주면 movie생성시에 detail 테이블까지 한꺼번에 생성가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nest.js 정리 (기본 구조 및 typeOrm 기초와 설정)]]></title>
            <link>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-part-1</link>
            <guid>https://velog.io/@gud_wns/Nest.js-%EC%A0%95%EB%A6%AC-part-1</guid>
            <pubDate>Tue, 11 Feb 2025 14:38:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gud_wns/post/fedfa4d8-618e-4cb8-8b3b-aff74313d54c/image.png" alt="">
nest g resource 명령어로 movie폴더와 같은 리소스를 만들 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/b70e18a1-e7cb-4c25-b7bf-d0628e995db1/image.png" alt=""></p>
<h3>@module</h3>
typeOrm과 컨트롤러와 비즈니스 로직등을 하나의 모듈로 묶어서 관리할 수 있다.
provides에서 비즈니스 로직을 의존성 주입 컨테이너(DI Container)에 등록하여 컨트롤러에서 사용가능하다.
사용방법을 익혀놓으면 될 듯하다.

<p><img src="https://velog.velcdn.com/images/gud_wns/post/0d66fef4-a8e1-4b8e-9799-fe0c8b823f1a/image.png" alt="">
프로젝트의 메인 모듈이 되는 app.module.ts 인데 joi를 사용해서 환경변수를 validation하였고,
참고로 환경변수 셋팅시에 fotRoot대신 forRootAsync를 통해 비동기적으로 환경변수를 셋팅할 수 있다고 한다.
GPT는 외부 API 연동 시에 유용하다고 하는데 강의를 좀 더 봐야할 듯 하다.</p>
<p>참고로 nest.js는 ioc 컨테이너를 통해 인스턴스 생성과 injection을 자동으로 해준다는데 이론적인 내용이라 필요할 때 다시 이해해봐야할 듯.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/34135625-21e4-4468-8d77-d1f5c2b3da6a/image.png" alt=""></p>
<h3>Dto</h3>
post나 patch 요청 시 request body의 타입을 지정하고 class-validator를 통해 유효성검사 실행.
예를 들어 title 필드 같은 경우 바디에 필드가 존재할 때는 반드시 값이 있어야 하지만, 존재하지 않으면 아예 검사에서 제외된다.

<p>custom validator를 만들 수도 있고, class transformer를 통해 expose, exclude, 응답값 변환도 가능하니 필요할 때 코드 참고하면 될 듯 하다.
필요할 때 찾아쓰자.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/fba9e414-8d19-44cd-94f4-d057beaebd41/image.png" alt=""></p>
<h3>Entity</h3>
TypeOrm을 쓰기 위해 설정한다.
id를 자동으로 증가시키며 유니크한 primary_key를 생성해주고, 컬럼생성, 생성시간, 업데이트 시간 등등을 자동으로 채워준다.
이것도 필요할 떄 찾아쓰면 될 듯.

<p><img src="https://velog.velcdn.com/images/gud_wns/post/fa5495fc-cf4e-40fc-922d-4c53aea557c3/image.png" alt=""></p>
<h3>Service</h3>
repository를 사용해서 지정한 entity에 대한 crud쿼리를 날릴 수 있다. SQL안써도 된다.
inject 필요한 부분들 코드확인해서 꼭 해당기능들 주입해주자.

<p><img src="https://velog.velcdn.com/images/gud_wns/post/0af4b31f-5bba-4fd2-863e-fb059d4a0b6c/image.png" alt="">
id값으로 특정 영화가져오는 코드인데 repository 메서드들은 필요할 때 찾아보자.
참고로 NotFoundException은 @nestjs/common에서 제공하는 기능인데 이거 안쓰면 id와 일치하는 영화 없어도 200 ok 떨어진다. 에러처리를 위해 꼭 필요한 곳에 사용해줘야 한다.</p>
<hr>
<p>강의 꽤 본 것 같은데 정리해보니 많지않다.
포스트맨과 postgres 사용해 보니 신기하고 api 어떻게 만드는지 감이 조금씩 오는 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[마이크로 프론트엔드 아키텍쳐]]></title>
            <link>https://velog.io/@gud_wns/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90</link>
            <guid>https://velog.io/@gud_wns/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90</guid>
            <pubDate>Sun, 22 Sep 2024 19:17:01 GMT</pubDate>
            <description><![CDATA[<p><a href="https://gud-jun.notion.site/f6c318e4b98e48708c5595c782318ad5?pvs=4">&gt; 회사에서 마이크로 프론트엔드 아키텍쳐를 주제로 발표하기 위해 정리한 노션</a></p>
<p>MFA 직접 구현과 webpack5의 module federation을 통한 구현도 실습해봐야할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[제네릭]]></title>
            <link>https://velog.io/@gud_wns/%EC%A0%9C%EB%84%A4%EB%A6%AD</link>
            <guid>https://velog.io/@gud_wns/%EC%A0%9C%EB%84%A4%EB%A6%AD</guid>
            <pubDate>Mon, 16 Sep 2024 23:20:54 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭의-기본-개념-파악">제네릭의 기본 개념 파악</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/39765705-9d61-4a4e-aee7-3920eb83528b/image.jpeg" alt=""></p>
<blockquote>
<p>제네릭은 다양한 타입을 지원하면서도 타입 안정성을 제공한다. 제네릭을 사용하면 코드의 유연성이 크게 향상되며, 코드의 재사용성을 높이고 유지보수성을 개선할 수 있다.</p>
</blockquote>
<h2 id="제네릭-타입-vs-유니언-타입">제네릭 타입 vs 유니언 타입</h2>
<p>다음은 클래스의 인스턴스에서 어떤 타입의 데이터를 저장할건지 선택하고 해당 데이터 타입만 사용하는 클래스를 구현한 코드이다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/f25bd21e-1c07-4d71-b7e7-98be98d17f75/image.png" alt=""></p>
<p>만약 아래와 같이 구현할 경우 매서드를 호출할때마다 윈시형 타입이라면 어떠한 타입이라도 data 속성에 추가 및 삭제할 수 있으며, 기능을 정상적으로 구현하기 위해서는 typeof로 타입체크를 해야하기 때문에 코드의 가독성도 많이 떨어질 것이다.
<img src="https://velog.velcdn.com/images/gud_wns/post/57c11bc6-0166-4262-b659-beb9f8d8e255/image.png" alt=""></p>
<p>메서드 호출 시, 파라미터 타입의 유연성을 가지고 싶다면 유니언 타입, 하나의 타입으로 고정하고 싶으면 제네릭 타입을 사용하면 된다. </p>
<hr>
<blockquote>
<p><strong>Generic 타입은 함수나 클래스의 선언 시점이 아닌, 사용 시점에 타입을 선언할 수 있는 방법을 제공한다.</strong></p>
</blockquote>
<p>출처: <a href="https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Generic-%ED%83%80%EC%9E%85-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0">https://inpa.tistory.com/entry/TS-📘-타입스크립트-Generic-타입-정복하기</a> [Inpa Dev 👨‍💻:티스토리]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[추상클래스,  protected, private, 싱글톤 패턴, getter와 setter]]></title>
            <link>https://velog.io/@gud_wns/%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4-protected-private-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4-getter%EC%99%80-setter</link>
            <guid>https://velog.io/@gud_wns/%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4-protected-private-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4-getter%EC%99%80-setter</guid>
            <pubDate>Fri, 13 Sep 2024 22:21:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gud_wns/post/3e7cfa58-b730-45c9-b053-e0443506d7f9/image.png" alt="">
<img src="https://velog.velcdn.com/images/gud_wns/post/c39b0845-393a-4353-9641-2a93e6e2813a/image.png" alt=""></p>
<p>getter와 setter를 통해서만 private &amp; protected 속성에 접근하도록하여 캡슐화를 구현.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[class (this, 접근제한자)]]></title>
            <link>https://velog.io/@gud_wns/class-this-%EC%A0%91%EA%B7%BC%EC%A0%9C%ED%95%9C%EC%9E%90</link>
            <guid>https://velog.io/@gud_wns/class-this-%EC%A0%91%EA%B7%BC%EC%A0%9C%ED%95%9C%EC%9E%90</guid>
            <pubDate>Mon, 09 Sep 2024 17:10:10 GMT</pubDate>
            <description><![CDATA[<h2 id="this">this:</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/b82dca7a-e7a7-4ace-9078-3adfe3ec304f/image.png" alt=""></p>
<p>호출 하는 대상이 무엇인가에 집중해야한다.
<a href="https://www.youtube.com/watch?v=tDZROpAdJ9w">https://www.youtube.com/watch?v=tDZROpAdJ9w</a> 해당 영상참조하여,
bind, 화살표함수에서의 this도 알아 둘 것.</p>
<h2 id="타입체크를-추가하여-컴파일단계에서-에러체크">타입체크를 추가하여 컴파일단계에서 에러체크</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/01e2f965-a4b3-4be2-984d-a7bf1492ece3/image.png" alt=""></p>
<p>또한 employees 프로퍼티 앞에 private가 있는데, 해당 클래스 안에서만 접근이 가능하며, 외부에서 직접 </p>
<pre><code class="language-javascript">accounting.employees[2] = &quot;Anna&quot;;</code></pre>
<p>와 같이 수정하려고 하면 에러가 표시될 것이다.</p>
<p>addEmployee 메서드를 써서 추가하도록 해야하고, public은 별도로 표시안해도됨.</p>
<h2 id="약식-초기화-및-readonly">약식 초기화 및 readonly</h2>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/ff7887df-389e-4f9d-898c-f9940da8d00b/image.png" alt=""></p>
<p>constructor에서 프로퍼티 선인 및 초기값 할당을 할 수 있다.
readonly를 추가하면 내부 메서드에서도 해당 프로퍼티는 수정할 수 없다.</p>
<hr>
<p>태블릿 구입기념 스터디내용 기록중.
뭔가 더 좋은 기록방법을 연구해야할 듯.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[router: 상대경로 절대경로와 Link의 relative 속성]]></title>
            <link>https://velog.io/@gud_wns/router-%EC%83%81%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C%EC%99%80-Link%EC%9D%98-relative-%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@gud_wns/router-%EC%83%81%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C%EC%99%80-Link%EC%9D%98-relative-%EC%86%8D%EC%84%B1</guid>
            <pubDate>Thu, 04 May 2023 20:17:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gud_wns/post/0873bc25-ca3e-47b4-9ad6-60f20eb3d869/image.png" alt=""></p>
<p>일반적으로 router설정은 여태 위와같이 /를 붙혀 절대경로로 지정했는데,<img src="https://velog.velcdn.com/images/gud_wns/post/ad018042-6ea2-466f-b6e3-35d054c84ec6/image.png" alt=""></p>
<p>위와 같이 부모라우트를 절대경로로 지정하고, 자식 페이지들(RootLayout 컴포넌트의 Outlet으로 들어가는)은 상대경로로 지정해주면 부모 url의 뒤에 자동으로 붙는다.</p>
<hr>
<h4 id="첫번째-문제점">첫번째 문제점</h4>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/3eeffabb-6e55-4f1e-91c4-9bc714969857/image.png" alt=""></p>
<p>product의 리스트페이지에서 각 상품의 title을 클릭하면 상세페이지로 이동하는데, 기존에는 절대경로 지정이기 때문에 제일 앞에 root가 안붙는다.
그렇기 때문에 해당 경로를 찾을 수 없다.<img src="https://velog.velcdn.com/images/gud_wns/post/076a782a-aef0-41a7-a3c7-70b7b1f65eb9/image.png" alt=""></p>
<p>디테일 페이지는 목록에서만 접근할 수 있기 때문에,
디테일 페이지로 이동하는 경로를 상대경로로 바꿔주면 자동으로 id값이 붙어 정상적으로 디테일 페이지로 이동한다.
(useParams 적용은 위에서 맨 위에서 설정해두었으니 참고바람)</p>
<hr>
<h4 id="두번째-문제점">두번째 문제점</h4>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/6131734d-d13e-41c2-a346-e55a9ee7a184/image.png" alt="">
디테일 페이지에 뒤로가는 기능인 버튼을 달았고, 보통 import시 경로설정을 할 때 많이 본 &#39;..&#39;로 상위 페이지로 이동하게 하였다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/3eb591db-89ee-4de1-b44b-9b1f920498b8/image.png" alt=""></p>
<p>디테일 페이지 url.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/4c90ed8c-ef9c-4d32-82a7-6a5404ef1861/image.png" alt="">
클릭했을 때의 url.</p>
<p>클릭하면 당연히 리스트로 갈줄알았는데 왜 root로 가버릴까?</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/ad018042-6ea2-466f-b6e3-35d054c84ec6/image.png" alt=""></p>
<p>지정한 router설정을 보면 리스트 페이지인 &#39;products&#39;와
디테일페이지인 &#39;products/:productId&#39;는 형제관계이다. 즉 상위 페이지는 &#39;/root&#39; 였던 것이다.</p>
<p>이를 해결하기 위해서는</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/f71edd71-878b-4bf5-bba4-c4f6f9049afc/image.png" alt=""></p>
<p>Link 속성에 relative=&quot;path&quot;를 사용하여 현재 url경로를 토대로 상위페이지로 이동하게 한다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/0a2dd04d-a3b8-482d-9d0f-681b99a7f6cf/image.png" alt=""></p>
<p>원하던 대로 리스트페이지로 이동됐다.</p>
<p>default 값은 relative=&quot;route&quot; 인데, 말그대로 route를 참조하느냐 현재 path를 참조하느냐의 차이이다.</p>
<hr>
<p>그간 router설정은 path 지정과 auth 상태에 따른 페이지접근 지정만 해보고 오늘 포스팅내용은 처음 봤다.
NavLink라는 간편한 기능(너무 간단해서 검색하면 될 듯)도 모르고 그저 Link만 사용하여 css로 처리했는데, 간단하지만 유용한 부분을 알게 된 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react portal]]></title>
            <link>https://velog.io/@gud_wns/react-portal</link>
            <guid>https://velog.io/@gud_wns/react-portal</guid>
            <pubDate>Sat, 22 Apr 2023 16:30:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공합니다.</strong></p>
</blockquote>
<blockquote>
<p><strong>overflow: hidden대화 상자를 불러오는 구성 요소가 대화 상자를 방해하는 컨테이너 또는 다른 스타일 안에 있더라도 포털을 사용하여 페이지의 나머지 부분 위에 떠 있는 모달 대화 상자를 만들 수 있습니다.
portals의 일반적인 사용 사례는 부모 컴포넌트가 overflow: hidden 이나 z-index 스타일을 가지지만, 자식이 컨테이너에서 시각적으로 “이탈해야 하는” 경우입니다. 예를 들어, 다이얼로그나, 호버카드나, 툴팁같은 게 있습니다.</strong></p>
</blockquote>
<hr>
<p>그간 modal은 각 컴포넌트에서 상황에 따라 불러와 해당 컴포넌트내부에서 자식으로 직접 배치하여 사용했다.</p>
<p>index.html의 매우 깊숙한 곳에 자리잡고 있을 것이고, 부모요소의 css 스타일이 적용될 수도 있기 때문에 성능상 좋지 않은 방법이다.</p>
<p>portal을 사용하면 DOM 트리의 다른 위치에 렌더링할 수 있기때문에, modal등의 컴포넌트들을 어떠한 컴포넌트의 자식요소로 배치할 필요가 없게 된다.</p>
<p>스터디를 하면서 portal의 개념을 처음으로 알게 되었고 이를 사용한 후기를 기록해보자.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/beaab400-d369-4b94-ab96-f2aa2c1e4a90/image.png" alt="">
index.html에서 모달컴포넌트가 렌더될 id=&quot;overlays&quot;인 div를 만들어둔다.</p>
<pre><code class="language-javascript">import ReactDOM from &#39;react-dom&#39;;

import classes from &#39;./Modal.module.css&#39;;

const Backdrop = (props) =&gt; {
  return &lt;div className={classes.backdrop} /&gt;;
};

const ModalOverlay = (props) =&gt; {
  return (
    &lt;div className={classes.modal}&gt;
      &lt;div className={classes.content}&gt;{props.children}&lt;/div&gt;
    &lt;/div&gt;
  );
};

const portalElement = document.getElementById(&#39;overlays&#39;);

const Modal = (props) =&gt; {
  return (
    &lt;&gt;
      {ReactDOM.createPortal(&lt;Backdrop /&gt;, portalElement)}
      {ReactDOM.createPortal(
        &lt;ModalOverlay&gt;{props.children}&lt;/ModalOverlay&gt;,
        portalElement
      )}
    &lt;/&gt;
  );
};

export default Modal;</code></pre>
<p>위와 같이 modal 컴포넌트 내부를 구성한다.
<img src="https://velog.velcdn.com/images/gud_wns/post/984ce6a2-690b-47a8-907b-445b668f1bf1/image.png" alt=""></p>
<p>root말고 overlays에 모달이 렌더된다.</p>
<p>react 18버전에서는 react-dom에서 createPortal 훅을 지원하기 때문에
아래와 같이 변경할 수 있다.</p>
<pre><code class="language-javascript">import ReactDOM from &quot;react-dom&quot;;
import { createPortal } from &quot;react-dom&quot;; //변경

import classes from &quot;./Modal.module.css&quot;;

const Backdrop = (props) =&gt; {
  return &lt;div className={classes.backdrop} /&gt;;
};

const ModalOverlay = (props) =&gt; {
  return (
    &lt;div className={classes.modal}&gt;
      &lt;div className={classes.content}&gt;{props.children}&lt;/div&gt;
    &lt;/div&gt;
  );
};

const portalElement = document.getElementById(&quot;overlays&quot;);

const Modal = (props) =&gt; {
  return (
    &lt;&gt;
      {createPortal(&lt;Backdrop /&gt;, portalElement)} //변경
      {createPortal(
        &lt;ModalOverlay&gt;{props.children}&lt;/ModalOverlay&gt;,
        portalElement
      )}
    &lt;/&gt;
  );
};

export default Modal;
</code></pre>
<hr>
<p>사용방법은 굉장히 간단한데 얻는 이점이 굉장히 많을 것으로 생각된다.
현재 프로젝트를 리팩토링 하면서 modal을 portal로 변경해보는 것으로 더 연습을 해보면 좋을 것 같다.</p>
<p>portal 관련 react 공식문서 <a><a href="https://react.dev/reference/react-dom/createPortal">https://react.dev/reference/react-dom/createPortal</a></a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect를 활용한 debouncing 활용]]></title>
            <link>https://velog.io/@gud_wns/useEffect%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-debouncing-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@gud_wns/useEffect%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-debouncing-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Sun, 09 Apr 2023 12:29:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>디바운싱(Debouncing) : 연이어 발생한 이벤트를 하나의 그룹으로 묶어서 처리하는 방식으로, 주로 그룹에서 마지막, 혹은 처음에 처리된 함수를 처리하는 방식으로 사용되곤 한다.</p>
</blockquote>
<pre><code class="language-javascript">useEffect(() =&gt; {
      setFormIsValid(
        enteredPassword.trim().length &gt; 6 &amp;&amp; enteredEmail.includes(&quot;@&quot;)
      );
  }, [enteredEmail, enteredPassword]);</code></pre>
<p>위와 같이 enteredEmail, enteredPassword를 의존성 배열에 추가하여, 변화가 있을 때마다 formIsValid의 값을 변화시킨다고 했을 때에는 위와같이 useEffect를 사용하면 큰 문제가 되지 않을 것이다.</p>
<p>하지만 백엔드 서버와 통신하는 코드가 있다고 가정하면, 의존성 배열의 state들이 변화가 있을 때마다 네트워크 요청을 보낼 것이고, 이는 불필요한 네트워크 트래픽을 만든다.</p>
<p>따라서 최상단 정의대로 간단한 <strong>디바운싱</strong>을 구현하여, 마지막에 함수를 처리해보도록 하자.</p>
<hr>
<ol>
<li><pre><code class="language-javascript">useEffect(() =&gt; {
 setTimeout(() =&gt; {
   console.log(&quot;checking form validity!&quot;);
   setFormIsValid(
     enteredPassword.trim().length &gt; 6 &amp;&amp; enteredEmail.includes(&quot;@&quot;)
   );
 }, 500);
}, [enteredEmail, enteredPassword]);</code></pre>
</li>
</ol>
<p>위와 같이 사용한다면 8번의 타이핑을 했을 때 <img src="https://velog.velcdn.com/images/gud_wns/post/9d36682b-e192-404f-b7d1-7f273e0310c3/image.png" alt=""></p>
<p>500ms 후 8번의 코드실행이 나타난다.
서버 통신이라고 가정한다면 8번의 실행은... 불필요하다.</p>
<hr>
<ol start="2">
<li><pre><code class="language-javascript">useEffect(() =&gt; {
 setTimeout(() =&gt; {
   console.log(&quot;checking form validity!&quot;);
   setFormIsValid(
     enteredPassword.trim().length &gt; 6 &amp;&amp; enteredEmail.includes(&quot;@&quot;)
   );
 }, 500);

return () =&gt; {
   console.log(&quot;CLEANUP&quot;);
 };
}, [enteredEmail, enteredPassword]);</code></pre>
</li>
</ol>
<p>return문에 <strong>CLEANUP</strong>을 출력하는 익명함수를 추가했다.(기명 함수도 물론 가능.)</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/6beb0703-239b-4b58-bb99-33fd3fc18684/image.png" alt=""></p>
<p>첫번째 이펙트가 실행되기 전에는 CLEANUP이 출력되지 않는다.
11번의 타이핑이 있으면 <span style='background-color: yellow'><strong>첫번째 이펙트가 실행되기 전에</strong></span> 11번 출력된다.</p>
<hr>
<p>3.</p>
<pre><code class="language-javascript"> useEffect(() =&gt; {
    const identifier = setTimeout(() =&gt; {
      console.log(&quot;checking form validity!&quot;);
      setFormIsValid(
        enteredPassword.trim().length &gt; 6 &amp;&amp; enteredEmail.includes(&quot;@&quot;)
      );
    }, 500);

    return () =&gt; {
      console.log(&quot;CLEANUP&quot;);
      clearTimeout(identifier);
    };
  }, [enteredEmail, enteredPassword]);</code></pre>
<p>identifier라는 함수를 만들고 cleanup함수에서 clearTimeout을 시켜준다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/b69c8489-dcbc-4645-b1b1-737129241337/image.png" alt=""></p>
<p>첫번째 이펙트는 결국 한번만 실행되고, 서버와 통신하는 코드라면 한번만 실행하여 불필요한 네트워크 트래픽을 줄일 수 있게 되었다.</p>
<p><strong>참고로 의존성 배열이 빈배열이면 cleanup함수는 dom에서 컴포넌트(useEffect를 사용하는 컴포넌트)가 제거 될때 실행된다.</strong></p>
<hr>
<p>현재 실무에서는 서버와 통신하는 useEffect를 생성하여 의존성배열에 빈배열을 두고 컴포넌트 렌더시 한번만 실행하게 하고 있다.</p>
<p>그외 다른 state들을 조작하는 useEffect들은 새로 useEffect훅을 사용하여 처리하고 있는데, 디바운싱을 잘 활용하면 여러개의 useEffect들을 만들지 않고 보다 깔끔하게 처리 할 수 있을 것 같다.</p>
<hr>
<blockquote>
<p>쓰로틀링(throttling) : 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것
몇 밀리초에 한 번씩만 실행되게 제한을 두는 것</p>
</blockquote>
<p>이라는 개념도 있는데, 스크롤 이벤트에 주로 달아준다.</p>
<pre><code class="language-javascript">const handleScroll = () =&gt; {
    if (throttle) return; //throttle이라는 state를 미리 선언해준다.
    if (!throttle) {
      setThrottle(true);
      setTimeout(() =&gt; {
        console.log(&quot;throttle&quot;);
        setThrottle(false);
      }, 300);
    }
  };
</code></pre>
<p>위와 같은 함수를 scroll이벤트에 달아주면
throttle이 false일 때 setTimeout함수가 실행되면서 throttle을 true로 바꿔주고 throttle이 true일 때는 return으로 함수를 종료시킬 수 있다.</p>
<p>그래서 throttle이 true일 때 실행될 함수를 300ms마다 실행할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[접근자 프로퍼티(getter와 setter)]]></title>
            <link>https://velog.io/@gud_wns/%EC%A0%91%EA%B7%BC%EC%9E%90-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0getter%EC%99%80-setter</link>
            <guid>https://velog.io/@gud_wns/%EC%A0%91%EA%B7%BC%EC%9E%90-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0getter%EC%99%80-setter</guid>
            <pubDate>Sun, 26 Mar 2023 09:17:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>getter, setter 함수라고도 부름
스스로는 값을 갖지 않음 - 다른 프로퍼티의 값을 읽거나 저장할 때 사용
get, set을 앞에 붙임</p>
</blockquote>
<pre><code class="language-javascript">const person1 = {
  age: 17,

  get koreanAge () {
    return this.age + 1;
  },

  set koreanAge (krAge) {
    this.age = krAge - 1;
  }
}

console.log(person1, person1.koreanAge); //get함수 사용법. 함수같이 생겼는데 마치 프로퍼티를 쓰는 것처럼 ()를 안붙힌다.
//{age: 17} 18

person1.koreanAge = 20; // set함수 사용법. 역시 ()를 안붙히며, 인자는 하나이므로 20하나만 전달한다.

console.log(person1, person1.koreanAge); 
//{age: 19} 20;</code></pre>
<p>클래스에서도 사용가능하다.</p>
<pre><code class="language-javascript">class CityOfKorea {
  constructor (name, no) {
    this.name = name;
    this.no = no;
  }
  get cityTitle() {
    return `${this.no}번 도시 ${this.name}`;
  }
  set cityNo(cityNo) {
    if (typeof cityNo !== &#39;number&#39;) return;
    if (cityNo &lt;= 0) return;
    this.no = cityNo;
  }
}

const city1 = new CityOfKorea(&#39;서울&#39;, 1)</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/052d6984-38e4-4a67-a845-f8ed5df3bdc7/image.png" alt=""></p>
<p>정상적인 숫자가 들어가야 no가 바뀌는 것을 볼수 있다.</p>
<pre><code class="language-javascript">class CityOfKorea {
  constructor (name, no) {
    this.name = name;
    this.no = no;
  }
  get no() {
    return this.no + &#39;번 도시&#39;;
  }
  set no(no) {
    this.no = no;
  }
}</code></pre>
<p>이렇게 필드 이름과 setter의 이름이 같을 때는 어떻게 되는가?
무한 반복이 일어나는데 
이부분까지는 무료이니 <a href="https://www.inflearn.com/course/lecture?courseSlug=%EC%A0%9C%EB%8C%80%EB%A1%9C-%ED%8C%8C%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&amp;unitId=131880">얄코 강좌 중 접근자 프로퍼티</a>를 참고해보자.</p>
<p>setter의 무한반복이 일어난다고 할 수 있다.</p>
<h3 id="해결방법">해결방법</h3>
<pre><code class="language-javascript">class CityOfKorea {
  constructor (name, no) {
    this.name = name;
    this.no = no;
  }
  get no() {
    return this._no + &#39;번 도시&#39;; //밑줄추가
  }
  set no(no) {
    this._no = no; //밑줄추가
  }
}

const city1 = new CityOfKorea(&#39;인천&#39;, 2);</code></pre>
<blockquote>
<p>setter와는 다른 필드명을 사용하여 자기반복호출을 방지
constructor의 no는 setter를 가리키고 <strong>실제 필드명은 _no가 됨</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/bf8f341a-05bb-4c18-be40-3aabdcae8d54/image.png" alt=""></p>
<hr>
<h3 id="은닉">은닉</h3>
<blockquote>
<p>💡 <strong>캡슐화</strong> encapsulation
인스턴스의 프로퍼티 값을 함부로 열람하거나 수정하지 못하도록 은닉
자바스크립트의 필드는 기본적으로 public</p>
</blockquote>
<pre><code class="language-javascript">class Employee {
  #name = &#39;&#39;;
  #age = 0;
  constructor (name, age) {
    this.#name = name;
    this.#age = age;
  }
}

const emp1 = new Employee(&#39;김복동&#39;, 32);

console.log(emp1);
//Employee {#name: &#39;김복동&#39;, #age: 32}

console.log(emp1.#name); // ⚠️ 오류 발생
// 오류발생이라고는 하는데 console에는 잘 찍힌다... 뭐지...
// 대괄호로 접근 할 때는 undefined 반환</code></pre>
<pre><code class="language-javascript">class Employee {
  #name = &#39;&#39;;
  #age = 0;
  constructor (name, age) {
    this.#name = name;
    this.#age = age;
  }
  get name () {
    // [n]: n + 1 번째 글자를 반환
    return this.#name[0] + &#39;모씨&#39;;
  }
  get age () {
    return this.#age - (this.#age % 10) + &#39;대&#39;;
  }
  set age (age) {
    if (typeof age === &#39;number&#39; &amp;&amp; age &gt; 0) {
      this.#age = age;
    };
  }
  getOlder(years) { this.#age += years; }
}

const emp1 = new Employee(&#39;김복동&#39;, 22);</code></pre>
<blockquote>
<p><strong>constructor, 접근자 프로퍼티 또는 기타 함수에서 접근 가능</strong>
인스턴스에서 바로 접근은 못하도록 은닉, 위 방법들로 제어</p>
</blockquote>
<p>외부에서 클라이언트가 그 키를 알고 값을 요청해도 접근이 불가능하게 막아둔다.</p>
<p><a href="https://www.yalco.kr/@javascript/5-4/">얄코 접근자프로퍼티에 대하여</a></p>
<p>개인적으로 위의 링크에서 맨 밑에 부분에서 하나하나 복사하며 값을 직접 확인해보는 것을 추천한다.</p>
<hr>
<p>이번 챕터는 얄코님 페이지에서 많은 코드를 발췌해서 정리했다.</p>
<p>조금 이해하기가 어려워서 3번정도 돌려보았다.</p>
<p>결국 위의 개념은 정보은닉으로 이어지는 것 같다.</p>
<p>객체에 대한 구체적인 정보를 노출시키지 않도록 하고, 외부접근도 불가능하게 하기 위한 방법으로 이해할 수 있을 것 같다.</p>
<p>캡슐화가 컴포넌트화인줄 알고, 은닉이란 것은 막연히 서버 쪽 개념이라고만 생각해왔는데</p>
<p>이번에라도 알게 되어 다행인 것 같기도 하고.. 아직 멀었다 싶기도하다...🥲</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 클래스]]></title>
            <link>https://velog.io/@gud_wns/JS-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@gud_wns/JS-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sun, 26 Mar 2023 08:20:46 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-javascript">class FavoriteMovie {
  constructor (name, type) {
    this.name = name;
    this.type = type;
  }
  introduce () { // 💡 메서드
    return `내가 좋아하는 영화는 ${this.name}이고 ${this.type}장르야!`;
  }
}</code></pre>
<p>클래스의 기본 형태.</p>
<p>constructor매서드는 아래와 같은 특성을 지닌다.</p>
<blockquote>
<ul>
<li>인스턴스 생성시 인자를 받아 프로퍼티를 초기화함</li>
</ul>
</blockquote>
<ul>
<li><em><strong>클래스에 하나만 있을 수 있음 - 초과시 오류 발생</strong></em></li>
<li>다른 메서드 이름을 쓸 수 없음</li>
<li>기본값 사용 가능</li>
<li><em><strong>필요없을 (인자가 없을 때 등) 시 생략 가능</strong></em></li>
<li><em><strong>값을 반환하지 말 것! 생성자 함수처럼 암묵적으로 this 반환</strong></em></li>
</ul>
<hr>
<h3 id="생성자-함수와의-차이">생성자 함수와의 차이</h3>
<p>클래스로 인스턴스 생성시 로그에서 프로토타입으로 들어간다.</p>
<p>간단하게 만들어서 콘솔로 확인해보자.</p>
<hr>
<h3 id="필드">필드</h3>
<pre><code class="language-javascript">class FavoriteMovie {
  actor = &#39;실베스터 스탤론&#39;;  //field값이 정해져있으므로 constructor 필요없음.
  about = {&#39;best&#39;: 1, &#39;worst&#39;: 5};
  constructor (name, type) {
    this.name = name;
    this.type = type;
  }
  introduce () {
    return `내가 좋아하는 영화는 ${this.name}이고 ${this.type}장르야!`;
  }
  more (name) {
    return `${this.about[name]}편이 제일 재밌었어`
  }
}


const movie1 = new FavoriteMovie(&#39;록키&#39;,&#39;액션&#39;);
const movie2 = new FavoriteMovie(&#39;대부&#39;, &#39;느와르&#39;);

movie1.about[&#39;best&#39;] = 6; //movie1만 변경. 과연 movie2도 영향이 있을까?

console.log(movie1.more(&#39;best&#39;), movie2.more(&#39;best&#39;))
</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/e4ffac82-1010-4ae5-9e0b-1312d1fd4941/image.png" alt=""></p>
<p>여기서 알수 있는 것은 초기값만 같을 뿐, 인스턴스들의 값들은 <strong>개별적으로</strong> 적용된다!</p>
<hr>
<h3 id="정적static필드와-메서드">정적(static)필드와 메서드</h3>
<pre><code class="language-javascript">class FavoriteMovie {
  static theme = &quot;복싱&quot;;
  static introduceTheme () {
      return `${this.theme}장르입니다.`;
  } 
  constructor (name, type) {
    this.name = name;
    this.type = type;
  }
  introduce () {
    return `내가 좋아하는 영화는 ${this.name}이고 ${this.type}장르야!`;
  }
}</code></pre>
<p>이렇게 클래스를 선언하고 아래와 같이 테스트해봤다.
<img src="https://velog.velcdn.com/images/gud_wns/post/491f5977-f344-4c30-ade8-660de9f7b4ff/image.png" alt="">
<img src="https://velog.velcdn.com/images/gud_wns/post/639c9b07-a59b-4267-8a05-4845555d775a/image.png" alt=""></p>
<p>여기서 알수 있는 것은 static 필드는 클래스 차원에서만 호출이 가능하다는 점이다.</p>
<p>그리고 정적 메서드(introduceTheme)는 정적 필드(theme)만 사용 가능하다.
constructor로 만들어진 필드에 접근하면 에러가 발생한다.</p>
<p>그리고 만들어진 인스턴스 수에 관계없이 메모리한곳만 차지한다.</p>
<p>위의 class를 예시로 들면,
위의 class를 통해 만들어진 인스턴스들은 각각 name,type, introduce()를 차지하고 있는데,</p>
<p>static필드는 FavoriteMovie 단 한곳에서만 차지한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자 함수]]></title>
            <link>https://velog.io/@gud_wns/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@gud_wns/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98</guid>
            <pubDate>Sat, 25 Mar 2023 09:26:52 GMT</pubDate>
            <description><![CDATA[<p>JS공부를 할수록 기본이 제일 중요한 것 같다.
생성자 함수에 대해 어렴풋하게 알고 있었는데, 추후 프로토타입공부할때 헷갈리지않게 확실히 공부해보자.</p>
<hr>
<pre><code class="language-javascript">
//인천의 관광지들을 나타내는 객체들

const spot1 = {
  name: &#39;월미도&#39;,
  no: 1,
  introduce () {
    return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
};

const spot2 = {
  name: &#39;차이나타운&#39;,
  no: 2,
  introduce () {
    return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
};

const spot3 = {
  name: &#39;개항장&#39;,
  no: 3,
  introduce () {
    return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
};</code></pre>
<p>위와 같이 같은 형식의 객체를 만들었다.
그러나 관광지들이 100개, 1000개라면?</p>
<pre><code class="language-javascript">// 생성자 함수 

function IncheonTravelingSpot (name, no) {
  this.name = name;
  this.no = no;
  this.introduce = function () {
  return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
}</code></pre>
<p>와 같이 하나의 틀을 만들면 된다.</p>
<pre><code class="language-javascript">// 아래와 같이 생성자 함수로 만들어진 객체를 인스턴스라고 한다.

const spot1 = new IncheonTravelingSpot(&#39;월미도&#39;, 1);
const spot2 = new IncheonTravelingSpot(&#39;차이나타운&#39;, 2);
const spot3 = new IncheonTravelingSpot(&#39;개항장&#39;, 3);</code></pre>
<ul>
<li><em><strong>생성자 함수는 new 연산자와 함께 사용한다!</strong></em> =&gt; new를 붙히지않으면 undefined 반환.</li>
<li>생성자 함수에서는 메서드 정의가 불가하다 =&gt; 리터럴 객체 생성과 클래스에서는 가능.</li>
</ul>
<hr>
<h2 id="프로토타입">프로토타입</h2>
<p>자바스크립트 객체지향의 중심이라고 한다.</p>
<pre><code class="language-javascript">// 생성자 함수 

function IncheonTravelingSpot (name, no) {
  this.name = name;
  this.no = no;
  this.introduce = function () {
  return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
}

const spot1 = new IncheonTravelingSpot(&#39;월미도&#39;, 1);</code></pre>
<pre><code class="language-javascript">IncheonTravelingSpot.prototype.introEng = function () {
  return `Welcome to Incheon at ${this.name}!`;
};</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/0e393d3f-0d4a-4e06-8b5c-6b0e9d623420/image.png" alt=""></p>
<p>spot1정의 후 introEng라는 프로토타입을 추가했음에도 유기적으로 연결되어 있다.</p>
<hr>
<h2 id="타-방식과의-차이점">타 방식과의 차이점</h2>
<pre><code class="language-javascript">//생성자 함수
function IncheonTravelingSpot (name, no) {
  this.name = name;
  this.no = no;
  this.introduce = function () {
  return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
}

//객체를 직접 반환
function createIncheonTravelingSpot (name, no) {
  return {
    name, no,
    introduce () {
      return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
   }    
  }
}

/// 객체 리터럴
const spot1 = {
  name: &#39;월미도&#39;, no: 1,
  introduce: function () {
  return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
  }
};

// 객체 반환 함수
const spot2 = createIncheonTravelingSpot(&#39;차이나타운&#39;, 2);

// 생성자 함수
const spot3 = new IncheonTravelingSpot(&#39;개항장&#39;, 3);</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/eb9bbb10-4494-4cda-835c-fa4aa5138e74/image.png" alt="">
차이점은 일단 spot3만 로그앞에 생성자 함수명이 써져있기도하고.... instanceof의 return값도 다르다.
<img src="https://velog.velcdn.com/images/gud_wns/post/01eb5c67-39e2-4b7c-bcf4-64d05d52ea68/image.png" alt="">
<img src="https://velog.velcdn.com/images/gud_wns/post/abdae21f-388d-4b37-b25c-854662dabce0/image.png" alt="">
constructor체인을 열어보면 차이점을 명확하게 알 수 있다.
spot3은 생성자함수를 포함하고 있다.</p>
<hr>
<h2 id="생성자-함수-자체의-프로퍼티와-함수-추가">생성자 함수 자체의 프로퍼티와 함수 추가</h2>
<pre><code class="language-javascript">function IncheonTravelingSpot (name, no) {
  this.name = name;
  this.no = no;
  this.introduce = function () {
    return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
   }
}

IncheonTravelingSpot.gu = &#39;중구&#39;;
IncheonTravelingSpot.contact = function () {
    return `안녕하세요! ${this.gu}청입니다. 무엇을 도와드릴까요?`
} ;

const spot3 = new IncheonTravelingSpot(&#39;개항장&#39;, 3);</code></pre>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/c5bda90a-d784-45b3-825e-49da43f2d453/image.png" alt=""></p>
<p>위의 프로토타입 항목에서 처럼 *<em>IncheonTravelingSpot.prototype~ *</em>방식이 아닌 직접 넣었다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/94a120bb-c469-4748-9cdc-98cb3d953c30/image.png" alt=""></p>
<p>차이를 알 수 있을 것이다.</p>
<p>자세한 내용은 차후 정적 프로퍼티(static)에서 다루도록 하자.</p>
<pre><code class="language-javascript">// new를 넣지 않았을 때
function IncheonTravelingSpot (name, no) {
  this.name = name;
  this.no = no;
  this.introduce = function () {
    return `인천의 ${this.no}번째 관광지, ${this.name}입니다!`;
   }

  if (!new.target) { 
    return new IncheonTravelingSpot(name, no); //재귀함수 형식을 사용하여 방지해줄 수 있다.
  }
}

const spot1 = new IncheonTravelingSpot(&#39;월미도&#39;, 1);
const spot2 = IncheonTravelingSpot(&#39;차이나타운&#39;, 2);

console.log(spot1, spot2);</code></pre>
<p>정상 출력되는 것을 볼 수 있다.</p>
<p>클래스에서는 new 없으면 오류발생하므로 그냥 이렇구나하고 넘어가자.</p>
<hr>
<p><a href="https://www.inflearn.com/course/%EC%A0%9C%EB%8C%80%EB%A1%9C-%ED%8C%8C%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard">https://www.inflearn.com/course/%EC%A0%9C%EB%8C%80%EB%A1%9C-%ED%8C%8C%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard</a></p>
<p>얄코님의 제대로 파는 자바스크립트를 보고 있는데 확실히 모던자바스크립트라는 서적으로만 보면 이해안되는 것을 쉽게 설명 잘해주신다.
항상 기본을 중시하고 항상 복습하도록 하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ES6에서의 객체 (동일한 key명과 value명 처리, 매서드의 의미 )]]></title>
            <link>https://velog.io/@gud_wns/ES6%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%9D%EC%B2%B4-%EB%8F%99%EC%9D%BC%ED%95%9C-key%EB%AA%85%EA%B3%BC-value%EB%AA%85-%EC%B2%98%EB%A6%AC-%EB%A7%A4%EC%84%9C%EB%93%9C%EC%9D%98-%EC%9D%98%EB%AF%B8</link>
            <guid>https://velog.io/@gud_wns/ES6%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%9D%EC%B2%B4-%EB%8F%99%EC%9D%BC%ED%95%9C-key%EB%AA%85%EA%B3%BC-value%EB%AA%85-%EC%B2%98%EB%A6%AC-%EB%A7%A4%EC%84%9C%EB%93%9C%EC%9D%98-%EC%9D%98%EB%AF%B8</guid>
            <pubDate>Sat, 25 Mar 2023 08:25:56 GMT</pubDate>
            <description><![CDATA[<p>퇴근 후 자바스크립트 공부는 적은 시간이지만 꾸준히 하고 있다.
기억해 둘법한 내용들을 간단하게 정리해보자.</p>
<hr>
<h3 id="기존-문법">기존 문법</h3>
<pre><code class="language-javascript">const x = 1, y = 2;

const obj1 = { 
  x: x,
  y: y
}

console.log(obj1);
=&gt; {x: 1, y: 2}</code></pre>
<p>콘솔찍어보기전에는 에러가 날 것 같았는데 x:1, y:2로 할당이 되었다.</p>
<h3 id="es6에서의-문법">ES6에서의 문법</h3>
<pre><code class="language-javascript">function createProduct (name, price, quantity) {
    return {name, price, quantity}
}

createProduct(&#39;선풍기&#39;, 5000, 1)

=&gt; {name: &#39;선풍기&#39;, price: 5000, quantity: 1}</code></pre>
<p>위와 같이 마치 클래스를 찍어내듯 사용할 수 있다.</p>
<hr>
<h3 id="기존문법">기존문법</h3>
<pre><code class="language-javascript">const person = {
  name: &#39;홍길동&#39;,

  salutate: function (formal) {
    return formal
    ? `안녕하십니까, ${this.name}입니다.`
    : `안녕하세요, ${this.name}이에요.`;
  }
}
console.log(person.salutate(true));
=&gt;안녕하십니까, 홍길동입니다.</code></pre>
<pre><code class="language-javascript">const person = {
    name: &#39;홍길동&#39;,

    salutate (formal) {
        return formal
        ? `안녕하십니까? 저는 ${this.name}입니다.`
        : `안녕? 나는 ${this.name}이야`
    }
}

console.log(person.salutate(true));
=&gt;안녕하십니까, 홍길동입니다.</code></pre>
<p>ES6부터는 위의 표현으로 정의된 함수만(function으로 정의한 것이 아닌) 매서드라고 부른다.</p>
<hr>
<p>코드의 길이가 짧아지고 가독성이 좋아진 것 같다.</p>
<p>사실 위 2개의 사례를 보면 현업에서 사용할 가능성은 적지만,
기록해두면 언젠가 써먹을 날이 오겠지..음..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[git action을 이용한 spa프로젝트 s3배포]]></title>
            <link>https://velog.io/@gud_wns/git-action%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-spa%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-s3%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@gud_wns/git-action%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-spa%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-s3%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Thu, 02 Mar 2023 17:23:15 GMT</pubDate>
            <description><![CDATA[<p>최근 일복이 터져 react 프로젝트를 삼주간 매달려서 완성하였다.</p>
<p>바로 다음 프로젝트가 이어졌고, 처음으로 실전에서 개발 환경 구성을 진행하였다.</p>
<p>사실 회사에서도 devops의 개념이 처음 도입되면서, 백엔드 쪽은 다른 분이, 프론트는 내가 맡게 되었고, AWS 버킷생성도 못하는 신입의 입장에서는 굉장히 난감한 상황이었다.</p>
<p>처음에는 많은 경험과 스펙을 쌓을 수 있어 호기롭게 도전했지만 docker, k8s, aws설정방법등에 대해 공부하며 느낀 것은 개발과는 또다른 삽질의 연속이다. 어떻게 하란거지가 아니라 이게 대체 무슨 말이지라는 느낌이었다. 특히 컨테이너개념과 오케스트레이션은 개념은 이해해도 이게 왜 프론트에 필요한지 아직 이해가 되지않는다. 간단하게 s3에 올리고 캐시무효화하면 될 거 같은데 말이다.. 쉽지않은 분야인 것 같다.</p>
<p>아무튼 가장 간단한 방법인 S3에 업로드 -&gt; cloudfront무효화의 과정을 git action을 통해 배포해 보기로 하였고, 과정을 간단하게 기록해보자.</p>
<hr>
<p>기록용이니 정말 간단하게 정리하자면</p>
<blockquote>
</blockquote>
<ol>
<li>route53은 메인 도메인에서 서브도메인을 연결하였고, dev와 prod 두개를 생성한다.</li>
<li>s3 bucket 역시 같은 용도로 2개, cloudfront도 2개 생성.</li>
</ol>
<ul>
<li>route53, s3 bucket, cloudfront의 생성방법은 구글링하면 잘 나와있으니 참고하자.</li>
</ul>
<p>정적 웹페이지 배포는 어려울게 없으나, spa프로젝트는 신경써야 할 것이 좀 있다.
기록해두고 잘안될때는 한번씩 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/ad7ba768-277b-44e2-98c1-44a6833830fb/image.png" alt=""></p>
<ul>
<li>모든 퍼블릭 액세스 차단 여부</li>
<li>버킷 정책 작성
<img src="https://velog.velcdn.com/images/gud_wns/post/2de91db0-1e7f-4fc1-bb9d-b5bbbd77d070/image.png" alt=""></li>
<li>cloudfront 오류 페이지 응답코드 200 반환 (spa특성상 index.html는 하나이기 때문)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/2578d4ec-986f-4438-a460-7d2fc617a79e/image.png" alt=""></p>
<ul>
<li>cloudfront 원본 편집 탭에서 엑세스 제어 설정 (모든 퍼블릭 액세스를 차단했기 때문에 제어 설정을 꼭 해준다. 이거 안해서 3시간 삽질함..)</li>
</ul>
<hr>
<p>내가 작성한 git action workflow.
주석 잘 읽고 실수하지말자</p>
<pre><code class="language-javascript">name: CI/CD

on:
  pull_request:
    types:
      - closed #pr이 닫길때, 즉 merge가 될 때

jobs:
  if_merged:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-18.04 #20.04 버전부터 바뀐 aws cli명령어가 있다. 그래서 버전을 지정해준다.
    steps:
      - run: |
          echo The PR was merged
      - name: Checkout source code
        uses: actions/checkout@master

      - name: Cache node modules # node modules 캐싱
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.OS }}-master-build-${{ hashFiles(&#39;**/yarn.lock&#39;) }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}-

      - name: Install Dependencies
        run: yarn
        env:
          CI: &#39;&#39;

      - name: Build
        run: npm run generate

      # 다음은 브랜치이름에 따라 s3업로드와 cloudfront무효화를 진행한다.
      # 브랜치이름을 변수명으로 만들어서 코드를 간소화 할 필요가 있다.

      - name: Deploy if develop
        if: github.base_ref == &#39;develop&#39;
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-2
          AWS_EC2_METADATA_DISABLED: true
        run: |
          aws s3 sync ./dist s3://프로젝트이름

      - name: Invalidate CloudFront if develop
        if: github.base_ref == &#39;develop&#39;
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          DISTRIBUTION: ${{ secrets.DISTRIBUTION_ID_DEV }}
          PATHS: &#39;/*&#39;
          AWS_REGION: ap-northeast-2
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Deploy if master
        if: github.base_ref == &#39;master&#39;
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-2
          AWS_EC2_METADATA_DISABLED: true
        run: |
          aws s3 sync ./dist s3://프로젝트이름

      - name: Invalidate CloudFront if master
        if: github.base_ref == &#39;master&#39;
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          DISTRIBUTION: ${{ secrets.DISTRIBUTION_ID_PROD }}
          PATHS: &#39;/*&#39;
          AWS_REGION: ap-northeast-2
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
</code></pre>
<p>다른 부분은 이상없이 예상대로 잘 됐는데 가장 삽질한 부분은 s3에 업로드이다.</p>
<pre><code class="language-javascript">- name: Deploy
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          aws s3 cp \
            --recursive \
            --region ap-northeast-2 \
            dist s3://프로젝트이름</code></pre>
<p>처음 구글링 시에는 위와 같은 방법으로 시도를 했는데 dist는 .ignore에 포함되어 있기 때문에 dist폴더를 찾지 못해 deploy에 계속 실패하는 것이다. 여기서 또 4시간 삽질했다.. </p>
<blockquote>
<p>The user-provided path dist does not exist.</p>
</blockquote>
<p>(이런 에러가 계속 떴다.)</p>
<p>결국 검색 후  aws s3 sync 를 사용하여 직접 나의 디렉토리 타깃경로와 s3를 동기화 하여 해결하였다. 아! 그리고 AWS REGION을 추가하고 AWS_EC2_METADATA_DISABLED : true를 꼭 추가하자. METADATA에 관한 부분은 문서를 읽고 있는데 무슨말인지 모르겠다.. 근데 안쓰면 에러가 나서 추가해줬다. AWS는 굉장히 심오하구나..</p>
<p>아무튼 정상적으로 자동 배포까지 성공하였고, merge된 브랜치에 따라 하나의 브랜치에만 deploy되는 것을 확인했다.
앞으로의 과제는 컨테이너 개념을 익히고, ecr push와 버전에 따른 deploy를 위해 tagging을 인식해 적용해볼 계획이다. </p>
<p>typescript도 공부해야하는데.. 하 이번 프로젝트 끝나면 개발공부도 꼭 다시 시작해야겠다. react-query도 공부해봐야지..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[드림코딩 docker 스터디]]></title>
            <link>https://velog.io/@gud_wns/%EB%93%9C%EB%A6%BC%EC%BD%94%EB%94%A9-docker-%EC%8A%A4%ED%84%B0%EB%94%94</link>
            <guid>https://velog.io/@gud_wns/%EB%93%9C%EB%A6%BC%EC%BD%94%EB%94%A9-docker-%EC%8A%A4%ED%84%B0%EB%94%94</guid>
            <pubDate>Mon, 23 Jan 2023 17:52:04 GMT</pubDate>
            <description><![CDATA[<p><strong>최근 사내 조직개편에 따라 front개발과 devops까지 수행하게 되었다.
드림코딩 영상을 참고하여, 간단한 app을 build해서 image를 만드는 과정을 기록해보자!</strong></p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/2c91fc88-c62c-488b-ace2-a96145ffe720/image.png" alt=""></p>
<p>express로 구성한 간단한 백엔드 코드</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/8aed9313-2589-4d03-a159-2c13235fabb0/image.png" alt=""></p>
<p>구성한 dockerfile과 강의 내용 설명</p>
<hr>
<p>dockerfile에서는 명령어 하나하나가 layer형태로 구성되는데</p>
<p>가장 빈번하게 수정될 내용들이 아래로 가는 것이 좋다.
<img src="https://velog.velcdn.com/images/gud_wns/post/a8d99f6a-2d54-47a4-9ebb-60a5aadb6d84/image.png" alt=""></p>
<p>변경된 최상단의 layer들만 업데이트해주고 나머지 layer들은 재사용된다.</p>
<p>image만드는 시간 단축 및 효율성 up!</p>
<hr>
<p>빌드 cli</p>
<ol>
<li><p>docker build -f Dockerfile -t fun-docker .</p>
<ul>
<li>.  (가장 마지막 dot)<ul>
<li>build context이며, 명령어를 수행하는 경로 지정이다 .</li>
<li>“.”은 가장 최상위 루트</li>
</ul>
</li>
<li>-f<ul>
<li>Dockerfile 이름 설정. 기본적으로 Dockerfile을 많이 사용한다.</li>
</ul>
</li>
<li>-t<ul>
<li>docker image의 이름 설정(tag 비슷함)</li>
</ul>
</li>
</ul>
</li>
<li><p>docker images를 이용하면 local machine에 만들어진 image들을 확인 가능</p>
</li>
</ol>
<ol start="3">
<li><p>docker run -d -p 8080:8080 fun-docker</p>
<ul>
<li>-d<ul>
<li>detached. 실행되는 동안 터미널이 대기하지않고, 다른 일을 수행할 수 있게 해준다.</li>
<li>to run a play background!</li>
</ul>
</li>
<li>-p<ul>
<li>host machine의 포트와 container의 포트연결</li>
</ul>
</li>
</ul>
</li>
<li><p>docker ps를 활용하면, 현재 실행중인 container 확인가능</p>
</li>
</ol>
<p>실행확인!<img src="https://velog.velcdn.com/images/gud_wns/post/7a89c142-d4f1-48bc-ae97-402bfc6fd0ec/image.png" alt=""></p>
<ol start="5">
<li>docker tag fun-docker:latest gudjun/privitetest:latest<ul>
<li>뒤에는 dockerhub의 repo이름.</li>
<li>push 하려면 image의 이름을 바꿔줘야한다.</li>
</ul>
</li>
</ol>
<ol start="6">
<li><p>docker login</p>
<ul>
<li>도커 로그인</li>
</ul>
</li>
<li><p>docker push gudjun/privitetest:latest</p>
<ul>
<li>push완료</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/ed1846dc-4ec9-4aab-a636-798f7b383119/image.png" alt=""></p>
<hr>
<p>도커의 필요성과 개념이해만 이틀정도 걸린 것같다...
처음 생활코딩보면서 html, css 공부하던때가 생각나는데
최대한 빨리 회사에서 원하는 능력으로 끌어올려야 할 것 같다.</p>
<p>출처: <a href="https://www.youtube.com/watch?v=LXJhA3VWXFA">https://www.youtube.com/watch?v=LXJhA3VWXFA</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[소수점 계산 오류]]></title>
            <link>https://velog.io/@gud_wns/%EC%86%8C%EC%88%98%EC%A0%90-%EA%B3%84%EC%82%B0-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@gud_wns/%EC%86%8C%EC%88%98%EC%A0%90-%EA%B3%84%EC%82%B0-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Fri, 30 Dec 2022 10:12:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/gud_wns/post/9b701a4c-eb3b-4f6c-8678-6ff63111a284/image.png" alt=""></p>
<p>자바스크립트뿐만 아니라 Java, C언어 등의 다른 언어들에서도 위와 유사한 계산 오류가 발생한다. </p>
<p>컴퓨터가 램에 숫자를 저장할때는 기본적으로
<mark style='background-color: #fff5b1'> 8개의 칸(8bit === 1byte)에 0과 1로 이루어진 2진법으로 저장하게 된다.</mark></p>
<p>그러나 몇몇 소수(ex: 0.1)는 10진법에서 2진법으로 변환하는 과정에서 무한 소수가 되어버린다.</p>
<p>저장공간의 한계가 있는 컴퓨터는 무한 소수를 유한 소수로 인식하게끔 일정 단위 이상 넘어가는 숫자들은 제거하게 되고 여기서 미세한 오차가 발생한다.</p>
<p>정밀도를 위해서는 이른바 칸이라는 것을 늘려주고 싶다면 float(32bit)나 double(64bit)을 사용해 늘려주거나,
<img src="https://velog.velcdn.com/images/gud_wns/post/47f2bcb7-f660-48b6-82ed-ef6a39375e52/image.png" alt="">
와 같이 나타낼 수도 있고, Math객체를 이용한 적절한 반올림을 통해 계산을 할 수도 있다.</p>
<hr>
<p>애플코딩 유튜브 강좌를 보니 미군의 패트리어트가 위와 비슷한 사유로 인해 시간계산의 오차가 나서 적의 미사일을 요격하지 못해 28명의 전사자가 발생하였다는데, 군대에서 유도탄특기로 4년을 복무한지라 더 와닿았던 것 같다.</p>
<p>아무튼 면접에서 관련질문 나올 시 약간이라도 아는 척을 할 수 있게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[null 병합 연산자]]></title>
            <link>https://velog.io/@gud_wns/null-%EB%B3%91%ED%95%A9-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@gud_wns/null-%EB%B3%91%ED%95%A9-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Thu, 29 Dec 2022 14:44:35 GMT</pubDate>
            <description><![CDATA[<p>학습 중 처음보는 연산자를 보았다.
사실 <strong>&amp;&amp;(and)연산자와 ||(or)연산자, 삼항 연산자</strong>로 큰탈없이 업무를 수행하고 있지만, 추후에 활용도가 있어보여 null 병합 연산자에 대해 가볍게 기록한다.</p>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/7ff350aa-0ecc-4048-bd7c-6d681ca2386e/image.png" alt=""></p>
<p><mark style='background-color: #ffdce0'> ?? </mark> 를 활용하여 변수의 값이 null 또는 undefined 일때 뒤에 코드를 실행할 수 있다.</p>
<p>1번은 변수가 선언(초기화)만 상태이기 때문에 undefined(해당 O), 
2번은 0이라는 number를 할당해줬기 때문에 해당안됨,
3번은 null을 직접 할당했기때문에 null(해당 O)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/gud_wns/post/3d766571-3f22-4b67-b314-45a5bc2cba54/image.png" alt=""></p>
<p>주로 산술 연산자에서만 위와 같은 식으로 사용했는데, 병합 할당 연산자로도 사용이 가능하다.</p>
<p>단축평가를 이용한 연산방법인데
x가 0(falsy)이기 때문에 100 실행,
y가 빈문자열(falsy)이기 때문에 앞에 코드만 실행하고 종료,
z는 null이기 때문에 뒤에코드 실행</p>
<hr>
<p>api를 통해 받아오는 data값 중 null이나 undefined를 체크할 때 사용할 수도 있을 것 같아 적어두었다.
사실 조건을 체크할 때 !(부정연산자)를 사용할 수도 있기 때문에 쓸일이 많이 있을까싶긴 하지만 알아두면 좋을 것 같다.</p>
]]></description>
        </item>
    </channel>
</rss>