<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>c-on.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 16 Mar 2022 15:07:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>c-on.log</title>
            <url>https://images.velog.io/images/c-on/profile/7e71471c-3da3-47c0-90ab-257fe5bdeaab/1586590548209-2.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. c-on.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/c-on" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Nginx in EC2]]></title>
            <link>https://velog.io/@c-on/nginx-in-EC2</link>
            <guid>https://velog.io/@c-on/nginx-in-EC2</guid>
            <pubDate>Wed, 16 Mar 2022 15:07:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/42de02f1-6e2f-439d-87ef-47307090130f/image.png" alt=""></p>
<h1 id="nginx">Nginx</h1>
<p>엔진엑스는 동시접속 처리에 특화된 웹서버 프로그램이다. 아파치와 양대산맥을 이루고 있지만 최근 급부상하고 있다. 그 이유는 훨씬 단순하며, 비동기로 동작해서 엄청난 효율을 보여주기 때문이다.</p>
<hr>
<h3 id="역할">역할</h3>
<h4 id="http-서버">HTTP 서버</h4>
<p>html,css나 이미지 등 정적파일들을 앞단에서 전송하는 역할을 한다.</p>
<h4 id="리버스-프록시-서버">리버스 프록시 서버</h4>
<p>프록시서버는 클라이언트의 요청을 받아서 서버에게 전달하는 전달자이다. 리버스 프록시서버를 두게 되면 보안적으로 우세하며, 요청을 배분하는 역할을 한다.</p>
<hr>
<h3 id="장점">장점</h3>
<p>Nginx는 event-driven구조의 비동기 방식으로 동작한다. 새로운 요청이 들어오면 아파치는 쓰레드를 새로 생성해서 처리를 하기 때문에 많은 요청이 들어오면 CPU와 메모리의 소모가 커진다. 반면, 엔진엑스는 프로세스는 고정되어 있고 쓰레드를 새로 생성하지 않기 때문에 적은자원으로 효율적인 운용이 가능해진다.</p>
<hr>
<h3 id="설치">설치</h3>
<pre><code>sudo apt-get install nginx</code></pre><p><code>usr/local/etc/nginx</code>로 이동하면 엔진엑스 파일들을 확인할 수 있다.</p>
<hr>
<h3 id="사용">사용</h3>
<p><code>nginx.conf</code>파일이 엔진엑스의 방식을 지정하는 파일이다.</p>
<pre><code>sudo vi nginx.conf</code></pre><p>파일을 열어보면 다음 구조를 볼 수 있다.</p>
<pre><code class="language-js"># worker 프로세스를 실행할 사용자 설정
user  nginx;

# worker 프로세스 설정
worker_processes  1;

# 오류 로그 파일 경로
error_log  /var/log/nginx/error.log warn;

# NGINX 마스터 프로세스 ID 를 저장할 파일 경로 
pid        /var/run/nginx.pid;


# 접속 처리에 관한 설정을 한다.
events {
    # 워커 프로레스 한 개당 동시 접속 수 지정 (512 혹은 1024 를 기준으로 지정)
    worker_connections  1024;
}

# 웹, 프록시 관련 서버 설정
http {
    # mime.types 파일을 읽어들인다.
    include       /etc/nginx/mime.types;

    # MIME 타입 설정
    default_type  application/octet-stream;

    # 엑세스 로그 형식 지정
    log_format  main  &#39;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#39;
                      &#39;$status $body_bytes_sent &quot;$http_referer&quot; &#39;
                      &#39;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#39;;

    # 엑세스 로그를 남길 파일 경로
    access_log  /var/log/nginx/access.log  main;

    # sendfile api 를 사용 여부
    sendfile        on;
    #tcp_nopush     on;

    # 접속시 커넥션 유지 시간
    keepalive_timeout  65;

    #gzip  on;

    # /etc/nginx/conf.d 디렉토리 아래 있는 .conf 파일을 모두 읽어 들임
    include /etc/nginx/conf.d/*.conf;
}</code></pre>
<h4 id="리버스-프록시-설정">리버스 프록시 설정</h4>
<p>리버스 프록시를 설정하려면 다음과 같은 구조로 작성한다.</p>
<pre><code class="language-js">http {
    server {
        listen 80;
        location / {
            proxy_pass http://127.0.0.1:8080;
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[테스트코드에 관하여]]></title>
            <link>https://velog.io/@c-on/%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@c-on/%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BD%94%EB%93%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Wed, 02 Mar 2022 15:27:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/58687b2c-1b86-423b-a880-5dfa4d8c6976/image.png" alt=""></p>
<h1 id="tdd">TDD</h1>
<p>TDD는 다음 개발과정을 얘기한다.</p>
<blockquote>
<ol>
<li>테스트를 작성한다. (테스트 실패)</li>
<li>코드를 작성한다. (테스트성공)</li>
<li>리팩토링한다.</li>
</ol>
</blockquote>
<p>리팩토링 과정은 TDD의 핵심이라고 볼 수 있는데, 리팩토링을 통해 테스트코드가 가지는 의존성을 최소화하는데 집중한다.</p>
<hr>
<h3 id="목표">목표</h3>
<p>TDD를 통한 목표는 테스트로 개발을 이끌어가는 것이다. 이를 통해 다음의 이점을 가질 수 있다.</p>
<ul>
<li>버그에 대한 두려움을 상쇄시킨다.</li>
<li>더 좋은 코드에 대해 생각하게 된다.</li>
<li>동작하는 문서를 만들 수 있다.</li>
<li>개발 방향을 잡아준다.</li>
</ul>
<hr>
<h3 id="원칙">원칙</h3>
<h4 id="엉클밥의-tdd원칙">엉클밥의 TDD원칙</h4>
<ul>
<li>실패하는 테스트를 작성하기 전에는 절대로 제품 코드를 작성하지 않는다.</li>
<li>실패하는 테스트 코드를 한 번에 하나 이상 작성하지 않는다.</li>
<li>현재 실패하고 있는 테스트를 통과하기에 충분한 정도를 넘어서는 제품 코드를 작성하지 않는다.</li>
</ul>
<h4 id="로버트-마틴의-tdd원칙">로버트 마틴의 TDD원칙</h4>
<ul>
<li>유닛테스트를 통과할 목적이 아니라면 프로덕션 코드를 작성할 수 없다.</li>
<li>유닛테스트가 실패한다면 더 이상 테스트코드를 작성할 수 없다.</li>
<li>실패한 유닛테스트를 통과하게 만든다면, 더 이상 프로덕션 코드를 작성할 수 없다.</li>
</ul>
<h4 id="first-원칙">FIRST 원칙</h4>
<ul>
<li>Fase : 느린것에 대한 의존성 낮추기, Mock이나 stub을 사용하여 파일, 데이터베이스, 네트워크 같은 것들에 대한 의존성을 낮춘다.</li>
<li>Isolated : 최소한의 유닛으로 검증하기, 하나의 테스트는 하나의 기능을 집중적으로 테스트</li>
<li>Repeatable : 언제는 실패, 언제는 성공하는 테스트가 아닌 항상 동일한 결과를 유지할 수 있는 테스트를 작성한다.</li>
<li>Self-Validating : 테스트 코드 내에서 스스로 결과를 검증할 수 있도록 한다.(Jest)</li>
<li>Timely : 사용자에게 배포되기 이전에 테스트코드를 작성</li>
</ul>
<hr>
<br>

<br>

<h1 id="jest">Jest</h1>
<h3 id="구성">구성</h3>
<p>Jest는 자바스크립트의 테스트 프레임워크이다.
NodeJS의 TDD강의를 보면 대부분 jest를 사용하고 있고, nestJS에 내장된 테스트 프레임워크도 jest이다.</p>
<p><code>npm install --save-dev jest</code></p>
<p>설치가 끝나면 다음 스크립트를 추가해서 <code>npm run test</code>로 테스트가 동작되도록 한다.</p>
<pre><code>{
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;jest&quot;
  }
}</code></pre><p>그리고 <code>jest --init</code>으로 jest의 기본 구성파일을 생성한다.</p>
<hr>
<h3 id="사용">사용</h3>
<p>test.js파일을 만들어서 다음 코드를 작성한다.</p>
<pre><code class="language-js">test(&#39;two plus two is four&#39;, () =&gt; {
  expect(2 + 2).toBe(4);
});</code></pre>
<p>그리고 <code>npm run test</code>를 명령하면 테스트코드가 동작하고 하나의 테스트가 성공했음을 보여준다.</p>
<pre><code class="language-js">it(&#39;객체 필드 값 확인&#39;, () =&gt; {
  const data = {one: 1};
  data[&#39;two&#39;] = 2;
  expect(data).toEqual({one: 1, two: 2});
});</code></pre>
<p>위 코드는 <code>toBe</code>를 사용하지 않는다. object의 경우 <code>toBe</code>를 사용하면 주소를 비교해서 테스트에 실패한다. 이럴 땐 <code>toEqual</code>을 통해 검사하면 재귀적으로 모든 필드를 검사한다.</p>
<p>jest는 이와 같이 다양한 매처가 있으므로 <a href="https://jestjs.io/docs/using-matchers">공식문서</a>에서 확인해서 가장 유용한 것을 사용하도록 한다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git CLI]]></title>
            <link>https://velog.io/@c-on/Git-CLI</link>
            <guid>https://velog.io/@c-on/Git-CLI</guid>
            <pubDate>Fri, 11 Feb 2022 08:40:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/8b1b5e85-b9ac-4cf8-8a6e-d42a0f78af64/image.png" alt=""></p>
<h1 id="🔎-버전관리">🔎 버전관리</h1>
<hr>
<h3 id="시작-명령">시작 명령</h3>
<pre><code class="language-bash">git init .</code></pre>
<p><code>.git</code> 이라는 디렉토리가 생기며 이 폴더에서 버전들이 기록된다.</p>
<hr>
<h3 id="버전생성">버전생성</h3>
<p><img src="https://images.velog.io/images/c-on/post/b64f574e-67e2-4998-8a71-89eca7ed0274/image.png" alt=""></p>
<p><code>git add</code> :  파일을 새로 생성하면 깃에게 관리할 파일이 생겼음을 알려줘야 하는데 add를 통해 staging area로 올리면 자동으로 관리상태(track)이 된다. </p>
<p><code>git commit</code> : staging area에 있는 파일로 저장소에 새로운 버전을 생성한다.</p>
<blockquote>
<p><code>git add .</code>를 사용하고 싶다면 추적하지 않을 파일까지 모두 track상태로 전환이 된다. 
이럴 때는 <code>git commit -am</code> 을 사용해서 track 상태의 파일만 커밋해 줄 수 있다.</p>
</blockquote>
<hr>
<h3 id="버전-이름-수정">버전 이름 수정</h3>
<p><code>git commit --amend</code></p>
<hr>
<h3 id="무엇이-바뀌었는지-확인하기">무엇이 바뀌었는지 확인하기</h3>
<p><code>git diff</code> : 수정된 내용에 대해 확인할 수 있다.</p>
<p><code>git log -p</code> : 버전 내역과 수정 사항들을 함께 보여준다.</p>
<hr>
<h3 id="시간여행">시간여행</h3>
<p><code>git checkout &lt;commitID&gt;</code> : 이 커밋 아이디가 생성했던 버전으로 돌아간다.</p>
<p><code>checkout</code>으로 버전 이동을 하고 <code>git log</code>를 통해 커밋 내역을 확인해보면, 현재 버전을 가르키고 있는 <strong>HEAD</strong>의 위치가 변경되고, 그 변경된 커밋이 가장 최근 커밋으로 바뀌어 있을 것이다.</p>
<p><code>git checkout master</code> : 다시 최신상태로 돌아간다.</p>
<hr>
<h3 id="삭제">삭제</h3>
<p>reset은 경계에 대해 잘 이해해야 한다.</p>
<p>어떤 커밋아이디로 리셋한다는 것은 그 버전을 삭제하겠다는 것이 아니라 <strong>그 버전이 되겠다</strong> 이다.</p>
<p><code>git reset [mode]</code></p>
<ul>
<li>hard : 수정하던 것 까지 없앰</li>
<li>soft : 수정하던 것은 살림</li>
</ul>
<hr>
<h3 id="삭제없이-되돌리기">삭제없이 되돌리기</h3>
<p><code>git revert</code></p>
<p>기존의 커밋 내역을 그대로 유지하면서 전달한 커밋 아이디의 버전까지 변경된 사항들을 없앤 버전을 새로 생성하는 것.</p>
<ul>
<li>삭제의 경우 커밋아이디를 전달하면 그 커밋아이디의 버전이 된다. 따라서 한번에 여러 커밋을 건너뛰어 과거로 돌아갈 수 있다.</li>
<li>되돌리기의 경우 커밋아이디를 전달하면 그 커밋의 수정된 사항들을 없애서 새로운 버전을 생성해낸다.<ul>
<li>새로운 버전을 생성한것이므로 커밋내역은 유지가 된다.</li>
<li>버전을 삭제하지 않는다는 장점이 있지만, 한번에 여러 과거를 건너뛰면 수정 사항이 무엇인지 순차적으로 파악이 되지 않으므로 꼭 역순으로 차례차례 revert를 해줄 수 밖에 없다.</li>
</ul>
</li>
</ul>
<br>

<br>

<h1 id="🔎-병합">🔎 병합</h1>
<h3 id="브랜치와-로그">브랜치와 로그</h3>
<p><code>git branch</code>명령으로 새 브랜치를 생성할 수 있으며 <code>git log</code> 로 커밋내역을 볼 수 있다.</p>
<p>이 때 브랜치와 병합과정들을 한눈에 보기 위해선</p>
<pre><code class="language-bash">git log --all --graph --oneline</code></pre>
<p>을 입력한다.</p>
<p>매번 로그를 보기 위해서 저 긴 문장을 입력하기 귀찮으므로 단축키를 생성해줄 수 있다.</p>
<pre><code class="language-bash">git config —global alias.l log --all --graph --oneline</code></pre>
<p>위 코드를 입력하면 <code>git l</code> 만으로 그래프까지 생성되는 로그를 확인할 수 있다.</p>
<hr>
<h3 id="head">HEAD</h3>
<p>head가 가르키는 것이 브랜치면 최종적으로는 그 브랜치가 가르키는 버전을 가르키는 것이다.</p>
<p>반면 헤드가 브랜치가 아닌 커밋을 직접 카르킬 수도 있다. 이처럼 HEAD가 브랜치로 부터 떨어져있는 상태를 <code>detached</code> 상태라고 한다.</p>
<hr>
<h3 id="충돌-발생">충돌 발생</h3>
<p>충돌이 발생하고 파일을 열어보면 <code>===</code> 구분자가 생성되어 있을 것이다.</p>
<p>구분자부터 <code>&lt;&lt;&lt;&lt; HEAD</code> 까지는 HEAD가 가르키고 있는 브랜치의 수정사항.</p>
<p>구분자부터 <code>&lt;&lt;&lt;&lt; new_branch</code> 까지는 new_brance의 수정사항.</p>
<p>이 두가지 수정사항에 충돌이 발생했다는 것이다.</p>
<p>그럼 이제 충돌사항을 해결한 코드를 작성해주고 저장한 다음 다시 <code>git add</code> 를 해줌으로써 충돌 사항이 해결되었음을 git에게 알려준다.</p>
<p>그 후 커밋은 메시지 작성없이 <code>git commit</code> 만 입력하면 된다.</p>
<br>

<br>

<h1 id="🔎-백업">🔎 백업</h1>
<p>회사에서 작업하던 것을 원격 저장소에 백업하고 집에가서 나머지 작업을 위해 데스크탑에 복원을 한다. 이를 위해 git과 github을 사용할 수 있다.</p>
<p>원격저장소는 Github말고도 bitbucket, Gitlab 등이 있다.</p>
<hr>
<h3 id="원격저장소와-연결">원격저장소와 연결</h3>
<p>연결방법은 http와 ssh가 있다. ssh가 보안적으로 더 우세하지만 배울 것이 많으므로 비교적 배울것이 적은 http로 진행한다.</p>
<p>깃헙에 생성한 빈 저장소의 주소를 복사한 뒤 </p>
<p><code>git remote add origin [주소]</code> 를 cmd에 입력한다.</p>
<p>이때 origin은 주소를 모두 입력하기 힘들기에 별칭으로 설정해준 것이라서 다르게 작성할 수 있지만, 관습적으로 원격저장소는 origin을 쓰기로 한다.</p>
<p>그리고 작업해놨던 파일과 내용들을 push하면 첫 업로드가 완료되는 것이다.</p>
<hr>
<h3 id="push">push</h3>
<p>remote후 <code>git push</code> 를 입력하면 <code>git push —set-updtream origin master</code>를 입력하라고 뜬다. 입력해준다. 이는 기본적으로 연결할 원격저장소를 셋팅하는 것으로 반복적으로 push를 할 때 마다 <code>git push origin master</code>를 <code>git push</code>만으로 가능케한다.</p>
<p>원격저장소에 push가 가능하도록 아이디와 비밀번호를 입력해준다.</p>
<hr>
<h3 id="clone">clone</h3>
<p><code>git clone [주소]</code> : 주소의 마지막 즉, 저장소이름을 이름으로 가진 폴더가 생성되고 그 안에 저장소의 파일들이 복원된다.</p>
<hr>
<h3 id="pull">pull</h3>
<p><code>git pull</code> : 원격저장소의 master브랜치에 최종 push된 내용을 끌어온다.</p>
<br>

<br>

<h1 id="🔎-협업">🔎 협업</h1>
<p>원격저장소도 로컬저장소와 마찬가지로 HEAD와 master을 필수로 가진다.</p>
<p>로그그래프로 확인해보면 <code>origin/HEAD</code>, <code>origin/master</code> 로 표시된다.</p>
<p>로컬의 브랜치가 깃헙의 마스터의 브랜치보다 앞서있다면 <code>git push</code> 가 필요하다.</p>
<p>로컬의 브랜치가 깃헙의 마스터 브랜치보다 뒤쳐져 있다면 <code>git pull</code> 이 필요하다.</p>
<hr>
<h3 id="fetch">fetch</h3>
<p>pull과 비슷하지만 다르다.</p>
<p>pull은 앞에서 말했듯 로컬의 브랜치가 깃헙의 마스터 브랜치보다 뒤쳐져있다면 로컬의 브랜치를 앞선 브랜치에 병합한다.</p>
<p><code>fetch</code>는 단순히 원격저장소에서 마스터브랜치의 버전을 가져오지만 로컬의 브랜치는 여전히 뒤쳐져 있도록 두는 것이다.</p>
<p>fetch는 병합 전 확인하는 용도로 사용된다.</p>
<br>

<br>

<h1 id="🔎-체리픽과-리베이스">🔎 체리픽과 리베이스</h1>
<h3 id="체리픽">체리픽</h3>
<p>다른 브랜치의 버전 중 하나를 마스터에 이어 붙이고 싶을 때 사용한다.</p>
<p><code>git cherry-pick [버전번호]</code></p>
<hr>
<h3 id="리베이스">리베이스</h3>
<p><code>base</code> 란 두 브랜치가 공통으로 가진 버전을 말한다.</p>
<p><code>rebase</code> 는 말 그대로 한 브랜치의 베이스를 재 설정해주는 것이다.</p>
<p>단, 리베이스는 새로운 버전을 생성하는 것이 아니라 버전을 잘라서 붙이는 역할이기에 원격저장소에 push 되기 전에 수행해야 한다. 그렇지 않으면</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NodeJS 파일시스템에 관하여]]></title>
            <link>https://velog.io/@c-on/NodeJS-%ED%8C%8C%EC%9D%BC%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@c-on/NodeJS-%ED%8C%8C%EC%9D%BC%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Mon, 07 Feb 2022 01:10:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/09734490-dc69-4252-bde3-bbdd6461e0e6/image.png" alt="">
<em><strong>참고 : nodeJS교과서 by 조현영</strong></em></p>
<br>

<h1 id="🔎-fs-모듈">🔎 fs 모듈</h1>
<p>파일을 생성하거나 삭제하고, 읽거나 쓸 수 있으며 폴더를 만들었다 지웠다 할 수 있다.</p>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
fs.readFile(&#39;./readme.txt&#39;, (err, data) =&gt; {
  if (err) {
    throw err;
  }
  console.log(data);
  console.log(data.toString());
});</code></pre>
<p>에러가 발생하지 않으면 그 다음코드인 <code>console.log</code>가 실행되며 콘솔에는 예기치 못한 Buffer가 출력될 것이다.</p>
<p><code>toString</code> 메서드를 사용해보면 정상적으로 문자가 출력된다.</p>
<p><code>readfile</code>의 결과는 버퍼 형식으로 제공되기 때문이다.</p>
<hr>
<h3 id="비동기">비동기</h3>
<p>fs모듈의 <code>readfile</code>은 비동기로 동작한다.
함수가 동작되면 백그라운드에 파일읽기 명령을 요청하고 다음 작업으로 넘어간다.</p>
<p>읽기가 완료된 요청은 메인스레드에 알림을 주고 메인스레드는 그 때 콜백함수를 실행한다.</p>
<p>이 방식을 활용하면 수백개의 I/O요청에도 메인스레드는 요청처리만 위임하며 처리 완료 알림을 받았을 때만 콜백함수를 처리함으로써 많은 요청을 받아낼 수 있다.</p>
<blockquote>
<p>동기와 비동기의 차이는 함수가 return 되는가이다.
블로킹과 논블로킹의 차이는 백그라운드 작업 완료 여부이다.</p>
</blockquote>
<hr>
<h3 id="동기">동기</h3>
<p><code>readfile</code>을 동기적으로 사용하기 위해서는 <code>readfileSync</code>를 사용해야 한다.</p>
<p>이 함수는 콜백함수를 넣는 대신에 return 값을 받아서 사용한다.</p>
<p>동기메서드를 사용하는 경우는 극히 드물며 비동기 메서드가 훨씬 효율적이기 때문에 웬만해선 비동기메서드를 사용한다.</p>
<hr>
<br>

<br>

<h1 id="🔎-버퍼와-스트림">🔎 버퍼와 스트림</h1>
<p>노드는 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련해두며, 파일 데이터를 메모리에 저장한 뒤 사용자가 조작할 수 있도록 해준다.
여기서 메모리에 저장된 데이터를 버퍼라고 한다.</p>
<hr>
<h3 id="buffer">Buffer</h3>
<p>버퍼 객체의 메서드</p>
<ul>
<li><code>from(문자열)</code>: 문자열을 버퍼로 바꾼다. <ul>
<li><code>.length</code> : 버퍼의크기를 알려줍니다. 바이트 단위입니다.</li>
</ul>
</li>
<li><code>toString(버퍼)</code> : 버퍼를 다시 문자열로 변환</li>
<li><code>concat(배열)</code>: 배열 안에 든 버퍼들을 하나로 합침</li>
<li><code>alloc(바이트)</code>: 빈 버퍼를 생성, 바이트를 인자로 지정해주면 해당 크기의 버퍼가 생성된다</li>
</ul>
<hr>
<h3 id="stream">stream</h3>
<p><code>readfile</code>은 모든 내용을 버퍼에 다 쓴 후에야 다음 동작으로 넘어가므로 파일 읽기, 압축, 파일 쓰기 등의 조작을 연달아 할 때 매번 전체 용량을 버퍼로 처리해야 다음 단계로 넘어갈 수 있다.</p>
<p>때문에 버퍼의 크기를 작게 쪼개서 여러번 보내는 스트림 방식이 등장했다.</p>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const readStream = fs.createReadStream(&#39;./readme3.txt&#39;, { highWaterMark: 16 });
const data = [];

readStream.on(&#39;data&#39;, (chunk) =&gt; {
  data.push(chunk);
  console.log(&#39;data :&#39;, chunk, chunk.length);
});

readStream.on(&#39;end&#39;, () =&gt; {
  console.log(&#39;end :&#39;, Buffer.concat(data).toString());
});

readStream.on(&#39;error&#39;, (err) =&gt; {
  console.log(&#39;error :&#39;, err);
});</code></pre>
<p><code>createReadStream()</code>은 읽기 스트림을 만들어준다.
첫번째 인자는 읽을 경로이며, 두번째 인자는 옵션 객체이다.
옵션 중 <code>highWaterMark</code>를 설정하여 버퍼의 크기를 정할 수 있다.</p>
<p>이벤트 리스너를 붙여서 사용한다. 대개 <code>data</code>, <code>end</code>, <code>error</code>를 사용한다.
읽기가 시작되면 data, 다 읽으면 end, 에러가 발생하면 error 이벤트가 발생한다.</p>
<hr>
<h3 id="pipe">pipe</h3>
<p>파일을 읽는 스트림과 파일을 쓰는 스트림을 연결할 수 있는데 이를 파이핑이라하며 <code>pipe</code> 메서드를 사용해서 구현한다.</p>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const readStream = fs.createReadStream(&#39;readme4.txt&#39;);
const writeStream = fs.createWriteStream(&#39;writeme3.txt&#39;);
readStream.pipe(writeStream);</code></pre>
<hr>
<br>

<br>

<h1 id="🔎-fs-메서드">🔎 fs 메서드</h1>
<ul>
<li><p><code>fs.mkdir(경로, 에러콜백)</code> : 폴더를 만드는 메서드, 이미 폴더가 있다면에러가 발생하므로 먼저 존재 확인 메서드를 호출해서 확인 필요</p>
</li>
<li><p><code>fs.open(경로, 옵션, 콜백(에러, 아이디))</code> : 파일의 아이디를 가져오는 메서드, 파일이 없다면 파일을 생성한 뒤 그 아이디를 가져온다. 가져온 아이디를 사용해 <code>fs.read()</code>나 <code>fs.write()</code>로 읽거나 쓸 수 있다.
두 번째 인자로 어떤 동작을 할 것인지 설정할 수 있습니다. 쓰려면 <code>&#39;w&#39;</code>,읽으려면 <code>&#39;r&#39;</code>, 기존 파일에 추가하려면 <code>&#39;a&#39;</code>이다.</p>
</li>
<li><p><code>fs.rename(기존 경로, 새 경로, 콜백)</code> : 파일의 이름을 바꾸는 메서드입니다. 기존 파일 위치와 새로운 파일 위치를 적어준다. 파일을 이동할 때 활용된다.</p>
</li>
<li><p><code>fs.readdir(경로, 콜백(에러, 결과))</code> : 폴더 안의 내용물을 확인, 배열안에 내부 파일과 폴더명이 나온다.</p>
</li>
<li><p><code>fs.unlink(경로, 에러콜백)</code> : 파일을 삭제. 파일이 없다면 에러가발생하므로 먼저 파일이 있는지를 꼭 확인한다.</p>
</li>
<li><p><code>fs.rmdir(경로, 에러콜백)</code> : 폴더를 삭제. 폴더 안에 파일이 있다면 에러가 발생하므로 먼저 내부 파일을 모두 지우고 호출</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[express에서의 비동기 에러처리]]></title>
            <link>https://velog.io/@c-on/winston-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%99%9C-%EA%B7%B8%EB%A0%87%EA%B2%8C-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94%EC%A7%80</link>
            <guid>https://velog.io/@c-on/winston-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%99%9C-%EA%B7%B8%EB%A0%87%EA%B2%8C-%EC%A0%81%EC%9A%A9%ED%95%98%EB%8A%94%EC%A7%80</guid>
            <pubDate>Fri, 04 Feb 2022 06:15:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/ab290a70-5546-48e1-9f06-b8b11c196344/image.png" alt=""></p>
<p>시퀄라이즈 orm의 API는 대부분 promise를 반환한다.</p>
<p>내 프로젝트 코드에는 프로미스 에러처리가 하나도 되어있지 않았었다. </p>
<p>때문에, 비동기 함수가 에러를 던지는 경우 적절한 응답을 반환하지 않거나 서버가 죽어버렸다.</p>
<p>스택오버플로우에 express의 비동기 에러처리를 검색했고, 가장 많은 추천을 받은 두가지 해결법을 적절히 섞어서 적용했다.</p>
<p><a href="https://stackoverflow.com/questions/51391080/handling-errors-in-express-async-middleware">https://stackoverflow.com/questions/51391080/handling-errors-in-express-async-middleware</a></p>
<h2 id="방법1-express-async-errors-모듈">방법1. express-async-errors 모듈</h2>
<p>처음 적용한 방법은 에러 핸들링 모듈이다.</p>
<p><a href="https://www.npmjs.com/package/express-async-errors">사용방법</a>이 간단하지만 내가 원하는 비동기 에러처리에 특화된 모듈이어서 가장 먼저 적용해보았다.</p>
<pre><code class="language-jsx">app.use((err, req, res, next) =&gt; {
  console.error(err.message);
  res.sendStatus(500);
});</code></pre>
<p>에러를 받는 미들웨어를 app을 생성한 파일에 작성해준다.</p>
<pre><code class="language-jsx">import &quot;express-async-errors&quot;</code></pre>
<p>비동기 에러 발생이 예상되는 파일에서 <code>express-async-errors</code> 를 import 해준다. </p>
<p>여기까지 완료하면 비동기 에러가 발생했을 때 던져진 에러는 에러처리 미들웨어에 잡혀 처리가 된다.</p>
<h2 id="방법2-trycatch">방법2. try..catch</h2>
<p>방법1은 적용하기 간편한 것이 장점이지만 각각의 에러를 하나의 미들웨어로 밖에 관리하지 못한다는 단점이 있다. </p>
<p>따라서 예측이 가능한 에러는 <code>try...catch</code>문을 사용해서 대응코드를 작성해주거나 에러 상황을 던지도록 했다. </p>
<p>던져진 에러는 express-async-errors가 최종 에러처리 미들웨어로 전달하고 에러 로그가 기록이 된다. 이 방식은 <a href="https://velog.io/@hopsprings2/node.js-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EC%9D%98-%EB%86%93%EC%B9%9C-%EB%B6%80%EB%B6%84%EC%9D%84-%EC%88%98%ED%98%B8%ED%95%B4%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4">로그를 어떻게 남기면 되는지에 대해 정리된 블로그</a>를 참고한 중앙집중식 핸들링 방식을 적용한 것이다.</p>
<p>중앙집중식 핸들링 방식을 사용하면  반복적인 코드 작성을 피할 수 있고 공통적인 에러 처리를 한번에 관리할 수 있다. 예를 들어 모든 에러처리가 콘솔에 로그를 기록하고 있었는데 콘솔 로그의 비효율성을 해결하기 위해 winston모듈로 교체했을 때 모든 <code>try...catch</code> 문을 찾아다니며 코드 변경을 할 필요가 없다는 것이다.</p>
<p>실제로 나는 로그를 <code>console</code> 을 사용하다가 <a href="https://velog.io/@ash/Node.js-%EC%84%9C%EB%B2%84%EC%97%90-logging-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-winston-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0">winston모듈 사용에 대해 정리한 블로그</a>를 참고해서 winston모듈로 교체하여 기록했다. nodeJS의 <code>console</code> API는 log와 error가 콘솔에 찍히는 것에 큰 차이가 없어서 분간이 어려웠고, 실제 서버의 콘솔에는 로그를 찍지 않을 것이기 때문에 파일로 기록하는 코드를 직접 짜야했다.</p>
<p>반면, winston은 <code>format.colorize()</code>로 로그의 레벨에 따라 출력되는 로그에 구분 색칠을 해주고 <code>winston-daily-rotate-file</code> 모듈을 함께 사용해서 레벨에 따른 기록 파일을 생성하고 유효기간을 설정해 줄 수 있다. 이외에도 로그와 관련된 편리하고 유용한 기능이 많아서 winston을 사용하기로 결정했다.</p>
<h2 id="로그를-어떤-식으로-기록할지">로그를 어떤 식으로 기록할지</h2>
<p>모든 비동기 함수에 에러처리를 작성하면 프로그램 가독성이 떨어질 수 있다. </p>
<p>오류가 발생할 수 있을 것이라고 가정되는 코드는 <code>try</code>문의 시작과 끝에 작성하고 오류가 발생하지 않는다고 가정할 수 있을 코드는 시작과 끝의 가운데에 배치해서 <code>try...catch</code>가 어떤 함수의 오류를 대응하는지 확인하기 쉽도록 한다.</p>
<p><code>catch</code>문에서는 에러 핸들러에게 던지는 오류가 어디서 발생했는지 알기 쉽도록 실패한 연산이나 유형의 이름을 함께 던져준다. </p>
<p><img src="https://images.velog.io/images/c-on/post/2e9782f2-5c05-4553-b09f-48cce05c7f9a/image.png" alt=""></p>
<p><code>d.ts</code>파일을 확인해보니 에러는 위와같이 이름과 메시지를 필수 프로퍼티로 가지고있다.</p>
<p>에러를 템플릿 리터럴로 문자열에 포함시켜 출력하면 <code>name : message</code> 형태로 출력이 되었다.</p>
<p>나는 이것을 이용해서 <code>catch</code>가 에러를 잡고 새로운 에러를 중앙으로 던질 때, 템플릿 리터럴 안에 에러상황과 에러를 함께 넣어서 던져주었고 결과적으로 다음과 같이 콘솔에 출력을 시킬 수 있었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/596ce0bd-f4f0-4a47-8540-f13e5f47f8a0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript SOLID 예제]]></title>
            <link>https://velog.io/@c-on/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-SOLID-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@c-on/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-SOLID-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Thu, 27 Jan 2022 08:49:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/d8677f55-89f2-459d-a255-a7767338bf58/image.png" alt=""></p>
<h1 id="🔎-solid">🔎 SOLID</h1>
<p>solid는 객체지향 프로그래밍의 설계 원칙을 다섯가지로 정의한 것이다. 유지보수와 확장에 유연한 프로그래밍을 하기위해서 SOLID원칙을 적용한다.</p>
<hr>
<h3 id="srp--단일책임원칙">Srp : 단일책임원칙</h3>
<blockquote>
<p>클래스를 수정할 땐 수정할 이유가 2개 이상 생기면 잘못 설계된 것으로 본다.</p>
<p>2개 이상의 이유는 너무 많은 기능들을 한 클래스가 수행한다는 의미가 되기 때문이다.</p>
</blockquote>
<h4 id="잘못된-예">잘못된 예</h4>
<pre><code class="language-js">class cafeOwner {
  constructor(coffeebeans) {
    this.coffeebeans = coffeebeans;
  }

  manageShop(time){
      console.log(`managing coffee shop at ${time}`)
  }

  makeCoffee(coffeebeans) {
    console.log(`making cofffe with ${coffeebeans}`)
  }

  serveCoffee(guest) {
    console.log(`serving coffee to ${guest}`)
  }
}</code></pre>
<p>카페사장이 가게도 관리하고 커피도 만들고 서빙도하고 있다. </p>
<p>커피가 너무 쓴 것 같다는 컴플레인을 받아서 커피를 만들 때 원두양을 1/2로 줄이려한다.</p>
<p>따라서 <code>makeCoffee()</code> 메서드를 수정할것이다.
수정으로 인해 다른 곳에 영향을 끼치지는 않는 지 확인이 필요하다.</p>
<p>그런데 원두의 양을 변경하는 것과 전혀 관련이 없는 <code>manageShop()</code>메서드까지 확인을 해주려니 너무 비효율적이라는 생각이 든다.</p>
<h4 id="올바른-예">올바른 예</h4>
<pre><code class="language-js">class coffemaker {
  makeCoffee(coffeebeans) {
    console.log(`making cofffe with ${coffeebeans}`)
  }
}

class coffeeServer{
  serveCoffee(guest) {
    console.log(`serving coffee to ${guest}`)
  }
}

class cafeOwner {
  coffee;
  constructor(coffeebeans) {
    this.coffeebeans = coffeebeans;
  }

  manageShop(time){
      console.log(`managing coffee shop at ${time}`)
  }
}
</code></pre>
<p>이렇게 책임을 나누면 클래스를 체크해야 하는 범위가 줄어들어 관리가 쉬워질 것이다.</p>
<hr>
<h3 id="ocp--개방-폐쇠-원칙">Ocp : 개방 폐쇠 원칙</h3>
<blockquote>
<p>확장에는 개방적이며, 수정에는 폐쇠적이어야 한다는 원칙이다.</p>
<ul>
<li>기능 추가가 필요할 때 기존 코드의 수정이 일어나지 않도록 한다.</li>
<li>내부 매커니즘이 변경되어도 외부에는 코드변화가 없어야 한다.</li>
</ul>
<p>이 두가지를 만족시켜야 하는 원칙이다.</p>
</blockquote>
<h4 id="잘못된-예-1">잘못된 예</h4>
<pre><code class="language-js">class LatteMaker {
  coffee = &quot;Latte&quot;;
}

class TeaMaker {
  coffee = &quot;Blacktea&quot;
}

class cafeOwner {
  constructor(maker, server) {
    this.maker = maker
  }

  makeCoffee(){
    if(this.maker.coffee===&quot;Latte&quot;){
      brewingLatte()
    }else if(this.maker.coffee===&quot;Tea&quot;){
      brewingBlacktea()
    }
  }
}

function brewingLatte(){
  console.log(`making coffee with Milk`)
}

function brewingBlacktea(){
  console.log(`making coffee without Milk`)
}</code></pre>
<p>현재 카페사장은 라떼머신이 무슨 커피가 담겨있는지 확인한 뒤 라떼를 만들 수 있고, 홍차머신이 무슨 커피가 담겨있는지 확인한 뒤 홍차를 만들 수 있다.</p>
<p>분명 두 머신은 구분되어있는데 이렇게 일일히 확인하고 커피를 내리는 것은 정말 비효율적인 작업이다. 
실제로 이렇게 커피를 내린다면 답답한 손님들은 더 이상 이 가게를 찾지 않을 것이다.</p>
<p>안에 들어있는게 무엇인지 확인할 필요없이 머신이 알아서 커피를 내려주도록 수정해보자.</p>
<h4 id="올바른-예-1">올바른 예</h4>
<pre><code class="language-js">class LatteMaker {
  coffee = &quot;Latte&quot;;
  brewingCoffee(){
    console.log(`making coffee with Milk`)
  }
}

class BlackteaMaker {
  coffee = &quot;Blacktea&quot;
  brewingCoffee(){
    console.log(`making coffee without Milk`)
  }
}

class cafeOwner {
  constructor(maker, server) {
    this.maker = maker
  }

  makeCoffee(){
    this.maker.brewingCoffee()
  }
}</code></pre>
<p>이제 카페사장은 필요한 커피 머신을 받아서 <code>makeCoffee()</code>버튼만 누르면 머신이 알아서 커피를 내릴 수 있게 되었다.</p>
<hr>
<h3 id="liskov--리스코프-치환-원칙">Liskov : 리스코프 치환 원칙</h3>
<blockquote>
<p>부모클래스와 자식클래스가 있다면 부모는 자식으로 교체되어도 프로그램이 정상 동작해야한다는 원칙이다.</p>
<p>올바르지 못한 복제는 오류를 야기한다.
따라서 상속을 할때는 리스코프 치환원칙을 지키도록 설계해야한다.</p>
</blockquote>
<h4 id="잘못된-예-2">잘못된 예</h4>
<pre><code class="language-js">class TeaMaker {
  coffee = &quot;&quot;

  makeCoffee(){
    this.coffee += &quot;🍵&quot;
  }

  serveCoffee(){
    console.log(`${this.coffee}서빙 완료`)
  }
}

class LatteMaker extends TeaMaker{}

const coffeeMaker = [new TeaMaker(), new TeaMaker(), new LatteMaker()]

coffeeMaker.forEach((maker)=&gt;{
  try{
    maker.makeCoffee()
    maker.serveCoffee() 
  }catch(err){
    console.log(err)
  }
})</code></pre>
<p><img src="https://images.velog.io/images/c-on/post/312341fc-03d5-4e02-a901-07ece3e492f6/image.png" alt=""></p>
<p>찻집 가게에는 <code>teaMaker</code>가 2대있었다.
<code>teaMaker</code>는 차를 내리고 서빙까지 해주는 첨단 로봇으로 전자공학부 출신인 가게 사장이 만들었다. </p>
<p>사장은 <code>teaMaker</code>를 복제하여 <code>LatteMaker</code>를 만들어 두고 알바한테 잘 사용하라고 편지를 남긴 뒤 휴가를 떠났다.</p>
<p>손님이 와서 녹차 2잔과 라떼 1잔을 주문했다.</p>
<p>알바는 녹차 2잔은 <code>teaMaker</code>에게, 라떼 1잔은 <code>LatteMaker</code>에게 차를 내린 후 서빙하라고 명령했다.</p>
<p>서빙이 완료되고 손님은 왜 녹차가 3잔이 왔냐며 알바를 구박했다.</p>
<p>사장이 복제를 잘못해서 발생한 실수이다.</p>
<p><em><strong>LatteMaker는 teaMaker가 고장났을 때 대신 차를 내려주지 못한다. 즉, 치환되지 못한다.
그러므로 사장은 복제를 하면 안되는 것이었다.</strong></em></p>
<h4 id="올바른-예-2">올바른 예</h4>
<pre><code class="language-js">class coffeeMaker {
  coffee = &quot;&quot;

  serveCoffee(){
    console.log(`${this.coffee}서빙 완료`)
  }
}

class TeaMaker extends coffeeMaker {
  makeCoffee(){
    this.coffee += &quot;🍵&quot;
  }
}

class LatteMaker extends coffeeMaker{
  makeCoffee(){
    this.coffee += &quot;☕&quot;
  }
}</code></pre>
<p>복제를 하려했다면 위 코드와 같이 서빙기능을 수행하는 로봇을 만들어서 그것을 복제하여 다양한 커피머신을 만들었어야 했다.</p>
<p><em><strong>TeaMaker, LatteMaker 모두 coffeeMaker가 하는 일들을 모두 수행할 수 있다. 따라서 Lsp원칙을 만족하므로 복제를 해도 괜찮은 것이다.</strong></em></p>
<hr>
<p>자바스크립트는 인터페이스 기능이 없다.
<strong>ISP</strong>와 <strong>DIP</strong>는 이해를 위해 자바스크립트와 유사한 문법으로 인터페이스를 구현할 수 있는 타입스크립트를 사용하여 예시를 작성했다.</p>
<hr>
<h3 id="isp--인터페이스-분리-원칙">Isp : 인터페이스 분리 원칙</h3>
<blockquote>
<p>사용자는 자신이 이용하지 않는 메소드에 의존할 필요가 없어야 한다는 원칙이다.</p>
<p>큰 덩어리의 인터페이스들을 더 작은 단위로 분리시킴으로써 사용자는 꼭 필요한 메서드들만 이용할 수 있게 된다.</p>
<p>앞에서 말했듯 자바스크립트에서는 인터페이스기능이 없다. 인터페이스를 사용하려면 타입스크립트를 활용해야 한다.</p>
</blockquote>
<h4 id="잘못된-예-3">잘못된 예</h4>
<pre><code class="language-ts">interface robot{
  brew() : void;
  serve() : void;
}

class oldCoffeeRobot implements robot {
  brew(){
    console.log(&#39;커피 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }
}

class newCoffeRobot implements robot {
  brew(){
    console.log(&#39;커피 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }

  clean(){
    console.log(&#39;자체 청소 완료&#39;)
  }
}</code></pre>
<p>최신 커피로봇은 청소기능이 추가되었다.
하지만 사용자는 이 기능을 사용할 수 없으며 있는지도 알 수 없다.</p>
<p>오래된 커피로봇의 규격서가 최신 커피로봇에 동일하게 적용되어있기 때문이다.</p>
<h4 id="올바른-예-3">올바른 예</h4>
<pre><code class="language-ts">interface robot{
  brew() : void;
  serve() : void;
}

interface cleanableRobot extends robot{
  clean() : void;
}

class oldCoffeeRobot implements robot {
  brew(){
    console.log(&#39;커피 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }
}

class newCoffeRobot implements cleanableRobot {
  brew(){
    console.log(&#39;커피 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }

  clean(){
    console.log(&#39;자체 청소 완료&#39;)
  }
}</code></pre>
<p>이렇게 인터페이스를 쓰임새에 맞게 작게 만들어 사용자가 필요한 메소드만 확인할 수 있도록 만드는 것이 ISP원칙이다.</p>
<hr>
<h3 id="dip--의존성-역전-원칙">Dip : 의존성 역전 원칙</h3>
<blockquote>
<ol>
<li>상위 모듈은 하위 모듈에 종속되지 않고 추상화에 의존해야 한다</li>
<li>세부사항 역시 추상화에 의해 달라져야 한다.</li>
</ol>
</blockquote>
<h4 id="잘못된-예-4">잘못된 예</h4>
<pre><code class="language-ts">interface robot{
  brew() : void;
  serve() : void;
}

class fastCoffeeRobot implements robot {
  brew(){
    console.log(&#39;더 빠르게 아메리카노 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }
}

class CafeOwner {
  constructor(){
    const coffeeRobot = new fastCoffeeRobot()
    coffeeRobot.brew()
    coffeeRobot.serve()
  }
}

new CafeOwner()</code></pre>
<p>새로 고용한 카페사장은 항상 <code>fastCoffeeRobot</code>만 사용할 수 있다.</p>
<p>최근 빠른 장비보다 안전한 장비가 개발되면서 <code>safeCoffeRobot</code>이 개발이 되었다.</p>
<p>이에 따라 카페의 특성에 맞게 어떤 카페에는 <code>safeCoffeRobot</code>를 사용하는 사장을, 어떤 카페에는 <code>fastCoffeeRobot</code>를 사용하는 사장을 배치시키고 싶다.</p>
<h4 id="올바른-예-4">올바른 예</h4>
<pre><code class="language-ts">interface robot{
  brew() : void;
  serve() : void;
}

class fastCoffeeRobot implements robot {
  brew(){
    console.log(&#39;더 빠르게 아메리카노 내리는 중&#39;)
  }

  serve(){
    console.log(&#39;서빙 완료!&#39;)
  }
}

class safeCoffeRobot implements robot {
  brew(){
    console.log(&#39;아메리카노 내리기&#39;)
  }

  serve(){
    console.log(&#39;안전하게 서빙 완료!&#39;)
  }
}

class CafeOwner {
  constructor(myRobot : robot){
    myRobot.brew()
    myRobot.serve()
  }
}

const machine1 = new fastCoffeeRobot()
const cafeOwner1 = new CafeOwner(machine1)

const machine2 = new safeCoffeRobot()
const cafeOwner2 = new CafeOwner(machine2)</code></pre>
<p>카페 사장은 특정 기계를 가지는 것이 아닌 <code>mechine</code>이라는 규격만 맞는다면 그게 어떤 기계이든 사용할 수 있다. 따라서 <code>fastCoffeeRobot</code>과 <code>safeCoffeRobot</code> 중 어떤 것을 전달해주더라도 오류가 발생하지 않는다.
이러한 방식을 DI(의존성주입)이라한다.</p>
<p>의존성주입을 사용하면 규격에 맞는 부품을 유연하게 갈아끼워 줄 수 있기 때문에 리팩토링을 하기 수월해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[aws 프리티어 요금 확인]]></title>
            <link>https://velog.io/@c-on/aws-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EC%9A%94%EA%B8%88-%ED%99%95%EC%9D%B8</link>
            <guid>https://velog.io/@c-on/aws-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%EC%9A%94%EA%B8%88-%ED%99%95%EC%9D%B8</guid>
            <pubDate>Wed, 26 Jan 2022 01:12:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/7468a7d3-6fe1-427b-ba42-2840a41c09c0/image.png" alt=""></p>
<p><a href="https://aws.amazon.com/ko/free/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;all-free-tier.sort-order=asc&amp;awsf.Free%20Tier%20Types=*all&amp;awsf.Free%20Tier%20Categories=*all">프리티어 제품별 요금 확인</a></p>
<h1 id="ec2">EC2</h1>
<p><img src="https://images.velog.io/images/c-on/post/76324a1f-4edc-4240-8943-19f8a85ff9a6/image.png" alt=""></p>
<blockquote>
<p>12개월 무료</p>
</blockquote>
<hr>
<h1 id="elb">ELB</h1>
<p><img src="https://images.velog.io/images/c-on/post/253741bc-aa28-43a4-9f5c-2cc421453eee/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/c-on/post/f981ab24-94c1-429e-be46-2058d0f576db/image.png" alt=""></p>
<blockquote>
<p>12개월무료 </p>
</blockquote>
<hr>
<h1 id="certificate-manager">Certificate Manager</h1>
<p><img src="https://images.velog.io/images/c-on/post/e5f63035-6edb-4def-9bce-631826f290cf/image.png" alt=""></p>
<blockquote>
<p>무료.</p>
</blockquote>
<hr>
<h1 id="s3">s3</h1>
<p><img src="https://images.velog.io/images/c-on/post/30df1207-62a6-4a51-b73e-c12163162547/image.png" alt=""></p>
<blockquote>
<p>12개월 5GB 무료</p>
</blockquote>
<hr>
<h1 id="rds">RDS</h1>
<p><img src="https://images.velog.io/images/c-on/post/a0bba044-8b6b-45d3-b435-c33d304da579/image.png" alt=""></p>
<blockquote>
<p>12개월 무료</p>
</blockquote>
<hr>
<h1 id="lambda">Lambda</h1>
<p><img src="https://images.velog.io/images/c-on/post/798dccd2-494a-4d4d-86de-e10d6c4a7e15/image.png" alt=""></p>
<blockquote>
<p>월 백만 요청까지 평생 무료</p>
</blockquote>
<hr>
<h1 id="route-53">Route 53</h1>
<p><img src="https://images.velog.io/images/c-on/post/41961713-67b7-47fa-aa55-f95a250de993/image.png" alt=""></p>
<blockquote>
<p>처음 25개까지 $0.5</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[nodeJS 서버 AWS에 배포하기 - (4) route53, SSL, 로드밸런서, Nginx]]></title>
            <link>https://velog.io/@c-on/AWS-nodeJS%EC%95%B1-%EB%8F%84%EB%A9%94%EC%9D%B8%EC%97%B0%EA%B2%B0-%EB%B0%8F-SSL%EC%84%A4%EC%A0%95Route53Certificate-Manager%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1NGINX</link>
            <guid>https://velog.io/@c-on/AWS-nodeJS%EC%95%B1-%EB%8F%84%EB%A9%94%EC%9D%B8%EC%97%B0%EA%B2%B0-%EB%B0%8F-SSL%EC%84%A4%EC%A0%95Route53Certificate-Manager%EB%A1%9C%EB%93%9C%EB%B0%B8%EB%9F%B0%EC%8B%B1NGINX</guid>
            <pubDate>Sat, 22 Jan 2022 04:56:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/a0ebb211-e4f4-49d5-a324-31f69525e025/image.png" alt=""></p>
<h1 id="🔨-도메인-구매">🔨 도메인 구매</h1>
<p>이제 도메인을 구매해서 인스턴스의 ip와 연결해줄 것이다. AWS는 Route53을 통해 도메인과 ip를 연결해주는 서비스를 제공한다.</p>
<p>일단 도메인을 구매해야하는데 나는 <a href="https://www.gabia.com/">가비아</a>에서 구매를 했다.
도메인 하나를 구입해놓고 서브도메인으로 프로젝트를 연결해주고 있는데 원래는 문어발처럼 관련없는 서비스끼리 한 도메인에 묶이면 안되겠지만 난 가난한 백수기에 그냥 서브도메인으로 프로젝트 서버 ip를 매핑해주고 있다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e63459cb-f233-4546-b446-abd0faff48ad/image.png" alt=""></p>
<p>원하는 도메인을 검색해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/5036be3d-792a-410f-a4ea-1e6f64d3f8f2/image.png" alt=""></p>
<p>이렇게 이벤트하는 도메인이 촤라락 나온다.
보통 탑레벨 도메인이 com이나 co.kr같은 것은 비싸고 사진처럼 shop 같은 것들은 500원 주고 1년 사용할 수 있다. 1년 이후에 연장을 해본적은 없어서 이벤트값이 적용되는 지 잘 모르겠다. 여튼 1년까지는 500원만으로 사용이 가능하다는 것!</p>
<p><img src="https://images.velog.io/images/c-on/post/fc028040-cd9c-40cf-854f-5fa68640d20d/image.png" alt=""></p>
<p>참고로 도메인 구조는 위와 같고 우리가 구입하는 것은 1단계+3단계 도메인을 구입하는 것이다. 서브도메인은
구매한 도메인 판매 사이트에서 차상위 도메인을 추가하는 기능을 통해 무료로 생성할 수 있다.</p>
<h1 id="🔨-route53">🔨 route53</h1>
<p>route53은 AWS가 제공하는 DNS이다. route53에 도메인을 하나 등록하면 이와 연결된 ip들을 설정해 줄 수 있다.</p>
<blockquote>
<p>route53 서비스는 도메인 하나당 $0.5이다. 한화로 650원 정도여서 1년이면 8천원정도 나온다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/c-on/post/0abf63fd-8222-4795-be4e-192b3305c2f2/image.png" alt=""></p>
<p>참고 : <a href="https://www.youtube.com/watch?v=0ehG45150Ns&amp;list=PLuHgQVnccGMCas8a4f0uIg5X4uERoG6gb&amp;index=2">생활코딩-aws2-route53 2.원리</a></p>
<p>top level domain마다 관리하는 등록소가 있다. 등록소에게 top level domain을 구매하기 위해 등록대행자를 거친다. 내가 이용한 등록대행자는 가비아인 셈이다.</p>
<p>도메인네임서버를 장만하면 도메인의 ip정보를 세팅해놓는다. 이때 도메인네임서버는 컴퓨터하나 사서 세팅해줄 수 있지만 AWS에서 route53서비스를 통해 임대를 할 수도 있다.</p>
<p>세팅이끝나면 내 탑레벨도메인을 관리하는 등록소에게 DNS가 누구인지 등록대행자(가비아)를 거쳐 전달해준다.</p>
<p>이제 클라이언트는 로컬 DNS서버에게 url을 전달하면 이 DNS는 루트 네임서버에게 탑레벨의 등록소 주소를 전달받고 다시 등록소에게 url의 DNS를 물어본다. 마지막으로 url의 DNS에게 ip를 전달받아 요청을 전달할 수 있게 된다.</p>
<p><img src="https://images.velog.io/images/c-on/post/5eabc277-dd80-4196-8f83-92fc4172af10/image.png" alt=""></p>
<p>route53 콘솔로 이동하고 왼쪽의 호스팅영역을 클릭한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/c9de727b-70f9-4372-83f5-0066769813f6/image.png" alt=""></p>
<p>호스팅영역 생성을 클릭</p>
<p><img src="https://images.velog.io/images/c-on/post/4fff10cb-dff5-49a4-a7ae-2aded95611be/image.png" alt=""></p>
<p>도메인이름에 구매한 도메인네임과 탑레벨 도메인까지 입력해주고 <code>호스팅 영역 생성</code>을 클릭</p>
<p><img src="https://images.velog.io/images/c-on/post/4d79b920-a542-4eb6-bd3a-053f6d860ecf/image.png" alt=""></p>
<p>생성된 호스팅의 도메인 이름을 클릭해서 접속한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/b81afff5-4cf1-4782-83bc-1fb0bc8ddb50/image.png" alt=""></p>
<p>오른쪽의 레코드생성을 클릭</p>
<p><img src="https://images.velog.io/images/c-on/post/952b7279-64d2-4fef-a021-c4b6905a5dea/image.png" alt=""></p>
<p>레코드이름은 자신이 이름을 딱 봤을 때 어떤 ip와 연결되는 레코드인지 구별할 수 있는 이름을 적어준다. 
레코드유형은 A로 하고 값에 인스턴스의 탄력적IP주소를 입력한 뒤 <code>레코드 생성</code>을 누른다.]</p>
<p>그리고 호스팅영역을 생성했을 때 NS유형과 SOA유형의 레코드가 자동으로 생성되어있는데 이는 도메인을 구입한 사이트에서 네임서버를 변경해주는데 사용한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/942504f4-9bc9-4ae9-9e50-7d09ee3b509b/image.png" alt=""></p>
<p>가비아는 <code>도메인관리</code> 페이지로 접속하면 왼쪽의 <code>도메인 정보변경</code> 메뉴에서 <code>네임서버</code>를 클릭하면 DNS를 변경해줄 수 있다. </p>
<p><img src="https://images.velog.io/images/c-on/post/4de22caa-d730-4be7-898c-bfce4063a5eb/image.png" alt=""></p>
<p>여기에 NS유형의 값 4개를 네임서버 설정에서 호스트명으로 입력해주고 적용한다.</p>
<p>이제 시간이 조금 지나면 도메인네임으로 인스턴스에 접속할 수 있다.</p>
<p>도메인만으로 접속하면 기본 80포트로 접속되고 뒤에 <code>:</code>와 함께 포트번호를 적어서 다른 포토에 접근할 수 있다.</p>
<p>이 문제는 도메인과 인스턴스에 SSL을 적용한 뒤에 해결하도록 하자</p>
<h1 id="🔨-ssl">🔨 SSL</h1>
<p>크롬에서는 SSL적용이 안된 사이트는 클라이언트에게 계속해서 알림을 준다. 위험할 거 같은 사이트는 접속하기 싫어하니 SSL을 적용을 해주자.</p>
<p>AWS의 Certification Manager를 이용하면 간편하게 인증서를 발급받아 route53에 등록되어 있는 도메인에 적용할 수 있다.</p>
<p><img src="https://images.velog.io/images/c-on/post/09fd43f6-89f3-44ac-87af-f9ecfe62128b/image.png" alt=""></p>
<p>Certification Manager 콘솔로 이동해서 우측 상단의 <code>요청</code>을 클릭하고 인증서요청이 뜨면 <code>다음</code>을 클릭</p>
<p><img src="https://images.velog.io/images/c-on/post/423b9778-f53a-4426-b1e1-d599a4c866e7/image.png" alt="">
SSL을 적용해 줄 도메인을 입력해준다.
와일드카드 SSL을 생성해주면 하위(3차이상) 도메인(shop.naver.com, news.naver.com 등등) 모두에 SSL이 적용된다. 와일드카드 SSL을 요청하려면 <code>*</code>를 사용한다.(*.naver.com)</p>
<p>단, 2차도메인(naver.com)은 적용이 안되므로 <code>이 인증서에 다른 이름 추가</code>를 눌러서 2차 도메인도 적어준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/a3a257d8-85fe-4bd0-9239-b8e3a4308dfc/image.png" alt=""></p>
<p>파란색 알림창의 <code>인증서보기</code>를 눌러준다. 알림창 안뜨면 그냥 생성된 인증서 ID를 눌러줘도 된다.</p>
<p><img src="https://images.velog.io/images/c-on/post/1030291c-d674-4799-8d9f-b2098080606e/image.png" alt=""></p>
<p><code>Route54에서 레코드 생성</code>을 눌러주면 자동으로 DNS레코드 생성 단계를 설정해준다. 그러면 <code>레코드생성</code>버튼을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e31868d9-cdfd-4b1c-b4df-8b090319812e/image.png" alt=""></p>
<p>이렇게 검증 대기중으로 뜬다. 검증까지는 시간이 좀 걸린다. 배고픈거 참고 여기까지 했으니 발급되는 동안 저녁을 먹고온다.</p>
<p><img src="https://images.velog.io/images/c-on/post/f3d53215-7594-4efb-95fd-062834bcaae0/image.png" alt=""></p>
<p>밥먹고 오니 발급 완료! 이제 연결을 해주자.</p>
<h1 id="🔨-alb-생성">🔨 ALB 생성</h1>
<p>ALB는 Application load balancer이다.
클라이언트가 수백명이면 서버가 응답하다 지쳐서 멈출 수도 있다.</p>
<p>이러한 문제는 다음 2가지 방법으로 해결해준다.</p>
<ul>
<li>Scale-up : 하드웨어의 성능을 업그레이드</li>
<li>Scale-out : 여러대의 서버가 나눠서 일을 분담</li>
</ul>
<p>신기하게도 서버를 추가하는 것이 하드웨어 향상보다 비용이 적다. 그리고 무중단 서비스가 가능해진다.</p>
<p>Scale-out시에 여러 서버에게 트래픽을 균등하게 분산시켜 주는 것이 바로 <strong>로드밸런서</strong>이다.</p>
<p><img src="https://images.velog.io/images/c-on/post/4849ec3c-120e-44e0-8b07-9a4049d6c4e2/image.png" alt=""></p>
<p>EC2 콘솔에서 왼쪽 아래에 로드밸런서에 접속한 뒤 <code>로드밸런서 생성</code>버튼을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/4c39c497-deb4-4305-8501-507b5706006c/image.png" alt=""></p>
<p>세가지 로드밸런서가 있는데 적혀있는 설명에 따르면 첫번째 <strong>애플리케이션 로드밸런서</strong>가 http및 https 트래픽을 사용한 웹 애플리케이션을 위한 유연한 기능을 위해 사용된다고 한다. 
따라서 애플리케이션 로드밸런서의 <code>create</code>를 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/9158ec98-5f2f-430e-a23f-f128b8c2285e/image.png" alt=""></p>
<p>보안그룹은 이전에 만든 basic을 적용해주고 Listener and routing에 http와 https를 설정해준다.
각 리스너의 default action을 target-group(대상그룹)으로 선택한다.</p>
<p>대상그룹이란 리스너가 전달한 요청을 처리하기 위한 부하분산 대상들의 모임이다.
대상 그룹에는 EC2의 인스턴스 ID, post 등이 적혀있고 요청이 잘 전달되는지에 관한 모니터링을 하는 헬스체크를 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/c-on/post/43e7165a-c7c4-4efa-a3f5-9d92adf19b8e/image.png" alt=""></p>
<p>로드밸런서를 선택하고 리스너에서 <strong>규칙 보기/편집</strong>을 클릭한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/90d52384-fc18-4fb6-b930-24e1c7c131cc/image.png" alt=""></p>
<p>연필모양을 클릭하면 규칙을 편집할 수 있다. 위의 연필을 클릭하고 마.지.막 옆에 나타난 연필을 또 클릭한다. </p>
<p><img src="https://images.velog.io/images/c-on/post/6ac76e82-d11f-417f-855a-808231ab891f/image.png" alt=""></p>
<p>then에 있는 규칙을 삭제하고 작업추가를 눌러서 <code>리디렉션대상</code>을 선택한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e052ebd8-65b9-40fb-ba80-e4614f71cf8f/image.png" alt=""></p>
<p>https, 443을 입력해서 설정해준 뒤 체크를 클릭하고 오른쪽 상단의 업데이트를 누른다.</p>
<p><img src="https://images.velog.io/images/c-on/post/215a3889-6228-42d9-81a0-e569a4f0f9d3/image.png" alt=""></p>
<p>route53으로 이동해서 레코드생성을 누른다. 별칭을 on으로 바꿔주면 생성한 로드밸런서를 선택할 수 있다. 선택후 생성을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/89daec96-a72d-40ec-9011-243a81376a02/image.png" alt=""></p>
<p>새로운 레코드를 생성했으므로 기존에 <code>domain.com</code>과 <code>www.domain.com</code>으로 생성했던 레코드는 삭제해주어야 로드밸런서로 거쳐갈 수 있다.</p>
<p>하지만 아직 포트없이 도메인만 호출하면 에러가 뜰것이다. nginx를 설치해서 해결해보자.</p>
<h1 id="🔨-nginx">🔨 Nginx</h1>
<p>proxy서버는 서버와 클라이언트 사이 중계기능을 하는 서버를 의미한다. 클라이언트는 서버에게 직접요청하지 않고 프록시 서버에게 요청을 날린다. 요청을 받은 프록시서버는 웹서버에게 실제 데이터를 요청하고 그 응답을 클라이언트에게 전달해준다.</p>
<p>프록시서버는 크게 두 가지로 나눠진다.</p>
<ul>
<li>포워드 프록시</li>
<li>리버스 프록시</li>
</ul>
<p><strong>포워드 프록시</strong>는 클라이언트는 캐싱기능을 사용한다. 클라이언트의 요청이 인터넷에 연결되기 전 캐시에 데이터가 남아있다면 캐시에서 컨텐츠를 제공한다. </p>
<p><strong>리버스 프록시</strong>는 실제 데이터를 작업하는 서버보다 앞에서 대리로 요청을 받아 인트라넷에 직접 접근하는 요청을 차단할 수 있다. 또한 SSL암호화 등을 수행할 수 있으며 API gateway역할을하여 외부의 요청에 대한 응답을 엔드포인트로 대리 전송해준다.</p>
<p>따라서 우리는 nginx를 리버스프록시 용도로 사용할 것이다. </p>
<blockquote>
<p><strong>아파치와의 차이점</strong>
아파치는 쓰레드/프로세스 기반으로 요청 하나당 쓰레드 하나가 처리하는 구조이다. 따라서 사용자가 많으면 많은 쓰레드를 생성해야 하기 때문에 메모리 및 cpu낭비가 심하다.
엔진엑스는 비동기 이벤트 드리븐 구조로 nodeJS와 유사하다. nodejs 창시자는 nginx를 nodejs의 앞단에 두어서 버퍼 오버플로우 취약점에 의한 공격을 방지하는 것을 추천한다. 
버퍼오버플로우란 버퍼가 메모리공간을 벗어나는 경우로 사용하지 않는 영역에 데이터가 덮어씌워져 주소,값을 바꾸는 공격이다.</p>
</blockquote>
<h3 id="nginx-설치">nginx 설치</h3>
<p>인스턴스에 접속하여 <code>sudo apt-get install nginx</code>를 입력하여 <code>nginx --v</code>를 입력해서 설치가 성공적으로 완료됐는지 확인한다.</p>
<p>그리고 domain.conf를 수정해야 하므로 다음을 차례대로 입력한다.</p>
<pre><code>cd /etc/nginx
sudo vi nginx.conf</code></pre><p>vi 편집기에서 파일을 수정해줄 것이다. 
vi의 기본적인 명령어는 어렵지 않으므로 구글에 검색한다.
http안에 다음코드를 삽입해준다.</p>
<pre><code>server {     
  listen 80;
  server_name  ~.;   
  location / {         
    proxy_set_header X-Real-IP $remote_addr;             
    proxy_set_header HOST $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # port setting , 서버의 port와 동일한 port로 pass 시켜야 합니다.
    proxy_pass http://127.0.0.1:4200;
    proxy_redirect off;
  }
}</code></pre><p><img src="https://images.velog.io/images/c-on/post/3e286570-4dcc-4d0a-b562-4a402b59ea0c/image.png" alt=""></p>
<p>이제 이렇게 http요청을 보내면 자동으로 https로 리다이렉팅하여 nginx가 nodejs에게 대신 요청을 전달하고 응답을 받아 우리에게 보여주게 된다.</p>
<h2 id="❗오류">❗오류</h2>
<h3 id="http-post요청-오류">http post요청 오류</h3>
<p><img src="https://images.velog.io/images/c-on/post/f3103de3-3bf2-47e6-aef0-c8f63c0182f8/image.png" alt=""></p>
<p>get요청은 정상적으로 nodejs서버에게 전달되지만 post요청은 전달이 되지 않았다.
http로 요청을 보내면 https로 redirecting이 되면서 post요청이 GET요청으로 바뀌어있었다.</p>
<p>리다이렉트시 동일한 메서드로 요청하려면 http상태코드를 307을 사용해야 한다. 하지만 ALB는 301과 302만을 지원한다. </p>
<p>따라서 리다이렉트를 로드밸런서에서 하지않고 nginx에서 수행하기로 했다.</p>
<p><img src="https://images.velog.io/images/c-on/post/96a2eda2-1479-4bff-96ce-d5c21b25be9b/image.png" alt=""></p>
<p>로드밸런서의 리스너 중 80포트에 관련된 것의 규칙을 다시 리다이렉트가 아닌 프로젝트의 인스턴스로 연결해주었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/a65986a2-2e68-4558-ab88-3d42462b8f57/image.png" alt=""></p>
<p><code>nginx.conf</code>는 리다이렉팅 코드를 추가하여 상태코드는 307로 설정해주었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/22f240fd-3bc3-4153-8848-c1093d7a6f48/image.png" alt=""></p>
<p>이제 리다이렉팅이 되어도 메서드는 유지되어 정상적으로 응답을 받을 수 있게 되었다.</p>
<h3 id="cookie-설정-오류">cookie 설정 오류</h3>
<p>프론트엔드와 백엔드의 도메인이 다르기 때문에 쿠키 옵션을 설정할 때 sameSite=&quot;none&quot;으로 설정해줬고 따라서 secure=true로 강제로 해줘야 했다. </p>
<p>문제는 프론트엔드에 쿠키가 저장이 안되는 것이었다. 
구글링을 해보니 express-session 옵션에서 <code>proxy=true</code>를 추가해줘야함을 발견했다.</p>
<pre><code class="language-js">secret: config.session.secreatKey,
  resave: false,
  saveUninitialized: false,
  store: sessionStore,
  proxy: true,
  cookie: {
    httpOnly: true,
    maxAge: 3600000,
    sameSite: &quot;none&quot;,
    secure: true,
  },</code></pre>
<p>이렇게 설정해주니 정상적으로 동작했다.</p>
<p>개발단계에서는 secure을 사용하기 힘드니 다음과같이 변경했다.</p>
<pre><code class="language-js">secret: config.session.secreatKey,
  resave: false,
  saveUninitialized: false,
  store: sessionStore,
  proxy: true,
  cookie: {
    httpOnly: true,
    maxAge: 3600000,
    sameSite: config.node === &quot;dev&quot; ? false : &quot;none&quot;,
    secure: config.node === &quot;dev&quot; ? false : true,
  },
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[typescript로 만든 웹앱 netlify로 배포하면서 만난 문제와 해결방법]]></title>
            <link>https://velog.io/@c-on/Typescript%EB%A1%9C-%EB%A7%8C%EB%93%A0-%EC%9B%B9%EC%95%B1-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c-on/Typescript%EB%A1%9C-%EB%A7%8C%EB%93%A0-%EC%9B%B9%EC%95%B1-netlify-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 21 Jan 2022 16:53:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/0f94bbff-a1f0-4643-b807-0981e7383e64/image.png" alt=""></p>
<p>이번에 typescrip와 OOP를 활용해서 프론트엔드를 설계하는 토이프로젝트를 진행해 봤다.</p>
<p>react로 웹앱을 제작하면 간편하지만 강력한 기능들을 활용할 수 있고 무엇보다 netlify 배포에 대한 정보를 찾기 쉬워서 좋아 보였다.</p>
<p>하지만 난 백엔드를 공부할 시간도 모자란, 실력이 부족한 취준생이기 때문에 아직 spa 프레임워크까지 공부할 수 있는 시기는 아닌것 같다. 뭐 하나라도 제대로 못하면서 다른 것 까지 욕심내면서 얼렁뚱땅 공부하고 싶지는 않다.</p>
<p>그래도 만약 배운다면 리액트보다 vue를 먼저 배우게 되지 않을까 싶다,,</p>
<p>어쨌든 토이프로젝트를 하려면 대충이라도 만든 프론트엔드가 필요하다(<em>하지만 성격상 대충 만들지를 못해서 프론트엔드 제작에 시간을 다 빼앗기고 있다. 제발 그냥 대충 만들었으면 좋겠다😂)</em>
마침 최근에 수강한 객체지향 강의에서 typescript를 사용한 프론트엔드 설계를 프로젝트로 진행하면서 OOP를 공부했다. 이 기회에 직접 만들어보면서 마구 깨질(?)수 있을 것 같아 typescript와 OOP로 프론트엔드의 웹앱을 제작해봤다.</p>
<p>얼추 완성하고 netlify에 올리려고 배포방법을 찾아봤다. netlify에서 CI를 구축하지 않고도 배포를 자동화할 수 있다길래 대박이다!하면서 잔뜩 기대를 품고 배포를 시작했다.</p>
<h1 id="netlify-시작">netlify 시작</h1>
<p><a href="https://www.netlify.com/">netlify</a>에 접속해서 가입하면 로그인 된 페이지가 뜨는데 build로 이동해준다.
<img src="https://images.velog.io/images/c-on/post/b2c8ba58-a2c2-49ca-b257-6bb1bfaa6040/20220121180827.png" alt=""></p>
<p>여기서 new site from Git을 클릭한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/b2f2bbf9-6903-4f53-ba22-aaf099c6799f/20220121180835.png" alt=""></p>
<p>github에 push할 때 자동으로 배포되는 기능을 이용할 것이기 때문에 github을 커넥트해줬다.</p>
<p><img src="https://images.velog.io/images/c-on/post/de7dc0d0-8100-433f-ba49-27255abd9e8d/20220121180843.png" alt=""></p>
<p><code>Authorize Netlify</code> 클릭해주고</p>
<p><img src="https://images.velog.io/images/c-on/post/b10ba786-f0c2-44a4-b87d-c240546b508a/20220121180947.png" alt=""></p>
<p>배포할 저장소를 선택해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/c8b78f55-aa11-43ff-b131-f55f67827cc9/20220121181033.png" alt=""></p>
<p>Branch to deploy는 배포자동화가 적용되는 브랜치를 설정하는 곳인데 나는 frontend 브랜치와 backend브랜치를 나눠서 작업했었기 때문에 frontend브랜치가 git push 할 때 자동 배포가 되도록 frontend를 적어넣었다.</p>
<p>그리고 npm run build를 적어야 한다길래 뭔지도 모르고 일단 적었는데 &quot;build.command&quot; failed 오류가 떴다.</p>
<h1 id="첫번째-오류-buildcommand-failed">첫번째 오류 <code>&quot;build.command&quot; failed</code></h1>
<p><img src="https://images.velog.io/images/c-on/post/d1944185-7b78-41e6-a4cd-7807e8b94672/20220121193205.png" alt=""></p>
<p><strong>한 번에 될리가 없.지.</strong> </p>
<p>리액트에는 build라는 명령이 있나보다. </p>
<p>그래서 지워봤더니 배포가 되었다고 떴다! 이제됐나?(<del>여기서 이미 실패</del>)라며 두근두근거리면서 배포된 주소로 접속했는데 js파일은 하나도 전송이 안되어있었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/fe9762fa-6e3a-4bbc-8333-81f42f3a23b5/image.png" alt=""></p>
<h1 id="두번째-오류-packagejson">두번째 오류 package.json</h1>
<p>근데 신기한건 style.css는 전부 적용이 되어있었다. 깃허브 저장소에서 뭐가 문젠지 하나하나 뒤져봤지만 이상한 점을 찾지 못했다. 세,네번 들락날락 해보고 눈치를 챘다. 그제서야 눈치챈게 한심할 정도의 문제였다.</p>
<p>깃허브에는 dist 폴더를 ignore해놔서 js파일은 push되지 않고 ts만 올라간다. 
netlify는 깃허브를 클론해서 배포하는데 tsc도 안된상태에서 js파일을 찾으니 없었던 것이다.</p>
<p>이를 해결할 방법은 바로 떠올랐다.</p>
<p>아까 지웠던 npm run build를 이용하는 것이다. 먼저 package.json에 typescript를 추가해주기 위해서 cmd를 켜고 파일 저장공간으로 이동해서 <code>npm i typescript</code>를 입력한다.
이미 전역적으로 typescript를 설치해뒀지만 package.json에 버전 따로 찾아서 등록하기 번거로워서 그냥 설치했다. </p>
<p>그리고 package.json의 script에 <code>&quot;build&quot;: &quot;tsc&quot;,</code>를 추가했다.
<img src="https://images.velog.io/images/c-on/post/cc4c680f-8278-4838-a805-69898001032d/image.png" alt=""></p>
<p>netlify사이트에서 아까 배포한 프로젝트로 이동하면 Site settings가 있다. 클릭한다.
<img src="https://images.velog.io/images/c-on/post/89920f0d-a016-4669-950a-bf550c33eebe/image.png" alt=""></p>
<p>왼쪽에 Build&amp;deploy로 들어간다.
<img src="https://images.velog.io/images/c-on/post/5bf55447-2f99-4e63-8897-f81a9985db31/image.png" alt=""></p>
<p>그럼 다음과 같이 Build settings에 다음과 같이 Build command를 설정할 수 있는데 여기에 <code>npm run build</code>를 입력해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/48ae5b46-c976-41ff-9762-050e47b7c659/image.png" alt=""></p>
<p>이제 코드의 변경사항을 깃허브에 push하면 netlify가 자동으로 다시 배포를 진행할 것이고 서버에 올리기 전, npm run build 명령어를 수행해서 ts파일을 js로 변환하고 성공적으로 js파일까지 서버가 전달할 수 있게 된다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NodeJS 기초 개념과 내장 모듈에 관하여]]></title>
            <link>https://velog.io/@c-on/NodeJS-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90%EA%B3%BC-%EB%82%B4%EC%9E%A5-%EB%AA%A8%EB%93%88%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@c-on/NodeJS-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90%EA%B3%BC-%EB%82%B4%EC%9E%A5-%EB%AA%A8%EB%93%88%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Thu, 20 Jan 2022 14:17:39 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/ef969425-42a3-4429-b13c-4281d911ff16/image.png" alt=""></p>
<br>

<h1 id="🔎-nodejs-이해하기">🔎 NodeJS 이해하기</h1>
<p>노드JS는 이벤트기반, 논블로킹 I/O를 사용한다. npm은 노드의 가장 큰 오픈 소스 라이브러리 생태계이다. npm에서 다운 받아 사용하는 모듈들을 서드파티라고 한다.</p>
<p>노드 JS는 정확하게 자바스크립트의 런타임을 의미한다. 런타임이란 어떤 언어가 돌아가는 환경을 말하는데 원래 자바스크립트의 런타임은 웹브라우저가 유일했지만 현재는 nodeJS를 통해 브라우저 외의 환경에서도 사용할 수 있게 되었다.</p>
<hr>
<h3 id="이벤트기반">이벤트기반</h3>
<p>이벤트기반이라는 것은 이벤트가 발생할 때 미리 저장해둔 작업을 수행하는 방식이다. 이벤트 기반의 핵심은 이벤트루프이다. </p>
<p>이는 <a href="https://velog.io/@c-on/Mozilla-JavaScript-%EB%AC%B8%EC%84%9C-%EA%B3%B5%EB%B6%80#%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">다른기록공간</a>에 더 자세하게 정리를 해두었다.</p>
<p>간단하게는 이벤트 발생 시 백그라운드가 이벤트 리스너에 등록한(된) 콜백함수를 테스크큐에 넣는다. 그리고 이벤트루프가 테스크큐의 함수를 콜스택으로 옮기고 콜백함수의 동작이 이루어진다고 알면된다.</p>
<p>이벤트루프는 콜스택이 비어있을 때만 테스크큐의 함수를 옮겨주기 때문에 setTimeout으로 3초를 지정해놨더라도 콜스택에 너무 많은 함수들이 쌓여있으면 3초가 지나도 setTimeout에 전달한 콜백함수를 실행해주지 않을 수도 있다.</p>
<hr>
<h3 id="논블로킹io">논블로킹I/O</h3>
<p>논블로킹IO는 이벤트루프를 적극 활용한 방법이다. 헤비하고 오래걸리는 함수를 백그라운드로 보내놓고 자바스크립트는 다른 코드를 먼저 처리한다. 그 동안 백그라운드는 헤비한 함수를 처리하고 테스크큐에 콜백을 올려놓는다. 이벤트루프는 자바스크립트의 콜스택이 비워질 때를 상시 체크하여 테스크큐에 올라온 헤비한 함수의 콜백을 콜스택으로 넣어줄 수 있다. 즉, 이전 작업의 완료를 기다리지 않고 다음 작업을 수행하는 것이 논블로킹 방식이다.</p>
<p>그렇다면 IO는 뭘까? I/O는 input과 output을 의미한다. 자바스크립트는 싱글스레드이기 때문에 모든 코드에서 시간적이득을 볼 수 있는 것은 아니고 주로 IO작업에서 시간 이득을 얻게된다. IO작업은 파일 시스템이나 네트워크 요청같은 작업으로 이러한 작업들은 OS에게 맡기고 다음 코드를 실행하는 방식으로 동작할 수 있기 때문이다. 이렇게 되면 OS는 작업을 요청받아도 노드를 막지 않아서 노드는 다음 동작을 수행한다. 만약 OS가 노드를 막았다면 그것은 블로킹 방식이고 노드는 OS가 작업을 완료할 때 까지 기다려줘야 한다.</p>
<p>Nodejs의 표준라이브러리의 모든 I/O메서드는 논블로킹인 비동기 방식을 제공한다. 그래서 fs모듈을 사용하면 비동기함수를 이용하게 되는 것이며 같은 작업을 블로킹 메서드로 제공하는 것들은 메서드 이름의 뒤에 Sync가 붙여서 사용할 수 있게한다.</p>
<blockquote>
<p>노드는 싱글스레드, 논블로킹 모델을 사용하기 때문에 컴퓨터 자원을 적게 사용한다는 장점이 있지만 CPU코어를 여전히 하나밖에 사용하지 못하는 단점도 있다.</p>
</blockquote>
<hr>
<h3 id="학습에서의-장점">학습에서의 장점</h3>
<p>노드에는 웹서버가 내장되어 있다. 다른 언어들은 아파치나 엔진엑스를 설치해야 하며 추가로 톰캣을 설치하기도 한다. 때문에 웹서버와 was사용법을 또 학습해야한다. 이런면에서 노드는 처음 서버를 공부할 때 유리하다. 하지만 결국 더 성장한 개발자가 되고 더 큰 서비스를 만드려면 결국에는 웹서버를 노드와 연결하게 되지만 처음부터 거기에 얽매이지 않을 수 있는 것이 장점이다.</p>
<hr>
<br>

<br>


<h1 id="🔎-내장-객체">🔎 내장 객체</h1>
<h3 id="global">global</h3>
<p>global 객체에 프로퍼티를 추가하면 전혀 다른 파일에서 전역적으로 사용할 수 있다.</p>
<pre><code class="language-jsx">global.greeting = &quot;how are you?&quot;</code></pre>
<pre><code class="language-jsx">console.log(greeting) //how are you?</code></pre>
<hr>
<h3 id="타이머">타이머</h3>
<p>global 객체 안에 타이머 기능을 제공하는 함수인 <code>setTimeout</code>, <code>setInterval</code>, <code>setImmediate</code>가 들어있다.</p>
<ul>
<li><code>setTimeout(콜백 함수, 밀리초)</code> : 주어진 밀리초(1000분의 1초) 이후에 콜백 함수를 실행합니다.</li>
<li><code>setInterval(콜백 함수, 밀리초)</code> : 주어진 밀리초마다 콜백 함수를 반복 실행합니다.</li>
<li><code>setImmediate(콜백 함수)</code> : 콜백 함수를 즉시 실행합니다.</li>
</ul>
<pre><code class="language-jsx">const timeout = setTimeout(() =&gt; {
console.log(&#39;1.5초 후 실행&#39;);
}, 1500);

const interval = setInterval(() =&gt; {
console.log(&#39;1초마다 실행&#39;);
}, 1000);

const timeout2 = setTimeout(() =&gt; {
console.log(&#39;실행되지 않습니다&#39;);
}, 3000);

setTimeout(() =&gt; {
clearTimeout(timeout2);
clearInterval(interval);
}, 2500);

const immediate = setImmediate(() =&gt; {
console.log(&#39;즉시 실행&#39;);
});

const immediate2 = setImmediate(() =&gt; {
console.log(&#39;실행되지 않습니다&#39;);
});

clearImmediate(immediate2);</code></pre>
<p><em>출처 : nodejs교과서 by 조현영</em></p>
<p>위 예제를 실행했을 때 콘솔에 어떻게 출력될지 예상해보자</p>
<pre><code class="language-jsx">즉시 실행
1초마다 실행
1.5초 후 실행
1초마다 실행</code></pre>
<p>함수표현식 선언은 함수를 즉시 실행한다.</p>
<p>백그라운드에 전달되어 정해진 시간이 지나면 테스큐에 콜백함수를 전달한다.
하지만 그 시간이 지나기전에 삭제함수가 들어있는 콜백함수가 먼저 테스크큐에 전달되고 콜스택에 먼저올라가게 되면 타이머콜백함수는 파괴된다.</p>
<p><code>immediate</code>의 경우 콜백함수가 백그라운드에 전달됨과 동시에 <code>clearImmediate</code>가 콜스택에 들어온다. <code>immediate</code>의 콜백함수가 테스크큐에 전달되었든 되지 않았든 콜스택의 <code>clearImmediate</code>가 사라지긴 전엔 콜스택으로 넘어올 수 없으며 <code>clearImmediate</code>가 동작해서 콜스택에서 사라질 때는 <code>immediate</code>의 콜백함수도 파괴되므로 콘솔에 출력되는 것은 없다.</p>
<hr>
<h3 id="process">process</h3>
<ul>
<li><code>process.env</code> : 서비스의 중요한 키를 저장하는 공간으로 사용</li>
<li><code>process.nextTick(콜백)</code> : 이벤트 루프가 다른 콜백 함수들보다 nextTick의 콜백 함수를 우선으로 처리<ul>
<li>nextTick의 콜백은 <strong>마이크로태스크큐</strong>에 들어가서 태스크큐보다 우선순위를 가진다.</li>
<li>resolve된 Promise도 태스크큐가 아닌 마이크로태스크큐로 전달된다.</li>
</ul>
</li>
<li><code>process.exit(코드)</code> : 실행중인 노드 프로세스를 강제 종료</li>
</ul>
<hr>
<br>

<br>

<h1 id="🔎-내장-모듈">🔎 내장 모듈</h1>
<h3 id="os">OS</h3>
<p>노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.</p>
<ul>
<li><code>os.type()</code>: 운영체제의 종류</li>
<li><code>os.uptime()</code>: 운영체제 부팅 이후 흐른 시간(초)</li>
<li><code>os.hostname()</code>: 컴퓨터의 이름</li>
<li><code>os.release()</code>: 운영체제의 버전</li>
<li><code>os.homedir()</code>: 홈 디렉터리 경로</li>
<li><code>os.tmpdir()</code>: 임시 파일 저장 경로</li>
<li><code>os.cpus()</code>: 컴퓨터의 코어 정보</li>
<li><code>os.freemem()</code>: 사용 가능한 메모리(RAM)</li>
<li><code>os.totalmem()</code>: 전체 메모리 용량</li>
</ul>
<hr>
<h3 id="path">path</h3>
<ul>
<li><code>path.dirname(경로)</code>: 파일이 위치한 폴더 경로를 보여줌</li>
<li><code>path.extname(경로)</code>: 파일의 확장자를 보여줌</li>
<li><code>path.basename(경로, 확장자)</code>: 파일의 이름(확장자 포함)을 보여줌</li>
<li><code>path.parse(경로)</code>: 파일 경로를 root, dir, base, ext, name으로 분리</li>
<li><code>path.format(객체)</code>: path.parse()한 객체를 파일 경로로 합침</li>
<li><code>path.normalize(경로)</code>: /나 \를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환</li>
<li><code>path.isAbsolute(경로)</code>: 파일의 경로가 절대경로인지 상대경로인지 true나 false로 알려줌</li>
<li><code>path.relative(기준경로, 비교경로)</code>: 경로를 두 개 넣으면 첫 번째 경로에서 두 번째 경로로 가는 방법을 알려줌</li>
<li><code>path.join(경로, ...)</code>: 여러 인자를 넣으면 하나의 경로로 합침</li>
</ul>
<hr>
<h3 id="url">url</h3>
<p>인터넷 주소를 쉽게 조작하도록 도와주는 모듈이다. </p>
<pre><code class="language-jsx">const url = require(&quot;url&quot;)
const URL = url.URL
const naver = new URL(&#39;https://search.shopping.naver.com/search/category?catId=50000174&#39;)

console.log(&quot;naver url module:&quot;, naver)
console.log(&quot;------------------------------&quot;)
console.log(&quot;naver url: &quot;, url.format(naver))</code></pre>
<pre><code class="language-jsx">naver url module: URL {
  href: &#39;https://search.shopping.naver.com/search/category?catId=50000174&#39;,
  origin: &#39;https://search.shopping.naver.com&#39;,
  protocol: &#39;https:&#39;,
  username: &#39;&#39;,
  password: &#39;&#39;,
  host: &#39;search.shopping.naver.com&#39;,
  hostname: &#39;search.shopping.naver.com&#39;,
  port: &#39;&#39;,
  pathname: &#39;/search/category&#39;,
  search: &#39;?catId=50000174&#39;,
  searchParams: URLSearchParams { &#39;catId&#39; =&gt; &#39;50000174&#39; },
  hash: &#39;&#39;
}
------------------------------
naver url:  https://search.shopping.naver.com/search/category?catId=50000174</code></pre>
<p><em>출처 : nodejs교과서 by 조현영</em></p>
<hr>
<h3 id="버퍼와-스트림">버퍼와 스트림</h3>
<p>파일을 전송하기 위해서 조금씩 나눠서 보내는 것을 <strong>스트리밍</strong>이라 한다.
스트리밍하는 데이터를 재생할 수 있을 만큼 모으는 것을 <strong>버퍼링</strong>이라고 한다.</p>
<p>스트리밍하는 데이터 즉, 쪼개진 데이터는 청크라고 부른다.</p>
<p>용량이 큰 파일을 읽을 때 한번에 읽으려 한다면 파일이 전부 가져와질 동안 빈 화면을 쳐다봐야한다. 이때 버퍼와 스트림을 사용해서 가져온 데이터라도 먼저 읽을 수 있도록 하는 것이 버퍼와 스트림이다.</p>
<pre><code class="language-jsx">const fs = require(&quot;fs&quot;)

result = []

fs.createReadStream(&#39;./test.txt&#39;, {
  highWaterMark: 8,
  encoding: &#39;utf-8&#39;
}).on(&#39;data&#39;, (chunk)=&gt;{
  result.push(chunk)
  console.log(chunk)
}).on(&#39;end&#39;, ()=&gt;{
  console.log(&quot;------스트리밍결과-------&quot;)
  console.log(result.join(&#39;&#39;))
})</code></pre>
<pre><code class="language-jsx">hello, n
ice to m
eet you.

how are
 you?
I&#39;
am fine,
 thank y
ou.
and 
you?
------스트리밍결과-------
hello, nice to meet you.
how are you?
I&#39;am fine, thank you.
and you?</code></pre>
<p><em>출처 : nodejs교과서 by 조현영</em></p>
<p>이런식으로 8바이트씩 데이터를 읽어오도록 스트리밍을 만들 수 있다.</p>
<hr>
<h3 id="이벤트">이벤트</h3>
<p>어떤 상황이 발생했을 때 그것에 반응하는 함수를 만들어줄 수 있다.
이벤트는 만들 수 있고 호출할 수 있고 삭제할 수 있다.</p>
<pre><code class="language-jsx">const EventEmitter = require(&#39;events&#39;)
const emitter = new EventEmitter()

emitter.on(&#39;Do greeting!&#39;, (name)=&gt;{
  console.log(`how are you, ${name}?`)
})
emitter.on(&#39;Do greeting!&#39;, ()=&gt;{
  console.log(&quot;I&#39;m fine, and you?&quot;)
})

emitter.emit(&#39;Do greeting!&#39;, &quot;james&quot;)</code></pre>
<pre><code class="language-jsx">how are you, james?
I&#39;m fine, and you?</code></pre>
<p><em>출처 : nodejs교과서 by 조현영</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 이론 정리]]></title>
            <link>https://velog.io/@c-on/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B4%EB%A1%A0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@c-on/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B4%EB%A1%A0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 20 Jan 2022 02:31:27 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스">데이터베이스</h1>
<h3 id="파일에-데이터를-보관하는-문제점">파일에 데이터를 보관하는 문제점</h3>
<ul>
<li>같은 내용의 데이터가 중복 저장<ul>
<li>저장공간의 낭비를 일으켰고 일관성과 무결성을 유지하기 어려움</li>
</ul>
</li>
<li>응용프로그램이 데이터파일에 종속적<ul>
<li>원래는 없던 컬럼을 추가하기 위해 파일시스템을 수정하면 응용프로그램도 수정해야함</li>
</ul>
</li>
<li>데이터파일의 공유, 보안, 회복 기능이 약함</li>
</ul>
<p>DBMS는 위 문제를 해결하기 위해 등장한 <strong>데이터를 통합하여 저장하고 관리하는 시스템</strong>이다.</p>
<p>별도의 파일로 관리되던 데이터를 하나로 통합하여 관리하면서 응용프로그램을 대신해서 데이터를 검색, 삽입, 수정할 수 있는 기능을 하게되었다. </p>
<p>응용프로그램들은 DB를 공유해서 사용할 수 있게 되었다.</p>
<hr>
<br>

<br>


<h1 id="데이터베이스-시스템">데이터베이스 시스템</h1>
<h3 id="데이터-시스템-요소">데이터 시스템 요소</h3>
<p><strong>스키마</strong> : 데이터베이스에 저장되는 데이터 구조와 제약조건을 정의한 것
<strong>인스턴스</strong> : 스키마에 따라 데이터베이스에 실제로 저장된 값
<strong>메타데이터</strong> : 데이터에 대한 데이터</p>
<hr>
<h3 id="데이터-언어">데이터 언어</h3>
<p><strong>데이터 정의어 DDL</strong> 
스키마를 정의하거나 수정, 삭제하기 위해 사용 (CREATE, ALTER, DROP)</p>
<p><strong>데이터 조작어 DML</strong> 
데이터 CRUD를 요구하기 위해 사용(SELECT, INSERT, UPDATE, DELETE)</p>
<ul>
<li>절차적 데이터 조작어 : 사용자가 어떤 데이터를 원하고 그 데어터를 얻기위해 무엇을 처리해야 하는지 설명</li>
<li>비절차적 데이터 조작어 : 사용자가 어떤 데이터를 원하는 지만 설명하면 데이터를 얻을 수 있음</li>
</ul>
<p><strong>데이터제어어 DCL</strong>
DBA가 관리를 목적으로 사용</p>
<hr>
<br>

<br>


<h1 id="데이터-모델링">데이터 모델링</h1>
<p>개체와 개체간의 관계를 이용해서 현실 세계를 개념적 구조로 표현</p>
<p>이것을 다이어그램 그림으로 그린것이 ERD이다.</p>
<p><strong>개체 = entity</strong>
저장할 가치가 있어야 하며 다른 개체와 구별되는 이름과 고유한 특성을 가지고 있어야 한다.
하나의 테이블과 대응됨</p>
<p><strong>속성 = attribute</strong>
개체나 관계가 가지고 있는 고유의 특성
의미있는 데이터의 가장 작은 논리적 단위
테이블의 한 컬럼과 대응됨</p>
<p><strong>인스턴스 = instance</strong>
개체를 구성하고 있는 속성이 실제 값을 가짐으로써 실체화된 개체
테이블에서 하나 이상의 행과 대응</p>
<hr>
<br>

<br>


<h1 id="관계-모델링">관계 모델링</h1>
<p>관계데이터 모델은 개념적 구조를 논리적 구조로 표현하는 논리적 데이터 모델이다.</p>
<hr>
<h3 id="릴레이션">릴레이션</h3>
<p>개체에 관한 데이터를 2차원 테이블의 구조로 저장한것, 즉 테이블을 말하는 것이다.</p>
<ul>
<li><strong>속성(atrribute):</strong> relation의 열</li>
<li><strong>튜플:</strong> relation의 행</li>
<li><strong>도메인:</strong> 하나의 속성이 가질 수 있는 모든 값의 집합(Integer, Text, ....)</li>
<li><strong>카디널리티:</strong> 하나의 릴레이션에서 튜플의 전체 개수</li>
<li><strong>릴레이션의 스키마</strong> : 릴레이션의 논리적구조, ex.고객(id, name, age, job)</li>
<li><strong>릴레이션의 인스턴스</strong> : 한 시점의 릴레이션에 존재하는 튜플의 집합</li>
<li><strong>릴레이션특성</strong><ul>
<li>튜플의 유일성 : 동일한 튜플은 존재할 수 없다</li>
<li>무순서 : 튜플 사이 순서나 속성 사이 순서는 아무 의미가 없다</li>
<li>속성의 원자성 : 속성은 더이상 쪼갤 수 없는 원자 값을 넣어줘야 한다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="키">키</h3>
<p>릴레이션에서 튜플들을 구별하는 하나 또는 그 이상의 속성</p>
<p><strong>키 특성</strong></p>
<ul>
<li>유일성 : 하나의 릴레이션에서 모든 튜플은 서로 다른 키를 가져야 한다</li>
<li>최소성 : 꼭 필요한 최소한의 속성들로만 키를 구성해야한다.</li>
</ul>
<p><strong>키 종류</strong></p>
<ul>
<li>슈퍼키 : 유일성을 만족하는 속성 또는 속성집합</li>
<li>후보키 : 유일성과 최소성을 만족하는 속성 또는 속성집합</li>
<li>기본키 : 후보키 중 기본적으로 사용하기 위해 선택한 키</li>
<li>대체키 : 후보키 중 기본키로 선택되지 못한 키</li>
<li>외래키 : 다른 릴레이션의 기본키를 참조하는 속성 또는 속성집합</li>
</ul>
<hr>
<br>

<br>

<h1 id="이상현상과-정규화">이상현상과 정규화</h1>
<p>이상현상이란 불필요한 데이터 중복으로 인해 연산 수행시 발생하는 부작용을 의미한다.</p>
<ul>
<li>삽입이상 : 새 데이터를 삽입할 때 불필요한 데이터도 함께 삽입해야 하는 문제</li>
<li>갱신이상 : 함께 바뀌어야 하는 튜플들 중 한 튜플의 속성이 바뀌었는데 나머지 튜플의 속성은 바뀌지않아 데이터가 불일치하는 경우</li>
<li>삭제이상 : 삭제하는 경우 꼭 필요한 데이터가 함께 삭제되어 데이터 손실이 발생하는 경우</li>
</ul>
<hr>
<h3 id="정규화">정규화</h3>
<p>이상현상이 발생하지 않는 바람직한 릴레이션으로 만들어가는 과정</p>
<p>정규화를 통해 릴레이션은 무손실 분해 되어야 하며 조인했을 때 분해전의 릴레이션으로 복원이 가능해야 한다.</p>
<hr>
<h3 id="정규형">정규형</h3>
<ol>
<li>제1정규형<ul>
<li>릴레이션의 모든 속성값이 원자값으로 가진 상태</li>
</ul>
</li>
<li>제2정규형<ul>
<li>1정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 완전함수 종속된 상태</li>
<li>이행적 함수 종속이 존재하면서 이상현상이 발생할 수 있다.</li>
<li>ex) <strong>id → (이름, 학부, 등록금)</strong> 이고 <strong>학부→등록금 관계</strong>가 일 때 등록금은 학부와 id에 두번 종속되는 현상이 발생한다.</li>
</ul>
</li>
<li>제3정규형<ul>
<li>2정규형에서 이행적 함수종속을 제거</li>
<li>기본키가 아닌 모든 속성이 기본키에 이행적함수종속이 되지않는 상태</li>
<li>ex) id → (이름, 학부, 등록금) 일 때, 다시 학부→등록금 관계가 되므로 이를 쪼개서 id→ 이름, 학부, 학부 → 등록금으로 테이블을 만들어줘야 한다.</li>
</ul>
</li>
</ol>
<hr>
<br>

<br>

<h1 id="인덱스란">인덱스란</h1>
<p>데이터베이스의 검색 기능 성능을 높여주는 기술이다. 전체를 조회할 때는 O(N)의 시간이 들지만 인덱스를 사용하면 O(logN)으로 시간복잡도가 감소한다.</p>
<p>다음과 같은 경우에 사용하면 좋다.</p>
<ul>
<li>SELECT와 JOIN이 잦은 경우</li>
<li>테이블 데이터가 많은 경우</li>
</ul>
<hr>
<h3 id="장점">장점</h3>
<p>원하는 데이터를 찾기 위해 테이블을 검색하는 작업을 줄이거나 없앨 수 있으며 검색 속도를 향상시킨다.</p>
<hr>
<h3 id="단점">단점</h3>
<p>테이블 새로운 데이터를 추가하거나 갱신, 삭제가 자주 일어나면 인덱스에도 추가, 갱신이 일어나기 때문에 오버헤드가 발생할 수 있다.</p>
<hr>
<br>

<br>

<h1 id="트랜잭션">트랜잭션</h1>
<p>데이터베이스의 상태를 변화시키는 하나의 작업 단위를 일컫는다. 따라서 그 안에는 여러개의 연산이 수행될 수 있으며 한 연산이라도 실패하면 그 트랜잭션은 전체가 실패한 것이 된다.</p>
<p>트랜잭션의 중요성은 금융소프트웨어를 예로들면 이해하기 쉽다.</p>
<p>100만원을 송금하는 작업은 내 계좌에서 100만원을 출금해서 상대방 계좌에 100만원을 입금하는 작업이다. </p>
<p>이때 출금까지는 정상작동했는데 입금에서 오류가 발생해버리면 출금한 돈은 증발하게 된다. </p>
<p>따라서 두 작업을 트랜잭션으로 묶어서 둘 중 하나라도 실패하면 모두 실패한 작업으로 처리해줘야 돈이 증발하는 일이 발생하지 않을 것이다.</p>
<hr>
<h3 id="특성">특성</h3>
<p>트랜잭션을 하기 위해선 데이터베이스의 무결성과 일관성을 보장하기 위해 ACID라는 4가지 특성을 만족해야 한다.</p>
<p><strong>Atomic</strong> : 원자성으로 트랜잭션을 구성하는 연산들은 한꺼번에 반영되거나 한꺼번에 반영되지 않아야 함, 즉 수행과정 중 오류가 발생하면 트랜잭션 시작 전 상태로 되돌려야 한다.</p>
<p><strong>Consistency</strong> : 일관성으로 트랜잭션 성공 후 데이터베이스가 일관성있는 상태를 유지하는 것. 즉 데이터베이스의 제약 조건을 위배하는 작업을 트랜잭션이 수행할 수 없게 하는 것이다.</p>
<p><strong>Isolation</strong> : 격리성으로 여러개의 트랜잭션이 동시에 실행중일때 서로의 연산에 끼어들수 없게 해야하는 것이다. 예를 들어 B의 계좌에 100만원을 넣고 있는 상태에 또 다른 누군가가 B의 계좌에 입금을 하는 작업을 할 수 없다는 것이다.</p>
<p><strong>Durability</strong> : 지속성으로 트랜잭션 성공 후 데이터베이스에 반영한 결과는 손실되지 않게 유지해야 하는 것을 말한다.</p>
<hr>
<h3 id="격리수준">격리수준</h3>
<p><strong>Isolation</strong>을 구현하는 개념으로 트랜잭션끼리 격리되어야 하는 정도를 나타낸다.</p>
<p>다음과같이 4단계로 나뉜다. 커밋은 트랜잭션이 성공적으로 완료된 상태를 의미한다.</p>
<ol>
<li><strong>READ UNCOMMITTED</strong>: 다른 트랜잭션에서 커밋되지 않은 내용도 참조할 수 있다</li>
<li><strong>READ COMMITTED</strong>: 다른 트랜잭션에서 커밋된 내용만 참조할 수 있다.</li>
<li><strong>REPEATABLE READ</strong>: 트랜잭션에 진입하기 이전에 커밋된 내용만 참조할 수 있다.</li>
<li><strong>SERIALIZABLE</strong>: 트랜잭션에 진입하면 락을 걸어 다른 트랜잭션이 접근하지 못하게 한다.(성능 매우 떨어짐)</li>
</ol>
<p>아래로 내려갈 수록 고립 정도가 높고 성능은 떨어진다.</p>
<p>mysql의 기본 격리수준은 REPEATABLE READ이다.</p>
<hr>
<br>

<br>


<h1 id="join">JOIN</h1>
<p>여러 테이블을 하나의 테이블처럼 논리적으로 연결하여 사용하는 방법이다.</p>
<p>inner join은 두 테이블을 교집합으로 join하는 개념이며 outter join은 합집합으로 join하는 개념이다.</p>
<p><img src="https://images.velog.io/images/c-on/post/a66c41cc-082e-4335-a7e0-89ee77b41c9e/image.png" alt=""></p>
<br>

<h1 id="nosql"><a href="https://velog.io/@c-on/NoSQL-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4">NoSQL</a></h1>
<p>블로그의 개별 게시물에 따로 정리를 해두었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 가 가진 특성에 관하여]]></title>
            <link>https://velog.io/@c-on/JavaScript-%EA%B0%80-%EA%B0%80%EC%A7%84-%ED%8A%B9%EC%84%B1%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@c-on/JavaScript-%EA%B0%80-%EA%B0%80%EC%A7%84-%ED%8A%B9%EC%84%B1%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Tue, 18 Jan 2022 16:05:20 GMT</pubDate>
            <description><![CDATA[<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript#for_complete_beginners">모질라 자바스크립트 입문자용</a></p>
<blockquote>
<p>문법이나 메서드에 관한 내용보다, 자바스크립트라는 언어가 가지는 특성을 중심으로 정리</p>
</blockquote>
<br>

<h1 id="🔎자바스크립트의-이해">🔎자바스크립트의 이해</h1>
<p>자바스크립트는 가벼운 <strong>객체 지향 인터프리터 언어</strong>이다. </p>
<p>객체지향 인터프리터 언어란 기계어번역을 위한 컴파일 작업 없이 소스코드를 한줄한줄 읽어가면서 프로그램을 실행시키는 시점에 기계어로 번역하는 언어를 말한다. 그렇기에 컴파일 시간이 필요없지만 컴파일된 프로그램보다는 느리게 동작할 수 있다.</p>
<p>또한, 상속이 가능한 <strong>프로토타입 언어</strong>이다.</p>
<br>

<br>


<h1 id="🔎객체">🔎객체</h1>
<p>자바스크립트에서는 배열, 함수, API 등 대부분의 것들이 <strong>객체</strong>이다. 
따라서 객체 기반의 자바스크립트 본질을 이해하는 것은 매우 중요하다.</p>
<p>객체를 이루는 것은 데이터와 함수인데 데이터는 객체의 <strong>프로퍼티</strong>, 함수는 <strong>메소드</strong>라고 부른다.</p>
<hr>
<h3 id="json과-객체의-차이">JSON과 객체의 차이</h3>
<p><strong>JSON</strong>은 자바스크립트 객체 리터럴 문법을 따르는 문자열이다.</p>
<p>단, 메서드는 담을 수 없다.</p>
<p>프로퍼티의 이름을 작성할 때는 무조건 큰 따옴표로 작성해야 하며 콤마나 콜론을 잘못배치하면 JSON파일이 작동하지 않으므로 주의해야 한다.</p>
<p>JSON을 활용하기 위해 다음 유용한 두가지 API가 있다.</p>
<ul>
<li><code>parse()</code> : JSON문자열을 매개변수로 수용하여 자바스크립트의 객체로 변환해준다.</li>
<li><code>stringify()</code> : 객체를 매개변수로 수용하고 JSON문자열 형태로 변환한다.</li>
</ul>
<br>

<br>

<h1 id="🔎비동기-자바스크립트">🔎비동기 자바스크립트</h1>
<p>우리는 가끔 모니터의 빙글빙글 돌아가는 스피너를 통해 로딩중임을 확인한다. 
이것은 여러 프로세서 코어를 사용할 수 있는 현 시대의 컴퓨터 기술을 잘 활용하지 못한 문제이다.</p>
<p>자바스크립트는 싱글스레드이기때문에 코어가 여러개있더라도 메인스레드에서만 작업을 실행할 수 있다. 하나의 스레드는 하나의 스택을 가지며 이 콜스택에는 차례대로 실행할 함수들이 쌓인다.</p>
<p>nodeJS는 멀티쓰레드이지만 JS를 동작시키는 것은 단 하나의 쓰레드이다. 그래서 노드를 싱글스레드라고 얘기하는 사람들도 있다. 그렇다면 비동기는 어디서 처리되는 걸까?</p>
<p>노드JS 내부적으로 여러가지 모듈들이있다. 그 중 V8엔진으로 자바스크립트를 동작시키고 Libuv라는 라이브러리를 통해 file system, Network, streaming등의 API를 논블로킹I/O로 동작시킨다. </p>
<blockquote>
<p>블로킹이란 어떤 처리때문에 앱이 프로세서 대한 제어권을 반환하지 않는 현상이다.</p>
</blockquote>
<blockquote>
<p>nodeJS가 읽는 자바스크립트 코드는 모두 하나의 싱글스레드(V8)에서 돌아간다. 
하지만 I/O작업을 하는 노드의 내부 모듈은 C로 만들어진 Libuv라는 라이브러리를 통해 논블로킹으로 동작한다. 
따라서 CPU연산이 많은 작업보다 I/O작업에서는 nodeJS가 더 유용하게 사용될 수 있다.</p>
</blockquote>
<hr>
<h3 id="자바스크립트-비동기-동작-흐름">자바스크립트 비동기 동작 흐름</h3>
<pre><code class="language-jsx">console.log(&#39;first&#39;)

setTimeout(function cb() {
    console.log(&#39;second&#39;)
}, 1000);

console.log(&#39;third&#39;)</code></pre>
<p>위 코드를 실행하면 다음과 같은 순서로 런타임이 동작한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/91c6a15b-7b54-4b73-b1e4-f21eddd0b7c3/image.png" alt=""></p>
<ol>
<li>콜스택에 <code>console.log(&#39;first&#39;)</code> 를 추가한다.</li>
<li>콘솔에 <code>first</code>를 출력한뒤 콜스택에서 제거한다.</li>
<li>콜스택에 <code>setTimeout()</code> 을 추가한다.</li>
<li>백그라운드는 웹API의 timer를 호출하고 콜스택에서 제거한다. 요청한 시간이 지나면 콜백함수 <code>cb</code>를 테스크큐에 추가한다.
a. 그 사이에 자바스크립트는 <code>console.log(&#39;third&#39;)</code> 를 콜스택에 추가한다.
b. 콘솔에 <code>third</code>를 출력한 뒤 콜스택에서 제거된다.</li>
<li>이벤트루프는 상시로 콜스택의 상태를 확인하는데 콜스택이 비워지는 순간 테스크큐에 있는 <code>cb</code>를 콜스택에 추가한다.</li>
<li><code>cb</code> 함수가 실행되고 내부에 있는 <code>console.log(&#39;second&#39;)</code>가 콜스택에 추가된다.</li>
<li>스택의 후입선출 특성에 따라 <code>console.log(&#39;second&#39;)</code> 가 먼저 실행 후 제거되고 <code>cb</code>가 마지막으로 콜스택에서 제거되어 모든 코드의 동작이 마무리된다.</li>
</ol>
<hr>
<h3 id="promise란">Promise란</h3>
<p>프로미스는 자바스크립트의 최신 비동기 코드이다. </p>
<p>프로미스는 작업의 완료 또는 실패를 나타내주어 다음 이점을 갖는다.</p>
<ul>
<li><p>콜백지옥에서 자유롭다.</p>
<ul>
<li>콜백이 반복되면 코드가 에너르기파를 맞고 아파한다.        </li>
<li>프로미스는 then을 사용해 다음 작업을 입력함으로써 이런 들여쓰기 반복을 없애준다.</li>
</ul>
</li>
<li><p>catch를 사용해서 각 콜백에서 개별적으로 처리하지 않고 블록 끝의 단일 블록에서 에러를 처리할 수 있다.</p>
</li>
<li><p>then..catch를 사용하면 무슨 일이 일어나고 있는지 훨씬 보기가 쉬워진다.</p>
</li>
<li><p>all을 사용하면 경쟁관계의 비동기의 완료순서를 신경쓰지 않고 코드를 작성하기가 수월해진다.</p>
<ul>
<li><p>가령 a,b라는 비동기 코드가 있고 누가 먼저 완료될 지 알 수 없는 상황에서 두 코드가 수행된 후 다음작업을 진행해야한다면 all을 사용하여 효율적으로 코드를 작성할 수 있다.</p>
<pre><code class="language-jsx">Promise.all([a, b]).then(()=&gt;{
*console*.log(&quot;done!&quot;);
}); </code></pre>
</li>
</ul>
</li>
</ul>
<p>프로미스는 생성될 때 <code>pending</code> 상태를 가진다. 
이후 약속을 반환할 때 성공했을 경우에는 <code>fullfilled</code>, 실패했을 경우 <code>rejected</code> 상태가 된다. </p>
<hr>
<h3 id="promise-만들기">Promise 만들기</h3>
<p>프로미스 생성자를 사용해서 직접 프로미스를 만들 수 있다. 
promise기반이 아닌 구식 비동기 API를 프로미스로 사용할 때 활용한다.</p>
<pre><code class="language-jsx">function timeoutPromise(message) {
  return new Promise((resolve, reject) =&gt; {
    if (message === &#39;&#39; || typeof message !== &#39;string&#39;) {
      reject(&#39;Message is empty or not a string&#39;);
    } else {
      setTimeout(() =&gt; {
        resolve(message);
      }, 1500);
    }
  });
}

timeoutPromise(&quot;&quot;)
.then((m)=&gt;console.log(m))
.catch((e)=&gt;console.error(e))</code></pre>
<hr>
<h3 id="asyncawait">async/await</h3>
<p>함수의 선언 앞에 <code>async</code> 를 넣으면 비동기 함수로 전환된다.</p>
<p>async함수안에서는 promise API앞에 <code>await</code> 를 붙여 동기적으로 사용할 수 있게 한다.</p>
<br>

<br>


<h1 id="🔎엄격모드">🔎엄격모드</h1>
<p>엄격모드를 사용함으로써 다음 변경사항이 발생된다.</p>
<ol>
<li>기존에는 무시됐던 에러를 무시하지 않고 발생시킨다.</li>
<li>최적화 작업을 어렵게 만드는 실수를 없앤다.</li>
<li>ECMAScript의 차기 버전들에서 정의 될 문법을 금지한다.</li>
</ol>
<p>사용하는 방법은 구문 작성 전 제일 상단부에 <code>“use strict”</code>를 삽입하면 된다.
다만 모듈타입으로 자바스크립트를 사용하는 경우 자동으로 엄격모드가 적용되므로 따로 작성해줄 필요는 없다.</p>
<hr>
<h3 id="글로벌-변수-금지">글로벌 변수 금지</h3>
<pre><code class="language-jsx">mistypedVaraible = 17;</code></pre>
<p>변수를 지정하지 않았지만 느슨한모드에서는 전역객체에 대한 새 속성을 만들어 그대로 동작시킨다. 
엄격모드는 이 부분에서 Reference Error를 던진다.</p>
<hr>
<h3 id="실패된-동작에-예외를-발생">실패된 동작에 예외를 발생</h3>
<p><code>undefined</code>를 변수로 지정하면 값을 할당할 수 없다. <code>Infinity</code>도 마찬가지다. 
쓸 수 없는 변수에 값을 할당하는 것에 에러를 던진다. 
뿐만 아니라 생성한 객체의 프로퍼티 등 모든 실패에 에러를 던진다.</p>
<pre><code class="language-jsx">var obj1 = {};
Object.defineProperty(obj1, &quot;x&quot;, { value: 42, writable: false });
obj1.x = 9; // TypeError 발생

var obj2 = { get x() { return 17; } };
obj2.x = 5; // TypeError 발생</code></pre>
<hr>
<h3 id="함수-인자의-유일성">함수 인자의 유일성</h3>
<p>함수의 파라미터의 이름이 유일하도록 요구한다. </p>
<pre><code class="language-jsx">function sum(a, a, c){ // !!! 구문 에러
  &quot;use strict&quot;;
  return a + b + c; // 코드가 실행되면 잘못된 것임
}</code></pre>
<br>

<br>


<h1 id="🔎가비지콜렉션">🔎가비지콜렉션</h1>
<p>가비지 컬렉션은 자바스크립트가 객체가 생성되었을 때 자동으로 메모리를 할당하고 쓸모없어졌을 때 자동으로 해제하는 것을 의미한다.</p>
<p>메모리의 생존주기는 필요할 때 할당해서 사용한 뒤 필요없어지면 해제한다.</p>
<p>할당하고 해제하는 것은 저수준언어에서는 명시적으로 사용되지만 고수준 언어에서는 암묵적으로 작동한다.</p>
<p>자바스크립트는 값을 선언할 때 자동으로 메모리를 할당한다. </p>
<hr>
<h3 id="문제점">문제점</h3>
<p>대부분의 문제는 할당된 메모리가 필요없어졌을 때 해제하는 것에서 발생한다.</p>
<p>자바스크립트는 가비지컬렉션이라는 자동 메모리 관리 방법을 사용하여 해제를 시킨다. 가비지 콜렉터의 목적은 메모리 할당을 추적하고 더 이상 필요하지 않는지를 판단하여 회수하는 것이다.</p>
<p>하지만 메모리가 여전히 필요한지를 판단하는 것은 비결정적 문제이기 때문에 좋지않다.</p>
<p>가비지 콜렉션 알고리즘은 <strong>참조</strong>를 핵심으로 동작한다. 다음 코드를 읽어보면 참조에 대한 쉽게 감을 잡을 수 있다.</p>
<pre><code class="language-jsx">var x = {
  a: {
    b: 2
  }
};
// 2개의 오브젝트가 생성되었다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조된다.
// 나머지 하나는 &#39;x&#39; 변수에 할당되었다.
// 명백하게 가비지 콜렉션이 수행될 메모리는 아직 하나도 없다.

var y = x;      // &#39;y&#39; 변수는 위의 오브젝트를 참조하는 두 번째 변수이다.

x = 1;          // 이제 &#39;y&#39; 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었다.

var z = y.a;    // 위의 오브젝트의 &#39;a&#39; 속성을 참조했다.
                // 이제 &#39;y.a&#39;는 두 개의 참조를 가진다.
                // &#39;y&#39;가 속성으로 참조하고 &#39;z&#39;라는 변수가 참조한다.

y = &quot;mozilla&quot;;  // 이제 맨 처음 &#39;y&#39; 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없다.
                // (역자: 참조하는 유일한 변수였던 y에 다른 값을 대입했다)
                // 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까?
                // 아니다. 오브젝트의 &#39;a&#39; 속성이 여전히 &#39;z&#39; 변수에 의해 참조되므로
                // 메모리를 해제할 수 없다.

z = null;       // &#39;z&#39; 변수에 다른 값을 할당했다.
                // 이제 맨 처음 &#39;x&#39; 변수가 참조했던 오브젝트를 참조하는
                // 다른 변수는 없으므로 가비지 콜렉션이 수행된다.</code></pre>
<p>이 알고리즘을 <strong>Reference-counting</strong> 알고리즘이라 하는데 사실 더 이상 사용되지 않는다.</p>
<p>왜냐하면 순환참조를 이루는 경우에 한계가 발생하기 때문이다. 두 객체가 서로 참조하는 속성으로 생성되면 순환구조를 이루게 되는데 이 때문에 메모리 누수가 발생된다.</p>
<p><strong>Mark-and-sweep</strong>알고리즘으로 이 문제를 해결한다. </p>
<p>이 알고리즘은 roots라는 객체(전역변수들의 집합)로부터 시작하여 roots가 참조하는 객체들을 닿을 수 있는 오브젝트라고 하며 그렇지 않은 것은 닿을 수 없는 오브젝트라고 하여 이것에 대해 가비지 콜렉션을 수행한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript enum과 generic에 관하여]]></title>
            <link>https://velog.io/@c-on/TypeScript-enum%EA%B3%BC-generic%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@c-on/TypeScript-enum%EA%B3%BC-generic%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Mon, 17 Jan 2022 11:42:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/582925cd-6cb8-4ba9-b84f-d0dfc34215c6/image.png" alt=""></p>
<br>

<h1 id="🔎-enum">🔎 enum</h1>
<p>타입스크립트는 숫자와 문자열 기반의 열거형을 제공한다.</p>
<hr>
<h3 id="숫자-열겨헝">숫자 열겨헝</h3>
<pre><code class="language-ts">enum Direction {
    Up = 1,
    Down,
    Left,
    Right,
}</code></pre>
<p><code>Up</code>이 1로 초기화되면 뒤에 따라오는 멤버들은 2부터 차례대로 증가되는 값을 가진다.</p>
<p>초기화를 하지 않는다면 <code>Up</code>은 0부터 시작해서 다음 멤버들은 1부터 증가되는 값을 가진다.</p>
<hr>
<h3 id="문자열-열거형">문자열 열거형</h3>
<pre><code class="language-ts">enum Direction {
    Up = &quot;UP&quot;,
    Down = &quot;DOWN&quot;,
    Left = &quot;LEFT&quot;,
    Right = &quot;RIGHT&quot;,
}</code></pre>
<p>문자열 열거형은 각 멤버를 초기화해야하며, 자동 증가 기능이 없지만 <strong>직렬화</strong>에서 이점을 가진다.</p>
<p>예를 들어 숫자만으로 이것이 어떤 의미인지 정보를 제공받기 어렵지만, 문자열 열거형을 이용하면 코드를 실행할 때 유의미하고 읽기 좋은 값을 이용하여 실행할 수 있다.</p>
<hr>
<br>

<br>


<h1 id="🔎-제네릭">🔎 제네릭</h1>
<p>재사용이 가능한 컴포넌트를 생성하는 주요 도구 중 하나가 <strong>제네릭</strong>이다.
제네릭을 사용해서 단일타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성할 수 있기 때문이다.</p>
<pre><code class="language-ts">function identity&lt;T&gt;(arg: T): T {
    return arg;
}</code></pre>
<p><code>T</code>는 유저가 준 인수의 타입을 캡처하고 이 정보를 나중에 사용할 수 있게 한다.</p>
<p>위 예제는 <code>T</code>를 반환타입으로 다시 사용하여 타입 정보를 함수의 한쪽에서 다른 한쪽으로 운반할 수 있게끔 했다.</p>
<p><code>T</code>대신 <code>any</code>를 사용해도 코드에는 오류가 없지만, 타입 정보를 잃는 손해가 발생한다.
반면 제네릭은 어떤 정보도 잃지 않을 수 있다.</p>
<hr>
<h3 id="변수-작업">변수 작업</h3>
<pre><code class="language-ts">function loggingIdentity&lt;T&gt;(arg: T): T {
  console.log(arg.length);
  return arg;
}</code></pre>
<p>인수의 타입이 문자열이 아니기 때문에 <code>length</code> 프로퍼티는 존재하지 않고 오류가 발생된다.</p>
<hr>
<h3 id="제네릭-제약조건">제네릭 제약조건</h3>
<p>특정 타입으로 동작하는 제네릭 함수를 만들어 보자.</p>
<p>제네릭 타입이지만 <code>length</code> 프로퍼티를 가지는 타입을 원할 때 제네릭이 어떤 타입이 될 수 있는 지 제약 조건을 나열해줄 수 있다.</p>
<pre><code class="language-ts">interface Lengthwise {
    length: number;
}

function loggingIdentity&lt;T extends Lengthwise&gt;(arg: T): T {
    console.log(arg.length);
    return arg;
}</code></pre>
<p><code>T</code>는 인터페이스 규약을 따른다면 <code>length</code> 프로퍼티가 있어야 하며 이는 <code>number</code> 값을 가져야 한다.</p>
<p>따라서 T타입의 인수는 <code>.length</code>를 사용해서 길이를 얻을 수 있게 되는 것이다.</p>
<p>만약 <code>loggingIdentity(3);</code> 명령을 주면 <code>number</code>는 <code>length</code>프로퍼티가 없으므로 오류가 발생한다.</p>
<hr>
<h3 id="객체의-키를-타입의-제약조건으로-설정하기">객체의 키를 타입의 제약조건으로 설정하기</h3>
<pre><code class="language-ts">function getProperty&lt;T, K extends keyof T&gt;(obj: T, key: K) {
    return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, &quot;a&quot;); 
getProperty(x, &quot;m&quot;); </code></pre>
<p>K는 T의 key로 제약되어 있다.</p>
<p>따라서 <code>m</code>은 <code>x</code>라는 객체에 존재하지 않는 키이기 때문에 오류가 발생한다.
반면 <code>a</code>는 존재하는 키이기 때문에 정상동작한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[nodeJS 서버 AWS에 배포하기 - (3) nodejs, Git, pm2]]></title>
            <link>https://velog.io/@c-on/aws-s3-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@c-on/aws-s3-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Sat, 15 Jan 2022 16:10:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/9f5387cc-058c-4797-b5e3-4dd833dcf482/image.png" alt=""></p>
<h1 id="🔨-nodejs-설치">🔨 nodeJS 설치</h1>
<p><img src="https://images.velog.io/images/c-on/post/9a4eafe1-7618-457a-9c9f-bf0637c194c5/image.png" alt=""></p>
<p><a href="https://velog.io/@c-on/TypeDI-%EC%9D%B4%EC%A0%9C%EA%B9%8C%EC%A7%80-%ED%95%B4%EC%98%A8-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85">저번</a>에 접속했던대로 인스턴스에 ssh연결로 접속을 한다.</p>
<h3 id="apt-get-update">apt-get update</h3>
<p><code>sudo apt-get update</code>를 입력해준다.
이 명령어는 운영체제의 설치가능한 리스트를 업데이트 해주는 명령어다.</p>
<h3 id="nodejs설치">nodejs설치</h3>
<p><code>sudo apt-get install nodejs</code>를 입력하여 nodejs를 설치한다.</p>
<p>당연히 설치가 끝났으면 hello world 한번 찍어줘야 한다.</p>
<p><code>nodejs</code>를 입력하고</p>
<pre><code class="language-js">console.log(&quot;hello, world!&quot;)</code></pre>
<p>를 입력해서 nodejs님께 인사</p>
<h3 id="npm">npm</h3>
<p>node의 가장 큰 라이브러리 생태계인 npm을 설치해준다.</p>
<p><code>sudo apt-get install npm</code>을 입력</p>
<h1 id="🔨-git">🔨 GIT</h1>
<p>github에서 프로젝트를 클론해서 서버를 실행할 것이기 때문에 인스턴스에 git을 설치해준다.</p>
<p><code>suto apt-get install git</code></p>
<p>설치가 완료되면 <code>git --version</code>을 입력해서 git의 버전을 확인할 수 있으면 성공적으로 설치가 된 것이다.</p>
<p>이제 git을 사용하려면 username과 email을 설정해줘야 한다.</p>
<p><code>git config --global user.name 내 아이디</code>
<code>git config --global user.email 이메일</code>
을 차례로 입력해준다.</p>
<p>이제 대망의 클론</p>
<p>깃허브저장소로 가서 주소를 복사해온 뒤</p>
<p><code>git clone 주소</code>를 입력한다.
만약 권한없다고 하면 명령어 앞에 sudo붙여주기.</p>
<p>이제 cd &lt;폴더명&gt; 으로 클론한 폴더의 루트디렉토리로 이동한 뒤 <code>npm i</code>명령으로 dependency 모듈 설치해준다.</p>
<p>그리고 npm start(프로젝트할 때 package.json에 설정안되어 있으면 걍 <code>node index.js</code> )를 입력하면 실행이 된다!</p>
<p>이제 node 웹서버로 연결한 포트번호를 ec2의 엔드포인트의 뒤에 <code>:</code>와 함께 붙여서 접속하면 성공!</p>
<p>하지만 인스턴스를 나가면 서버도 중지되므로 무중단 모듈을 사용해줘야 한다.</p>
<h1 id="🔨-pm2">🔨 pm2</h1>
<p>별것 없다. 별 것 없는데 필요한 기능이 잘 수행되면 그것만큼 좋은 모듈은 없다.</p>
<p><code>npm i -g pm2</code>를 입력해서 pm2를 서버의 전역에 설치해준다.</p>
<p>그리고 <code>pm2 start app.js</code>를 입력하면 끝!</p>
<p><strong><em>일 줄 알았지만</em></strong> ts파일만 있어서 실패.
다시 <code>npm i -g typescript</code>로 타입스크립트 인스턴스에 전역으로 설치해주고 다시 프로젝트폴더에서 tsc해서 js파일만들어줬다.
그러고나서 드디어 <code>pm2 start dist/app.js</code>로 실행 성공!</p>
<p>이 부분은 배포자동화할 때 함께 자동화될 수 있도록 해결해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[nodeJS 서버 AWS에 배포하기 - (2) RDS,S3]]></title>
            <link>https://velog.io/@c-on/aws-ec2-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@c-on/aws-ec2-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Sat, 15 Jan 2022 16:10:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/89dbc6e6-f320-46ae-993e-3d677dfb34f0/image.png" alt=""></p>
<p>인스턴스를 생성했다면 그 안에 mysql을 설치해서 사용할 수 있다. 하지만 실무에서는 그렇게 하는 것을 지양한다. </p>
<p>DB서버는 따로 분리를 하는데 그렇게 되면 다음의 장점을 가진다.</p>
<p><strong>1.확장성</strong>
병렬 구조의 서버 증설이 필요할 때 DB 서버는 분리될 수 밖에 없다.</p>
<p><strong>2.보안성</strong>
WAS서버가 해킹을 당했을 때 루트 권한이 모두 넘어가버리면 데이터를 탈취당할 수 있다. 하지만 DB서버를 분리해놓고 제한적인 권한만 부여했다면 DB는 지킬 수 있는 것이다.</p>
<p>AWS에서는 relation database service 줄여서 RDS를 통해 MySQL서버를 만들 수 있다.</p>
<h1 id="🔨-rds-생성">🔨 RDS 생성</h1>
<p><img src="https://images.velog.io/images/c-on/post/e0b75ac7-77d3-49c4-9b96-bcbfbd3df63e/20220113214526.png" alt=""></p>
<p>검색창에 RDS를 검색해서 접속한다. 위에 떠있는 데이터베이스 생성 말고 밑에 있는 <code>데이터베이스 생성</code>을 클릭한다.</p>
<p>위에것은 Amazon Aurora 데이터베이스 생성이다.</p>
<p><img src="https://images.velog.io/images/c-on/post/79d49eb2-1299-49c3-ac84-364722db2137/image.png" alt=""></p>
<p>mysql선택하고 당연히 프리티어를 선택해준다.
그리고 설정으로 넘어가면 <code>인스턴스 식별자</code>를 입력해준다. 
무슨 DB서버인지 내가봤을 때 구별할 수 있는 이름으로 지정해주는게 좋다.</p>
<p><code>마스터사용자이름</code>은 그냥 root로 했고 <code>마스터암호</code>만 조금 어렵게 설정해주었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/a4a0f772-d26d-44e6-a513-d216ce8fed45/image.png" alt=""></p>
<p>퍼블릭엑스를 허용해주고 보안그룹을 하나 선택해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/66efe09e-dba3-4033-8643-175b7440762f/20220113214901.png" alt=""></p>
<p>암호인증을 선택하고 데이터베이스 새성을 누르면 </p>
<p><img src="https://images.velog.io/images/c-on/post/5ed3d9ab-9cf9-4562-b689-5ba3eb2f724b/image.png" alt=""></p>
<p>이렇게 생성이된다.</p>
<p>이제 인스턴스에 접속해서 엔드포인트를 복사하고 워크벤치에서 연결해보면 되는데!</p>
<p><img src="https://images.velog.io/images/c-on/post/4c193d07-fc7a-4057-9676-a75545e29c77/image.png" alt=""></p>
<p>오류가 떠서 구글에 검색도 해보고,
configure remote management로 검색관리도 해봤는데,
이렇게 연결이 안된다. </p>
<p>알고보니 인바운드 규칙에 mySQL 포트는 설정을 안해놔서 연결이 안됐었던것... ㅜㅜ</p>
<p>인바운드 규칙 수정후에는 바로 연결 성공!</p>
<br>

<h1 id="🔨-s3">🔨 S3</h1>
<p>s3는 simple storage service의 준말이다. 즉, 객체 스토리지 서비스로 데이터를 저장하고 보호한다. </p>
<p>무한대의 객체를 저장할 수 있기 때문에 확장, 축소에 신경을 쓰지않아도 된다.</p>
<p>HTTPS프로토콜을 사용하기 때문에 데이터를 안전하게 업로드/다운로드 할 수 있다. </p>
<p><strong>버킷</strong>이란것을 생성하여 사용하는데, 이는 최상위 디렉토리 개념으로 s3 리전 중 유일한 것이다. </p>
<p><strong>객체</strong>는 데이터와 메타데이터를 구성하고 있는 저장단위이다.</p>
<h2 id="생성">생성</h2>
<p>s3를 검색해서 콘솔로 이동한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/d0613143-90e6-43ab-8bbd-17cad21c2184/image.png" alt=""></p>
<p>오른쪽의 버킷만들기를 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/8a019209-23e7-4381-bfe1-513ecc0c500e/image.png" alt=""></p>
<p>이름을 설정하고</p>
<p><img src="https://images.velog.io/images/c-on/post/d9fb8a47-dc19-4d55-954a-274b08930370/image.png" alt=""></p>
<p>외부에서 접속할 수 있도록 퍼블릭 엑세스 차단을 비활성화해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/1ddb1d71-49d1-4297-9b82-ee575e1b7c0f/20220113233441.png" alt=""></p>
<p>버킷버전과리와 기본암호화도 비활성화 해주면 버킷이 생성된다.</p>
<p><img src="https://images.velog.io/images/c-on/post/0af13ddd-1aad-45c3-8d8a-32815edd1298/image.png" alt=""></p>
<h2 id="iam">IAM</h2>
<p>IAM은 aws리소스에 대한 개별적 접근제어와 권한을 주는 서비스이다. 
모든 리소스에 접근 가능하면 그 권한을 해당하는 경우 매우 위험해질 수 있기 때문에 IAM을 통해 s3에만 사용이 가능한 계정을 만들것이다.</p>
<p>IAM을 검색해서 콘솔로 이동한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/c803fead-70f4-4e1f-9be8-e158a8c3523a/20220114005021.png" alt=""></p>
<p>왼쪽의 사용자 메뉴를 누르고 오른쪽 상단의 사용자 추가를 클릭한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/c49a2c48-691b-43d6-9318-ac812bda8dcf/20220114005104.png" alt=""></p>
<p>사용자 이름을 기입하는데 간단하게 구분하기 쉽도록 s3-user을 입력해줬다.</p>
<p>그리고 권한설정에서 그룹을 생성해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/5739401e-bf26-427b-82fd-f0c7d0e2306d/20220114005136.png" alt=""></p>
<p>이렇게 그룹을 생성할 수 있는 창이 보이면 정책 필터에 s3를 검색하고 <code>AmazonS3FullAccess</code>를 선택해서 그룹생성을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/c50e0e7a-71c6-4b0a-9827-8f00b0b2d480/20220114005146.png" alt=""></p>
<p>다음:태그 를 누르고 다음 화면에서는 <code>사용자 만들기</code>를 누른다.</p>
<p>그럼 성공했다고 안내와 함께 엑세스 키 ID와 비밀 엑세스 키가 나오는데 csv로도 다운받고 값들을 어딘가에 복사하거나 저장해둔다. 
뒤에 nodejs연동에 꼭 필요한 값들이다.</p>
<h2 id="nodejs와-s3-연동">nodejs와 s3 연동</h2>
<p>프로젝트에 적용하기 전에 간단하게 동작할 수 있는 서버를 구축해봤다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e5150cde-6a6d-4f49-b5ca-84ececccb201/image.png" alt=""></p>
<pre><code class="language-js">import express from &quot;express&quot;;
import multer from &quot;multer&quot;;
import AWS from &quot;aws-sdk&quot;;
import multerS3 from &quot;multer-s3&quot;;
import path from &quot;path&quot;;
import dotenv from &quot;dotenv&quot;;

const app = express();

AWS.config.update({
  accessKeyId: process.env.S3_ACCESS_KEY_ID,
  secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  region: &quot;ap-northeast-2&quot;,
});

const upload = multer({
  storage: multerS3({
    s3: new AWS.S3(),
    bucket: &quot;???&quot;,
    key(req, file, cb) {
      cb(null, `post/${+new Date()}${path.basename(file.originalname)}`);
    },
  }),
});

app.post(&quot;/&quot;, upload.single(&quot;img&quot;), (req, res, next) =&gt; {
  console.log(req.file);
  res.json({ url: req.file.location });
});

app.listen(8080, () =&gt; {
  console.log(&quot;server starts&quot;);
});
</code></pre>
<p>테스트 해보기 일단 간단하게 코드를 작성했다.</p>
<p><img src="https://images.velog.io/images/c-on/post/f9cb8f72-f089-443d-9b5a-dd0d28b52cd0/image.png" alt=""></p>
<p>포스트맨으로 호기롭게 이미지를 보내봤는데..</p>
<p><img src="https://images.velog.io/images/c-on/post/bc22b8b3-8340-482a-b8d1-7523db1cab51/image.png" alt=""></p>
<p>이런 응답만 받고 실패.. </p>
<p>한번에 될리가 없.지.</p>
<p>뭐가 문제지 다 콘솔에 찍어보다보니 env변수가 값을 못가져오고 있는 것을 발견했다.</p>
<p>아.. <code>dotenv.config()</code>를 안해줬구나</p>
<p>import 모듈 끝나는 줄 밑에 <code>dotenv.config()</code>를 추가해주고 다시 이미지를 전송해보니</p>
<p><img src="https://images.velog.io/images/c-on/post/e1b0db11-9483-45b6-8782-b2691ee42773/image.png" alt=""></p>
<p>이렇게 정상적으로 수신이 잘 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[nodeJS 서버 AWS에 배포하기 - (1) EC2]]></title>
            <link>https://velog.io/@c-on/TypeDI-%EC%9D%B4%EC%A0%9C%EA%B9%8C%EC%A7%80-%ED%95%B4%EC%98%A8-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@c-on/TypeDI-%EC%9D%B4%EC%A0%9C%EA%B9%8C%EC%A7%80-%ED%95%B4%EC%98%A8-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Wed, 12 Jan 2022 01:11:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/5bdbdcfd-101b-4b86-aeb2-54d2a074db21/image.png" alt=""></p>
<p>이전 프로젝트는 Google cloud platform의 app engine으로 배포를 진행했었다. 구글의 문서가 깔끔하게 잘 정리되어있어서 진행하는데에 큰 무리는 없었다. </p>
<p>문서가 잘 정리되어있더라도 무언가 필요한게 있을 때, 그런데 관련 내용이 문서의 어디에 있는지 못 찾겠을 때는 어쩔수없이 구글링을 해야한다. 하지만 GCP는 구글링해도 정보가 많이 나오질 않아서 곤혹이었다.</p>
<p>또 무료사용 기한이 3개월 밖에 되지 않는다. 이거 때문에 예전에 IoT서비스 프로젝트할 때 애를 많이 먹었었는데 여기서도 발목을 잡게될 줄은 몰랐다.</p>
<p>그래서 <em>&#39;다음 프로젝트는 무조건 aws로 배포해야지..&#39;</em> 라며 다짐했고 그 다짐을 지금 실현하게 되었다. 그냥 막 정보찾아서 배포하기보단 지금 사용하는 것이 무엇이고 어떤 원리(컴퓨터 구조적인 원리 그런게 아니라 배포 과정,흐름에서 맡는 역할)을 이해하고, 왜 이렇게 설정을 해야하는지 기록해가면서 배포를 해보려 한다.</p>
<h1 id="🔨-인스턴스-생성">🔨 인스턴스 생성</h1>
<p>가입은 특별한게 있는게 아니니 패스.
대신 무료랑 유료 선택하는 것들은 전부 무료를 선택해주면 된다.</p>
<p>aws콘솔에 로그인하고 왼쪽 검색창에서 <code>EC2</code>를 검색해서 접속한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/71cb0fbb-243c-4ee2-8b9d-ff366431d710/image.png" alt=""></p>
<p>좌측에 보면 인스턴스가 있다. 인스턴스에 접속해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/35556bc1-4986-4ca9-96db-45733de4a8ab/image.png" alt=""></p>
<p>우측 상단에 <code>인스턴스시작</code>을 클릭해서 새로운 인스턴스 생성을 시작한다.</p>
<blockquote>
<p><strong>인스턴스</strong>란 클라우드의 가상서버이다. 가상이라해서 다른것이 없고, 원격으로 조종할 수 있는 컴퓨터 한대를 대여했다고 생각하면 된다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/c-on/post/206cda34-e676-4d14-b8b1-4e06c17bceb5/image.png" alt=""></p>
<p>AMI를 선택할 건데 유료는 안되므로 왼쪽에서 <code>프리티어만</code>을 체크해준다. 
나는 ubuntu server 20.04 64비트를 선택했다.</p>
<blockquote>
<p><strong>AMI</strong>는 가상서버에 필요한 소프트웨어 정보를 담고있는 탬플릿 이미지이다. OS와 어플리케이션이 포함되어 있어 인스턴스 생성할 때 그대로 설치된다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/c-on/post/84993e0d-19df-4ec8-a43d-77a2dc7fb05b/image.png" alt=""></p>
<p>인스턴스 유형을 선택하여 CPU와 메모리, 스토리지 용량을 원하는 옵션으로 설정할 수 있다. 하지만 요금 내기 싫으므로 프리티어 사용 가능이 적혀있는 <code>t2 micro</code>를 선택한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/bc58a5d5-38e8-4bb3-bdf0-2c8fa1cdcc5e/image.png" alt=""></p>
<p>검토 및 시작을 누르면 키 페어를 선택하는 다이얼로그가 뜬다. </p>
<p>키 페어 없으므로 <code>새 키 페어 생성</code>을 선택하고 이름은 어느 인스턴스의 키 페어인지 알 수 있도록 지정해준다.</p>
<p>키페어 다운로드를 눌러서 잘 저장시키고 활성화된 인스턴스 시작 버튼을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/48f4b947-9c13-434d-b26f-5c4539fddbf4/image.png" alt=""></p>
<p>이렇게 되면 인스타스 생성이 완료된다.</p>
<br>

<h1 id="🔨-보안그룹-설정">🔨 보안그룹 설정</h1>
<p>보안그룹은 트래픽을 제어하는 가상 방화벽 역할을 맡는다. 포트포워딩을 여기서 간단하게 설정해주는 것이다. 너무 편리하다. </p>
<p><img src="https://images.velog.io/images/c-on/post/ffdb8685-cfd8-4c15-8afe-8b115e0ee717/image.png" alt=""></p>
<p>다시 EC2 콘솔로 이동하면 왼쪽 아래에 보안그룹이 있다 접속한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e8798c80-bafa-451b-a003-eb77f04d0a2d/image.png" alt=""></p>
<p>오른쪽 상단에 있는 <code>보안 그룹 생성</code>을 클릭.</p>
<p><img src="https://images.velog.io/images/c-on/post/89d06a95-cdff-47df-8769-43c6f44416cc/image.png" alt=""></p>
<p>보안 그룹 이름을 설정해주는데 통상적으로 적용될 보안그룹이라면 <code>basic</code>이라고 지정해주면 된다.
난 아무 단어나 썼다.
설명은 쓰고 싶으면 쓰면 된다. 난 쓰기 싫어서 패스~</p>
<p><img src="https://images.velog.io/images/c-on/post/8e47a205-dda6-479c-b245-3e29f07c94e7/20220113211303.png" alt=""></p>
<p>IP로 접속하게 되면 포트를 따로 지정해주지 않는 경우 기본적으로 80포트로 접속한다고 한다. 그래서 80포트는 열어주되 IPv4(0.0.0.0/0)와 IPv6(::/0)을 각각 열어준다.
뒤에 나오는 모든 설정이 IPv4와 IPv6를 모두 열어줄 것이다.</p>
<p>HTTPS는 443포트를 사용하므로 그에 맞춰 규칙 추가.</p>
<blockquote>
<p>국제 표준 문서에 http 기본포트는 80포트이고 https 기본포트는 443포트라고 기술한다. 80포트는 비어있는 포트를 고른것이었고 443포트는 이유불문이다.</p>
</blockquote>
<p>4200포트는 개발용 서버 포트, 3000포트는 실제 서비스 서버 포트이다. </p>
<h3 id="❗❗추가적으로-ssh와-mysql서버도-규칙을-추가해줘야-한다">❗❗추가적으로 SSH와 MySQL서버도 규칙을 추가해줘야 한다.</h3>
<p><img src="https://images.velog.io/images/c-on/post/0ba4d8bb-f1b6-4ccd-a1eb-a47d76683ca9/image.png" alt=""></p>
<p>최종적으로는 이렇게 규칙이 만들어져야한다.</p>
<p>RDS도 생성해서 사용할 때 이 규칙을 활용하기 위해서 <code>mysql/aurora</code> 유형의 규칙도 추가해준다. 포트는 기본적으로 3306포트이다.</p>
<p>이거 안해줬다가 RDS생성해서 이 보안그룹 적용해주고 접속이 안돼서 한참 해맸었다.</p>
<p><img src="https://images.velog.io/images/c-on/post/232c7893-910f-4b44-9486-db2a1ef0c672/image.png" alt=""></p>
<p>아웃바운드는 자유롭게 나갈수 있도록 모두 열어준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/bd244d87-55df-4f53-90f0-e3420f496e66/image.png" alt=""></p>
<p>생성을 눌러주면 초록생 창에서 생성 알림을 띄워준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e06f3f69-38cd-4564-9bc6-b99622f485e8/image.png" alt=""></p>
<p>인스턴스 콘솔에서 생성한 인스턴스를 체크하고 오른쪽 상단의 작업을 누르면 여러가지 메뉴가 내려온다.
그 중 보안을 누르고 보안그룹 변경을 눌러준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/b011d30a-fbde-45eb-96b2-b8bd9dee9770/image.png" alt=""></p>
<p>보안 그룹 검색 창에서 방금 만든 보안그룹의 이름을 검색해서 추가한 뒤 저장한다. </p>
<p><img src="https://images.velog.io/images/c-on/post/ce90f2ab-2d3c-43be-9d0d-63ce6d67902b/image.png" alt=""></p>
<h1 id="🔨-탄력적-ip-할당">🔨 탄력적 IP 할당</h1>
<p>탄력적 IP는 정적 IPv4 주소이다. 
나중에 도메인을 인스턴스와 연결해줄 때 DNS에 탄력적 IP 주소를 지정해줄 때 필요하다..</p>
<p>원래 인스턴스 주소는 서버를 잠깐 중지했다가 다시 실행하면 변해버리는 유동성을 가지고 있다. 그 때마다 DNS에 IP주소를 변경해 줄 수 없으니 탄력적 IP를 할당해서 인스턴스와 연결해주어야 한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/80ae875a-18c6-483a-8493-98f084ee532f/image.png" alt=""></p>
<p>콘솔에서 왼쪽 아래로 내려가서 탄력적 IP를 클릭한다.</p>
<p><img src="https://images.velog.io/images/c-on/post/e3de0cf5-985c-422f-82e1-692df6591cd6/image.png" alt=""></p>
<p><code>탄력적 IP 주소 할당</code>을 클릭한다. 그 다음에 뭐 설정,태그 이런거 뜨는데 따로 만질 것 없으므로 바로 할당을 누른다.</p>
<p>그럼 다시 초록생 알림창이 뜨면서 &quot;할당되었습니다&quot; 한다.
그 아래 <code>탄력적 IP 주소 연결</code>이 같이 뜬다. 클릭한다.
안뜨면 생성한 IP주소 선택해서 상단 작업 메뉴를 클릭해서 IP 주소 연결하는 메뉴를 들어가면 될 듯 하다.</p>
<p><img src="https://images.velog.io/images/c-on/post/37be24b0-5c64-4146-90ec-9d41e47d3c79/image.png" alt=""></p>
<p>탄력적 IP주소를 연결할 인스턴스를 고르고 <code>연결</code>을 클릭!</p>
<p>이렇게 까지 하면 EC2의 모든 설정이 끝이났다~~</p>
<p>이제 접속해봐야겠다.</p>
<h1 id="🔨-접속">🔨 접속</h1>
<p>라즈베리파이 ssh연결할 때 putty를 사용했는데 좀 불편했다. 오래돼서 어떻게 사용했고 뭐가 불편했는지 구체적으로 기억은 안나지만 하여튼 불편했다.</p>
<p>요즘엔 모바엑스텀을 많이 사용하는거 같아서 이걸로 접속해보기로 한다.</p>
<p><a href="https://mobaxterm.mobatek.net/download.html">https://mobaxterm.mobatek.net/download.html</a></p>
<p><img src="https://images.velog.io/images/c-on/post/8687ea26-553f-45fe-8a94-2bfd989bc0a3/20220113212323.png" alt=""></p>
<p>당연히 프리로 다운로드</p>
<p><img src="https://images.velog.io/images/c-on/post/cb01dc7b-f28b-439c-9201-3d87ef4049e4/20220113212700.png" alt=""></p>
<p>둘 중 아무거나 선택해도 똑같은 거 설치된다.</p>
<p><img src="https://images.velog.io/images/c-on/post/03f9fce4-8610-4d68-8245-e79c2e23efe2/image.png" alt=""></p>
<p>왼쪽폴더표시된 공간에 마우스를 옮기고 우클릭을 하면 <code>new session</code>이 있다. 클릭.</p>
<p><img src="https://images.velog.io/images/c-on/post/259aa353-a654-4acd-b145-ae1e839c9046/image.png" alt=""></p>
<p>가장 왼쪽의 SSH를 클릭.</p>
<p>그러면 두 가지 파트로 설정하는 것들이 나눠지는데 위 파트에서 <code>Remote host</code>는 public DNS를 입력해주고 <code>Specify username</code>은  ubuntu를 입력한다.</p>
<p>아래 <code>Advanced SSH settings</code>를 누르고<code>Use private key</code>를 선택해서 인스턴스 생성에서 다운받은 키 페어(pem)을 등록해준다.</p>
<p><img src="https://images.velog.io/images/c-on/post/df95f95b-84d0-42a9-a829-da913eefe1d6/image.png" alt="">
이렇게 뜨면 드디어 접속까지 성공<del>~</del>!😎</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS express] 세션 인증]]></title>
            <link>https://velog.io/@c-on/TS-express-%EC%84%B8%EC%85%98-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@c-on/TS-express-%EC%84%B8%EC%85%98-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Thu, 06 Jan 2022 14:15:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/09055d7f-eb60-43cc-94e8-a27e283ec6b9/image.png" alt=""></p>
<h2 id="jwt의-장단점">JWT의 장단점</h2>
<blockquote>
<p>암호화방식이나 타입과 함께 유저아이디, 유효기간 등을  jwt서버로 전달하면 그 정보들을 jwt가 해독할 수 있는 토큰으로 발행받는다. 따라서 토큰에는 모든 정보들이 jwt만 읽을 수 있는 형태로 저장되어 있다.</p>
</blockquote>
<ul>
<li>서버에 데이터를 저장하지 않아(stateless)서 스케일링, 유지보수에 유연하다.<ul>
<li>근데 페이스북 정도가 아니면 스케일에 대응하기 어려운 것은 아닌것 같다.</li>
</ul>
</li>
<li>jwt정보를 다른 사람이 훔치게 되면 훔친 정보로 로그인이 가능해진다.<ul>
<li>토큰 유효기간을 짧게 하고 대신 refresh토큰을 사용하면 된다. ⇒ <strong>어디차박에 적용해보자!</strong></li>
</ul>
</li>
<li>전달된 토큰은 돌이킬 수 없다.<ul>
<li>악의적인 사용자가 유효기간전 정보 탈취가 가능해질 수 있다.</li>
<li>물론 서버만 알고있는 시크릿키가 있어야 토큰을 디코딩할 수 있지만 토큰자체를 해석하는 방법을 찾는다면 시크릿키 없이도 디코딩이 될 가능성은 분명히 있다.</li>
</ul>
</li>
</ul>
<h2 id="세션쿠키의-장단점">세션쿠키의 장단점</h2>
<blockquote>
<p>요청받은 로그인 데이터가 데이터베이스의 데이터와 일치하면 세션을 세션 저장소에 저장한 뒤, 다시 이와 연결되는 세션 ID를 발행해서 쿠키에 실어 보낸다.</p>
</blockquote>
<ul>
<li>쿠키 자체에는 계정정보가 들어있지 않기 때문에 http요청 중 노출되더라도 중요한 정보가 노출될 위험 부담이 적어진다.</li>
<li>세션 하이재킹 공격(http요청을 가로채 쿠키를 탈취)시에 그 쿠키를 이용해서 요청을 보낼 수 있다.<ul>
<li>ssl을 적용하고 세션에 유효기간을 설정한다.</li>
</ul>
</li>
<li>추가 저장공간이 필요하게 되고 확장, 유지보수에 추가 작업이 필요해진다.</li>
</ul>
<h2 id="세션-방식을-선택한-이유">세션 방식을 선택한 이유</h2>
<p>아무래도 토큰에 정보들이 들어가있다는 것이 부정적으로 다가온다. 또, 부득이 한 경우에 서버에서 사용자의 로그인 상태를 관리해야 하거나 모든 사용자를 로그아웃시켜야만 하는 경우가 있을 때 jwt인증방식은 할 수 있는 것이 없다. 이러면에서 장점과 단점을 비교했을 때 jwt만 가지는 장점보다 세션방식만 가지는 장점이 더 많아서 세션을 사용한 인증을 구현하기로 했다. </p>
<h1 id="🔨-사용하기">🔨 사용하기</h1>
<h3 id="express-session-flow">express-session flow</h3>
<ul>
<li><p>요청이 들어오면 세션 미들웨어를 통과하여 req에 session이라는 속성을 생성해서 미들웨어에서 설정한 옵션들을 쿠키 데이터로 할당한다.</p>
<pre><code class="language-tsx">  [1] Session {
  [1]   cookie: {
  [1]     path: &#39;/&#39;,
  [1]     _expires: 2022-01-08T15:27:54.237Z,
  [1]     originalMaxAge: 10000,
  [1]     httpOnly: true,
  [1]     secure: false
  [1]   }
  [1] }</code></pre>
</li>
<li><p>속성만 추가되었을 뿐, 세션 저장소에는 이 정보들을 저장하지 않는다.</p>
</li>
<li><p>req.session에 새로운 속성을 추가해주면 그때 세션이 선언되었다고 간주하여 세션저장소에 저장을 진행한다. </p>
</li>
<li><p>connect.id를 키로 가지며 선언한 것들을 value로 가지는 쿠키를 응답에 동봉하여 보낸다.</p>
<pre><code class="language-tsx">  //세션저장소 data필드에 저장되는 것
  {&quot;cookie&quot;:{&quot;originalMaxAge&quot;:10000,&quot;expires&quot;:&quot;2022-01-08T15:53:35.293Z&quot;,&quot;secure&quot;:false,&quot;httpOnly&quot;:true,&quot;path&quot;:&quot;/&quot;},&quot;is_logined&quot;:true,&quot;userId&quot;:1,&quot;dispayName&quot;:&quot;james&quot;}</code></pre>
</li>
<li><p>클라이언트가 받은 세션id역시 같은 시간의 expire옵션이 설정되어있다. 브라우저에서 expire 값을 변경하여 브라우저에 계속보관하더라도 서버에서는 세션id와 매핑되는 세션저장소의 데이터가 data필드의 expires가 만료된 상태면 사용하지 않는다.</p>
<ul>
<li>따라서 session옵션을 설정할 때 resave를 false로 둬서 처음 저장되었을 때의 expires를 유지해줘야 한다.</li>
</ul>
</li>
<li><p>로그아웃을 할 경우 destroy를 사용하면 세션저장소의 세션데이터를 삭제하고 req.session도 모두 삭제한다. 브라우저에 저장되어 있는 쿠키는 따로 삭제해주지 않아도 활용가치가 없는 id가 된다</p>
</li>
</ul>
<hr>
<h3 id="mysql에-세션데이터-저장">MySQL에 세션데이터 저장</h3>
<p>express-session만 사용하게 되면 서버의 메모리에 데이터를 저장하므로 서버가 재구동될 때 데이터가 모두 휘발된다. 따라서 다른 저장소에 데이터를 저장해줘야 하는데 mysql과 함께 사용할 수 있는 패키지로 <code>express-mysql-session</code> 이 있다.</p>
<pre><code class="language-tsx">import * as session from &quot;express-session&quot;
import expressMySqlSession from &quot;express-mysql-session&quot;;
import mysql2 from &quot;mysql2/promise&quot;;

const MySQLStore = expressMySqlSession(Session);
const connection = mysql2.createPool(config.db);
const sessionStore = new MySQLStore({}, connection);
app.use(
  session({
    secret: &quot;asdfasffdas&quot;,
    resave: false,
    saveUninitialized: false,
    store: sessionStore,
    cookie: {
      httpOnly: true,
      secure: false,
    },
  })
);</code></pre>
<ul>
<li>resave<ul>
<li>false로 해주어야 세션 저장소의 데이터가 바뀌지 않으면 세션을 저장하지 않도록 할 수 있다.</li>
<li>true로 지정하게 되면 세션저장소의 data필드의 expires 값이 새로 들어오는 요청마다 갱신이 되므로 원래 기대했던 시간에 삭제되지 않는다.</li>
</ul>
</li>
<li>saveUninitialized<ul>
<li>d.ts에 작성된 설명 : 선언되지 않은 세션이 저장소에 저장되도록 강요하는 옵션이다. 로그인 세션을 구현하는 경우에는 false로 설정해야 서버의 부하를 줄여준다.</li>
<li>따라서 false로 선언하면 회원가입이 성공되면 그때 세션을 선언해줌으로써 세션이 저장소에 저장되도록 할 수 있다.</li>
</ul>
</li>
</ul>
<p>이때 config로 부터 받아온 object의 타입이 정의되어 있지 않기 때문에 오류가 난다. 따라서 db환경변수 데이터는 다음과 같이 타입을 강제해준다.</p>
<pre><code class="language-tsx">db: {
    host: required(&quot;DB_HOST&quot;) as string,
    port: parseInt(required(&quot;DB_PORT&quot;)) as number,
    user: required(&quot;DB_USER&quot;) as string,
    password: required(&quot;DB_PASSWORD&quot;) as string,
    database: required(&quot;DB_DATABASE&quot;) as string,
  },</code></pre>
<p>여기까지 설정했으면 마지막으로 이제부터 발생하는 오류를 하나하나 해결해줘야 정상적으로 동작시킬 수 있다.</p>
<hr>
<h3 id="오류-1-session-parameter-속성-부재">오류 1. session parameter 속성 부재</h3>
<p><img src="https://images.velog.io/images/c-on/post/8bb4903d-fe16-4993-8e2c-7831fc9eb3fa/image.png" alt=""></p>
<p>express의 session형식에는 설정한적이 없는 속성이기 때문에 TS에서는 입력할 수 가 없다. </p>
<p>다음의 코드를 app.ts의 상단에 작성한다.</p>
<pre><code class="language-tsx">declare module &quot;express-session&quot; {
  export interface SessionData {
    is_logined?: boolean;
    dispayName?: string;
    userId?: number;
  }
}</code></pre>
<p>세션에 추가할 속성들에 대해서 위 코드에 선언해주면 할당이 가능해진다.</p>
<hr>
<h3 id="오류-2-mysql연동-오류">오류 2. mysql연동 오류</h3>
<p>오류메시지는 다음과 같다.</p>
<p><code>Client does not support authentication protocol requested by server; consider upgrading MySQL client</code></p>
<p>플러그인이 <code>caching_sha2_password</code> 를 사용하지 못하는 오류이다.</p>
<p>MySQL5,7까지는 <code>mysql_native_password</code> 가 default였다. 하지만 이 경우 hash코드를 탈취하면 비밀번호를 알아낼 수 있는 문제가 생긴다. 이를 막고자 RSA key를 이용한 salt 추가 방법으로 보안을 강화시키기로 했는데 여러번의 연산이 필요한 단점이 생겼다. 다시 이를 보완하기 위해 key와 정보를 메모리에 저장하는 <code>caching_sha2_password plugin</code> 이 기본 플러그인이 되었다.</p>
<h4 id="해결방법1-모듈-수정">해결방법1. 모듈 수정</h4>
<blockquote>
<p><strong>mysql2</strong>로 커넥션 풀을 생성해서 연결해주었기 때문에 세션 모듈에서 문제가 있는 것 같다는 의심을 하게 되었고, 세션 관련 모듈의 파일들을 살펴보았다. </p>
<p>그 과정에서 나는 설치한 적 없는 <strong>mysql</strong>모듈을 발견했다. 이전 <strong>typeORM</strong>연동에서도 오류를 발생시킨 모듈이었기 때문에 분명 삭제하고 <strong>mysql2</strong>를 새로 설치했었다. </p>
<p><strong>mysql</strong>과 관련있는 <strong>express-mysql-session</strong>모듈을 확인했더니 index파일의 첫줄에서 <strong>mysql</strong>모듈을 <code>require</code>하고있었고 이 모듈의 <strong>package.json</strong>에도 <strong>dependencies</strong>옵션에 <strong>mysql</strong>이 있었다. </p>
<p>먼저 <strong>mysql</strong>모듈을 지우고 세션모듈의 <strong>mysql</strong>대신 <strong>mysql2</strong>를 <code>require</code>해주는 것으로 바꿔주었다. 이 방식에 오류가 없을 것이라고 생각한 이유는 <strong>typeorm</strong>모듈에 <strong>mysql</strong>에서 <strong>mysql2</strong>로 교체가 가능하다고 공식문서에서 참고했었기 때문이다.</p>
<p>재실행 후 <strong>정상적으로 쿼리가 생성되는 것을 확인할 수 있었다.</strong></p>
</blockquote>
<h4 id="해결방법2-mysql-수정">해결방법2. MySQL 수정</h4>
<blockquote>
<pre><code class="language-sql">select host, user, plugin, authentication_string from mysql.user;</code></pre>
</blockquote>
<pre><code>&gt;
&gt;mysql에서 위 쿼리를 날려주면 `user`가 `root`인 필드의 `plugin`이 `caching_sha2_password`인 것을 확인할 수 있다.
&gt;
&gt;이때 다음 명령어를 입력해줘서 플러그인을 `mysql_native_password` 로 바꿔준다.
&gt;
&gt;```sql
ALTER USER &#39;root&#39;@&#39;%&#39; IDENTIFIED WITH mysql_native_password BY &#39;1234&#39;;</code></pre><blockquote>
<p>여기까지 완료되면 정상적으로 2번오류는 더 이상 발생하지 않고 정상 작동한다.</p>
</blockquote>
<hr>
<h3 id="세션-저장하기">세션 저장하기</h3>
<p>auth.controller.ts에서 로그인이 성공했을 시에 세션의 속성에 값을 할당한다.</p>
<pre><code class="language-tsx">req.session.is_logined = true;
req.session.userId = user.id;
req.session.dispayName = user.username;</code></pre>
<p>처음 설정할 때 saveUninitialized를 false로 선언했기 때문에 이렇게 속성값을 할당하면 세션 데이터가 req 속성에 추가된다. 추가된 값은 다음과 같다.</p>
<pre><code class="language-tsx">[1] Session {
[1]   cookie: {
[1]     path: &#39;/&#39;,
[1]     _expires: 2022-01-08T15:49:22.327Z,
[1]     originalMaxAge: 3600000,
[1]     httpOnly: true,
[1]     secure: false
[1]   },
[1]   is_logined: true,
[1]   userId: 1,
[1]   dispayName: &#39;james&#39;
[1] }</code></pre>
<p>그 다음에는 save를 통해 저장을 해준다. save를 하지않고 응답을 보내도 저장이 되지만 이때 정확하게 저장이 된 이후에 응답을 보내기 위해선 save의 콜백함수로 응답을 반환해주는 것이 좋다.</p>
<pre><code class="language-tsx">req.session.save(() =&gt; {
        return res.sendStatus(202);
});</code></pre>
<hr>
<h3 id="세션-삭제하기">세션 삭제하기</h3>
<p>로그아웃 요청을 보내면 클라이언트의 브라우저 쿠키에서 세션데이터를 갱신해줘야 한다. session의 destroy 메서드를 사용하면 세션저장소에 있는 데이터와 req.session을 모두 삭제해준다. 따라서 console에 req.session을 찍어보면 undefined가 출력될 것이다. 브라우저의 쿠키는 따로 삭제해주지 않아도 활용할 수 없는 id가 된다. </p>
<p>주의할 점은 destroy의 콜백함수로 응답을 반환해야한다. 그렇지 않으면 삭제가 되지 않은 상태를 보내게 될 수도 있기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS express] eager loading과 lazy loading ]]></title>
            <link>https://velog.io/@c-on/TS-express-eager-loading%EA%B3%BC-lazy-loading</link>
            <guid>https://velog.io/@c-on/TS-express-eager-loading%EA%B3%BC-lazy-loading</guid>
            <pubDate>Wed, 05 Jan 2022 08:43:03 GMT</pubDate>
            <description><![CDATA[<h1 id="n1문제">n+1문제</h1>
<p>연관관계에서 자식 엔티티를 조회할 경우 부모 엔티티를 알아내기 위해 n번의 쿼리를 추가로 날리게 되는 문제를 말한다.</p>
<ul>
<li>즉시로딩으로 데이터를 가져오는 경우</li>
<li>지연로딩으로 데이터를 가져온 이후에 하위엔티티를 다시 조회하는 경우</li>
</ul>
<p>위 두가지 경우에서 주로 발생하는데, 성능저하를 일으킬 수 있다.</p>
<p>성능저하를 해결하기 위해선 eager loading을 사용해줌으로써 해결할 수 있다. eager loading은 연관데이터를 JOIN해서 불러오는 것이다.</p>
<p>애초에 sql을 공부하면서 join을 알고 있었고 그래서인지 연관테이블을 함께 조회하는 경우 반복문을 만들어서 한번씩 조회하는 것보다 eager loading을 사용하는 것이 당연하다고 생각했다.</p>
<h1 id="eager-loading의-문제점">eager loading의 문제점</h1>
<p>문제점에 대해 생각하게 된 것은 게시물의 태그를 불러오면서이다.</p>
<p>불러온 게시물이 20개 일 때 각 게시물이 태그를 최소 10개씩 가지고 있다고 가정하면 200개의 태그 데이터를 한번에 불러와야 하는 것이다.</p>
<p>하지만 애플리케이션의 태그 조회 기능은 <code>태그</code> 버튼을 클릭했을 때에 조회를 진행하는 방식이었다.</p>
<p>따라서 태그와 게시물데이터가 JOIN되어 전달될 필요가 없다고 생각했고, 이처럼 당장의 조회가 필요하지 않는 연관테이블 데이터는 eager loading을 하지않는 것이 더 효율적인 방법이 맞는지에 대한 확인이 필요했다.</p>
<p> JOIN을 하지 않는, 즉 eager loading의 반대 조회방식을 lazy loading이라고 하며 typeORM을 사용할 때는 스키마 선언단계에서 <code>eager:true</code> 를 작성해주지 않는 방식이다.</p>
<p>lazy loading을 사용할 때의 이점은</p>
<ul>
<li>초기 로딩시간 절약</li>
<li>순간적인 자원 소비를 줄임</li>
<li>사용하지 않는 데이터를 결과 객체에 포함시키지 않아서 cpu타임 절약</li>
</ul>
<p>sns에서 댓글 더보기 기능같은 곳에 사용된다고 한다.</p>
<blockquote>
<p>태그 조회 기능역시 초기에 게시물을 조회할 때에는 필요치 않은 데이터이므로 위의 <strong>lazy loading 이점을 갖게 됨을 확인</strong>했고, 프로그램에 적용하기로 결정하게 되었다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[typeORM 기본 사용법]]></title>
            <link>https://velog.io/@c-on/typeORM-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@c-on/typeORM-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 04 Jan 2022 16:27:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/6d72bc05-d19d-4e59-a52f-4a1fd3cd6967/image.png" alt=""></p>
<br>

<h2 id="🔎-데이터베이스-테이블-만들기">🔎 데이터베이스 테이블 만들기</h2>
<p>먼저 엔티티를 정의해준다.</p>
<p><strong>엔티티</strong>는 <code>@Entitiy</code>로 데코레이트된 모델을 말한다.</p>
<p>컬럼을 추가하려면 <code>@Column()</code>, <code>@PrimaryColumn()</code>, <code>@PrimaryGeneratedColumn()</code>등을 사용할 수 있으며 컬럼의 타입은 괄호안에 <code>length:100</code>, <code>text</code>, <code>double</code>등을 전달해서 지정해준다.</p>
<pre><code class="language-jsx">import { Entity, Column, PrimaryGeneratedColumn } from &quot;typeorm&quot;;

@Entity()
export class Photo {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        length: 100
    })
    name: string;

    @Column(&quot;text&quot;)
    description: string;

    @Column()
    filename: string;

    @Column(&quot;double&quot;)
    views: number;

    @CreateDateColumn()
    createdAt: Date
}</code></pre>
<br>

<h2 id="🔎-연결">🔎 연결</h2>
<p>엔티티가 완성되면 메인 파일에서 연결을 진행해준다.</p>
<p><code>createConnection()</code> api를 사용해서 연결한다. 
메소드의 인자로 데이터베이스 정보를 적어줄 수도 있고, 작성하지 않으면 자동으로 ormconfig.json의 내용을 참조하게 되므로 ormconfig.json에 설정내용을 작성한다..</p>
<br>

<h2 id="🔎-insert-data">🔎 Insert data</h2>
<p><img src="https://images.velog.io/images/c-on/post/1e44090f-59c0-4ba8-aae9-7350f56f3517/image.png" alt="">
<code>getRepository()</code>의 타입정의이다.
typeorm모듈의 <code>getRepository()</code>를 사용하면 다음과 같이 Respositry타입의 객체를 반환한다.</p>
<hr>
<p><img src="https://images.velog.io/images/c-on/post/ca1c194d-fd9d-4dd6-8883-87642d6ad82c/image.png" alt=""></p>
<p>Respositry타입의 객체는 <code>save</code>API를 사용해서 엔티티를 전달해주면 실제 DB의 테이블에 저장을 한다. 저장이 완료되면 프로미스를 반환한다. 
위 타입 정의를 보면 엔티티는 배열로 여러개를 한번에 전달할 수도 있다.</p>
<hr>
<pre><code class="language-tsx">export class UserService implements authEntityService {
  constructor(private UserRepository: authEntityConstructor) {}

    async createUser(data: userData): Promise&lt;authEntity&gt; {
        const repository = getRepository(this.UserRepository);
        const model = new this.UserRepository();
        const keys = Object.keys(data);
        keys.forEach((key) =&gt; {
          model[key] = data[key];
        });
        return repository.save(model);
      }
}</code></pre>
<p>엔티티를 구현하고 값을 할당한 뒤 저장소에 전달을 해줌으로써 저장완료한다.</p>
<br>

<h2 id="🔎-기초엔티티-만들기">🔎 기초엔티티 만들기</h2>
<p>추상클래스로 기본적으로 가지는 컬럼을 설정해주고 필요한 엔티티에서 상속해서 사용한다.</p>
<pre><code class="language-tsx">import { CreateDateColumn, PrimaryGeneratedColumn } from &quot;typeorm&quot;;
import { ValidationEntity } from &quot;../validation/validation&quot;;

export abstract class BasicEntity extends ValidationEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @CreateDateColumn()
  createdAt: Date;
}</code></pre>
<br>

<h2 id="🔎-일대다-연결">🔎 일대다 연결</h2>
<p>예를 들어서 SlimePost엔티티와 SlimeOption엔티티가 일대다 연결을 맺고 있다면 각 엔티티의 끝단에 다음코드를 작성하면 된다.</p>
<pre><code class="language-tsx">@OneToMany(() =&gt; SlimeOption, (slime_option) =&gt; slime_option.slime_post)
  slime_options: SlimeOption[];</code></pre>
<pre><code class="language-tsx">@ManyToOne(() =&gt; SlimePost, (slime_post) =&gt; slime_post.slime_options, { onDelete: &quot;CASCADE&quot;, nullable: false })
  slime_post: SlimePost;</code></pre>
<blockquote>
<p>이때 onDelete 설정을 해주지 않으면 post를 지우고 싶을 때 종속된 postOption데이터가 존재할 경우 <code>Cannot delete or update a parent row</code> 오류가 발생한다.</p>
</blockquote>
<br>


<h2 id="🔎-일대다-연결-저장">🔎 일대다 연결 저장</h2>
<p>다대일 선언 부분에 joincolumn 데코레이션을 해주고 속성으로 fk를 넣어준다.</p>
<p>그리고 아래에 fk 컬럼을 똑같이 생성해주면 된다.</p>
<p>이때 마이그레이션을 하는 경우라면 <code>&#39;Invalid use of NULL value’</code> 라는 sql에러 메시지를 받을 수 있다. 이유는 fk는 not null인데 현재 데이터베이스에 저장된 데이터들을 fk를 null로 가지고 있기 때문이다. 테이블 전체를 지워주거나 하나하나 fk를 등록해주면 된다.</p>
<p>또 fk없이 save하는 경우 <code>[fk] doesn’t have default value</code> 라는 에러 메시지를 받을 수 있다.</p>
<pre><code class="language-tsx">@ManyToOne(() =&gt; User, (user) =&gt; user.slime_posts)
@JoinColumn({ name: &quot;userId&quot; })
user: User;

@Column()
userId: number;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TSexpress] typeORM 설정하기]]></title>
            <link>https://velog.io/@c-on/TSexpress-typeORM-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c-on/TSexpress-typeORM-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 03 Jan 2022 12:21:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/c-on/post/2d934aeb-1464-41b1-ba23-c1216aa9b641/image.png" alt=""></p>
<h1 id="typeorm-설치-및-설정">typeORM 설치 및 설정</h1>
<ul>
<li>npm을 사용해서 typeorm, reflect-metadata, mysql2 를 설치한다.</li>
</ul>
<blockquote>
<p><strong><em>mysql과 mysql2의 차이점</em></strong>
mysql은 MySQL 8.0 버전의 프로토콜에 맞는 인증요청을 보내지 못한다. mysql2는 가능하다.</p>
</blockquote>
<ul>
<li>cli에서 typeorm 명령어를 사용할 수 있도록 <code>npm install typeorm -g</code> 를 입력한다.</li>
<li>이미 프로젝트가 설정되어 있는 경우 <code>typeorm init --database mysql2</code>  을 입력하여 ormconfig.json을 생성한다.</li>
</ul>
<hr>
<h3 id="tsconfigjson">tsconfig.json</h3>
<pre><code class="language-jsx">{
  &quot;compilerOptions&quot;: {
    &quot;lib&quot;: [&quot;es5&quot;, &quot;es6&quot;, &quot;ES2018&quot;],
    &quot;target&quot;: &quot;es5&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;outDir&quot;: &quot;dist&quot;,
    &quot;rootDir&quot;: &quot;src&quot;,
    &quot;emitDecoratorMetadata&quot;: true,
    &quot;experimentalDecorators&quot;: true,
    &quot;sourceMap&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;noEmitOnError&quot;: true,
    &quot;noUnusedLocals&quot;: true
  }
}</code></pre>
<blockquote>
<p><strong>주의할 점</strong></p>
</blockquote>
<ul>
<li>tsconfig.json설정이 모두 덮어씌워진다. 그래서 이전에 설정해주는 것은 무의미하다.</li>
<li><code>esModuleInterop</code>을 <code>true</code>로 설정해줘야 ES모듈을 사용할 수 있다.</li>
<li>커맨드창에서는 오류가 생기면 해결법을 추천해주는데 이걸 쓰는 동안 발생한 3개의 오류는 몽고db 문제였다. <ul>
<li>lib에 es2018이상을 쓰라는 reconmmend를 받았다. recommend에 따라 추가를 해서 문제를 해결했다.<blockquote>
</blockquote>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong><em>옵션</em></strong></p>
</blockquote>
<ul>
<li><code>module</code>은 모듈방식을 정하는 것인데 프론트엔드에서 사용할 때는 ES2015를 사용할 수 있겠지만 백엔드에서는 node에 commonJS모듈이 많아서 commonJS로 사용해준다.</li>
<li>typeorm에 .d.ts 오류가 발생해서 skipLibCheck를 true로 설정함으로써 lib 체킹을 무시했다. 스택오버플로우에서 타입스크립트를 3.6까지 다운그레이드 시키는 방법도 찾았는데 이 방법이 더 간단해서 사용했다.<blockquote>
</blockquote>
</li>
</ul>
<hr>
<h3 id="ormconfigjson">ormconfig.json</h3>
<p>typeorm을 init하면 ormconfig.json이 생성된다. 
typrorm을 연결하기 위한 옵션들을 기입해준다.</p>
<blockquote>
<p>보안을 위해서 .gitignore에 ormconfig.json을 작성해준다.</p>
</blockquote>
<pre><code class="language-jsx">{
  &quot;type&quot;: &quot;mysql&quot;,
  &quot;host&quot;: &quot;localhost&quot;,
  &quot;port&quot;: 3306,
  &quot;username&quot;: &quot;root&quot;,
  &quot;password&quot;: &quot;1234&quot;,
  &quot;database&quot;: &quot;slimeworld&quot;,
  &quot;synchronize&quot;: true,
  &quot;logging&quot;: true,
  &quot;entities&quot;: [&quot;dist/data/**/*.{js,ts}&quot;],
  &quot;migrations&quot;: [&quot;dist/migration/**/*.{js,ts}&quot;],
  &quot;subscribers&quot;: [&quot;dist/subscriber/**/*.{js,ts}&quot;]
}</code></pre>
<blockquote>
<p><strong>주의할 점</strong></p>
</blockquote>
<ol>
<li>mysql 모듈은 오류가 발생하니 mysql2모듈을 다운받아야 하는데 ormconfig.json에서 <code>type</code>에 <code>mysql2</code>를 적으면 안된다! <code>type</code>은 <code>mysql</code>만 기입할 수 있다.</li>
<li>공식문서 예제에서는 <code>entities</code>가 <code>src</code>로 되어 있는데 ts-node로 바로 실행하면 오류가 없지만 tsc로 변환 후 노드를 실행하는 경우에는 오류가 발생한다. outDir로 정의한 폴더를 기입해준다. 내 경우에는 <code>dist</code>로 바꿔주었다.<blockquote>
</blockquote>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>