<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>chs_0303.log</title>
        <link>https://velog.io/</link>
        <description>무엇이든 끝까지 보람차게</description>
        <lastBuildDate>Wed, 07 Jun 2023 01:10:50 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>chs_0303.log</title>
            <url>https://images.velog.io/images/chs_0303/profile/b937b11c-0752-4951-b0a4-d631332f1ce8/KakaoTalk_20210815_204605506.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. chs_0303.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/chs_0303" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[블로그 이전]]></title>
            <link>https://velog.io/@chs_0303/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%A0%84</link>
            <guid>https://velog.io/@chs_0303/%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9D%B4%EC%A0%84</guid>
            <pubDate>Wed, 07 Jun 2023 01:10:50 GMT</pubDate>
            <description><![CDATA[<p>블로그 이전 합니다!
<a href="https://chsu.tistory.com/">https://chsu.tistory.com/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ubuntu docker setting]]></title>
            <link>https://velog.io/@chs_0303/ubuntu-docker-setting</link>
            <guid>https://velog.io/@chs_0303/ubuntu-docker-setting</guid>
            <pubDate>Tue, 14 Mar 2023 08:41:22 GMT</pubDate>
            <description><![CDATA[<p>우분투 서버에 docker 설치부터 배포과정까지 간단하게 정리해본다.</p>
<ol>
<li>api 패키지 설치</li>
</ol>
<pre><code class="language-bash">$ sudo apt-get update

$ sudo apt-get install ca-certificates

$ sudo apt-get install curl

$ sudo apt-get install gnupg

$ sudo apt-get install lsb-release</code></pre>
<p>1-2. GPG 키 추가 (docker 프로그램 자체를 인증하기 위한 키)</p>
<pre><code class="language-bash">$ sudo mkdir -p /etc/apt/keyrings

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

$ echo \
  &quot;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable&quot; | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</code></pre>
<ol>
<li>docker 설치</li>
</ol>
<pre><code class="language-bash">$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io</code></pre>
<p>위 코드들 중 입력해야 될 부분은 없다.</p>
<h3 id="docker-hub-사용법">docker hub 사용법</h3>
<p>docker hub에 회원가입 후 Repository를 생성해 준다.</p>
<p>그 후 docker hub에서 Account Settings &gt; Security &gt; Access Tokens를 통해 토큰을 새로 발급한다. (github 토큰과 마찬가지로 다시 볼 수 없으니 꼭 저장해둔다.)</p>
<pre><code class="language-bash">docker login

or

docker login -u &lt;docker ID&gt;</code></pre>
<p>위 명령어를 입력 후 pw부분에 발급 받은 토큰을 넣어주면 된다.</p>
<p>명령어를 통해 local에 인증을 하신 후 </p>
<pre><code class="language-bash">docker push &lt;Repository name&gt;</code></pre>
<p>을 통해 docker hub에 업로드를 진행하면 된다.</p>
<p>반대로 서버에 이를 받기 위해선 동일하게 설치 &gt; 로그인을 진행한 후</p>
<pre><code class="language-bash">docker pull &lt;Repository name&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS VPC]]></title>
            <link>https://velog.io/@chs_0303/AWS-VPC</link>
            <guid>https://velog.io/@chs_0303/AWS-VPC</guid>
            <pubDate>Thu, 09 Feb 2023 02:38:56 GMT</pubDate>
            <description><![CDATA[<p>회사에서 인프라를 설계하면서 VPC를 아무렇지 않게 생성하고 했지만 각 설정들에 대해 가볍게만 알고 있는 느낌이 들어 한번 다른 사람들도 이해할 수 있게 정리를 해보았다.</p>
<h3 id="vpc란">VPC란?</h3>
<p>Amazon VPC는 Amazon에서 제공하는 Private한 네크워크 망</p>
<ul>
<li><strong>Virtual Private Cloud(VPC)</strong> — 사용자의 AWS 계정 전용 가상 네트워크입니다.</li>
<li><strong>서브넷</strong> — VPC의 IP 주소 범위입니다.</li>
<li><strong>라우팅 테이블</strong> — 네트워크 트래픽을 전달할 위치를 결정하는 데 사용되는 라우팅이라는 규칙 집합입니다.</li>
<li><strong>인터넷 게이트웨이</strong> — VPC의 리소스와 인터넷 간의 통신을 활성화하기 위해 VPC에 연결하는 게이트웨이입니다.</li>
<li><strong>NAT 게이트웨이</strong> — 네트워크 주소 변환을 통해 프라이빗 서브넷에서 인터넷 또는 기타 AWS 서비스에 연결하는 게이트웨이입니다.</li>
<li><strong>씨큐리티 그룹</strong> — 보안 그룹은 인스턴스에 대한 인바운드 및 아웃바운드 트래픽을 제어하는 가상 방화벽 역할을 하는 규칙 집합입니다..</li>
<li><strong>VPC 엔드포인트</strong> — 인터넷 게이트웨이, NAT 디바이스, VPN 연결 또는 AWS Direct Connect 연결을 필요로 하지 않고 PrivateLink 구동 지원 AWS 서비스 및 VPC 엔드포인트 서비스에 VPC를 비공개로 연결할 수 있습니다. VPC의 인스턴스는 서비스의 리소스와 통신하는 데 퍼블릭 IP 주소를 필요로 하지 않습니다. VPC와 기타 서비스 간의 트래픽은 Amazon 네트워크를 벗어나지 않습니다.</li>
</ul>
<p><a href="https://medium.com/harrythegreat/aws-%EA%B0%80%EC%9E%A5%EC%89%BD%EA%B2%8C-vpc-%EA%B0%9C%EB%85%90%EC%9E%A1%EA%B8%B0-71eef95a7098">[AWS] 가장쉽게 VPC 개념잡기</a></p>
<p><a href="https://rcsecurity.tistory.com/4"><a href="2/5">IP</a> 네트워크 대역과 사설IP,공인IP</a></p>
<h3 id="vpc-생성해보기">VPC 생성해보기</h3>
<p>위의 사설망에서 보듯이 우리가 aws측에서 받아온 공인 ip 내부의 사설망 구조를 잡아주는 것이라고 생각하면 된다. </p>
<p>192.168.x.x, 172.16–31.x.x, 10.x.x.x IP 이 세가지 대역들은 사설망 내부의 ip이다. </p>
<ul>
<li>사설 네트워크에 활용하는 IP 대역 범위는 10.0.0.0 ~. 10.255.255.255, 172.16.0.0 ~ 172.31.255.255, 192.168.0.0 ~ 192.168.255.255 이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/d285bafa-82c6-4f47-b7b0-10bd3c2215de/image.png" alt=""></p>
<p>2023년 1월 기준 가장 최신의 vpc 생성창의 모습이다. 네트워크의 모든 구조를 안다면 vpc만 생성하기를 통해 기초부터 만들기가 가능하지만 모든 편의성과 간소화를 위해 vpc 등을 선택하여 만들어 준다.</p>
<ul>
<li><p>IPv4 CIDR 블록</p>
<ul>
<li><p>이 부분이 위에서 말한 사설 네트워크이다. 즉, 192.168.x.x, 172.16–31.x.x, 10.x.x.x 이 셋중 하나를 입력해주면 되는데 뒤의 / 는 무엇일까?</p>
</li>
<li><p>아래 링크에서 간단하게 이해하고 서브넷 마스크 치트 시트 이 부분만 인지하면 된다. (간단하게 이해하면 숫자가 클수록 해당 네트워크 안에서 사용할 수 있는 ip 주소의 갯수가 줄어든다고 생각하면 된다.)</p>
<p><a href="https://nordvpn.com/ko/blog/what-is-subnet-mask/">서브넷 마스크란 무엇인가요?</a></p>
</li>
</ul>
</li>
<li><p>가용 영역(AZ)</p>
<ul>
<li>aws 온프레미스 실제 데이터센터 내에 물리적으로 떨어져 있다는 것을 의미 a,b,c,d로 나누어져 있다.</li>
<li>이전에 카카오 화재 사건때를 봤을 때 a zone에서 사고가 생겨도 b,c,d 존은 서버를 유지 할 수 있게 설계 되어 있다.</li>
<li>이런 것을 고가용성 설계(high availability) 설계라고 한다.</li>
</ul>
</li>
<li><p>NAT 게이트웨이</p>
<ul>
<li><p>서브넷은 public 서브넷과 private 서브넷으로 나누어져 있다. public의 경우에는 인터넷 게이트웨이(igw)를 통해 외부 인터넷과 통신이 가능하게 만들어 놨지만 private은 외부 인터넷의 접근을 차단하고 내부 망에서만 사용이 가능하게 만든 서브넷이다. (ex. DB)</p>
</li>
<li><p>이러한 private 서브넷을 외부 인터넷을 통해 업데이트를 진행하는 등 접근이 필요한 경우를 위해 사용되어지는 것이 NAT 게이트웨이이다.</p>
<ul>
<li><p>예시 그림</p>
<p>  <img src="https://velog.velcdn.com/images/chs_0303/post/b2f8560c-8059-4546-9818-8eec010e69d2/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>- 이전에 private subnet의 DB에 접속하기 위해 public의 ec2에 ssh 접속을 해서 접근했던 방식이 있었는데 이와 비슷하다고 생각하면 된다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Terraform 작동원리와 CLI]]></title>
            <link>https://velog.io/@chs_0303/Terraform-%EC%9E%91%EB%8F%99%EC%9B%90%EB%A6%AC%EC%99%80-CLI</link>
            <guid>https://velog.io/@chs_0303/Terraform-%EC%9E%91%EB%8F%99%EC%9B%90%EB%A6%AC%EC%99%80-CLI</guid>
            <pubDate>Mon, 06 Feb 2023 08:43:03 GMT</pubDate>
            <description><![CDATA[<h3 id="terraform-init">Terraform init</h3>
<pre><code class="language-go">// provider.tf
provider &quot;aws&quot; {
  region = &quot;ap-northeast-2&quot;
}</code></pre>
<p>위와 같이 파일을 생성해주고 terraform init 명령어를 입력하면</p>
<p>그러면 initialize가 진행이 되고 프로바이더를 다운로드 받는다.</p>
<p>local에는 아래와 같은 폴더 두개가 생긴다.</p>
<p>.terraform , .terraform.lock.hcl</p>
<p>.terraform은 프로바이더에 명시한 cloud의 api 라이브러리들을 가져와 저장한다.</p>
<p>.terraform.lock.hcl은 잠금 파일이며 경쟁 상태에서 생길 수 있는 문제들을 피할 수 있게 해줍니다.</p>
<h3 id="terraform-plan"><strong>Terraform plan</strong></h3>
<pre><code class="language-go">// s3.tf
resource &quot;aws_s3_bucket&quot; &quot;test&quot; {
  bucket = &quot;terraform-inflearn-chs&quot;
}</code></pre>
<p>위와 같이 resource를 작성해주고 terraform plan을 입력해주면</p>
<pre><code class="language-bash"># aws_s3_bucket.test will be created
  + resource &quot;aws_s3_bucket&quot; &quot;test&quot; {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = &quot;terraform-inflearn-chs&quot;
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + tags_all                    = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule {
          + allowed_headers = (known after apply)
          + allowed_methods = (known after apply)
          + allowed_origins = (known after apply)
          + expose_headers  = (known after apply)
          + max_age_seconds = (known after apply)
        }
...</code></pre>
<p>이러한 형태가 return 된다.</p>
<p>위의 결과를 보면 bucket의 이름의 경우 필수 값이기 때문에 반드시 입력이 되었고 나머지는 default값 혹은 정의를 해도 되고 안해도 되는 그러한 부분들이다.</p>
<h3 id="terraform-apply"><strong>Terraform apply</strong></h3>
<pre><code class="language-bash">...

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &#39;yes&#39; will be accepted to approve.

  Enter a value:</code></pre>
<p>위와 같이 출력이 되는데 실제로 yes 를 입력 해주어야한다.</p>
<p>실제 인프라에 적용이 되는 부분이기 때문에 굉장히 예민한 작업이 될 수 있기에 경고문 역활을 한다.</p>
<p>이후 ls 명령어를 치면 terraform.tfstate 파일이 추가된 것을 볼 수 있다.</p>
<p>→ backend를 따로 지정해주지 않았기에 local이 backend가 된 것이다.</p>
<h3 id="terraform-import">Terraform import</h3>
<p>기존에 작성했던 s3 bucket들의 정보도 가져올 수 있다.</p>
<p>작동원리를 잘 이해해야된다.</p>
<ol>
<li><p>기존 작성했던 resource를 삭제하고 import를 진행하면 code가 없기 때문에 에러가 난다.</p>
</li>
<li><p>그 후 다시 똑같이 resource를 작성하고 plan을 하면 plan은 성공으로 나온다.</p>
<p> → <strong>여기서 알 수 있는 것은 plan은 실제 리소스가 있는지 없는지 검사하지 않는다는 점이다.</strong></p>
<pre><code class="language-bash"> Do you want to perform these actions?
   Terraform will perform the actions described above.
   Only &#39;yes&#39; will be accepted to approve.

   Enter a value: yes

 aws_s3_bucket.test: Creating...
 ╷
 │ Error: creating Amazon S3 (Simple Storage) Bucket (terraform-inflearn-chs): BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.</code></pre>
</li>
<li><p>이럴 때 사용하는 것이 import이다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Terraform과 DevOps 개념 및 기초]]></title>
            <link>https://velog.io/@chs_0303/Terraform%EA%B3%BC-DevOps-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@chs_0303/Terraform%EA%B3%BC-DevOps-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 01 Feb 2023 03:04:57 GMT</pubDate>
            <description><![CDATA[<h2 id="devops의-기본적-이해">DevOps의 기본적 이해</h2>
<h3 id="5가지-철학">5가지 철학</h3>
<p>문화(Culture)</p>
<ul>
<li>올바른 기업문화를 가져나가는 것</li>
</ul>
<p>자동화(Automation)</p>
<ul>
<li>업무를 자동화</li>
<li>변경에 유연하고 탄력적인 서비스를 구축</li>
</ul>
<p>측정(Measurement)</p>
<ul>
<li>변경 사항을 항상 측정, 예측</li>
</ul>
<p>공유(Sharing)</p>
<ul>
<li>언제든 접근 가능한 투명한 데이터</li>
<li>문제를 함께 해결 (open 되어있어야함)</li>
</ul>
<p>축적(File up &amp; Pile up)</p>
<p>데브옵스는 어떤 요구사항을 효율적으로 만족시키기 위하여, 일을 자동화하며 변경사항 지표들을 측정하고, 공유하고, 이 모든 결과물들을 지속적으로 축적해 나아가는 문화를 만들어가는 철학, 방법론, 기술</p>
<h2 id="devops-엔지니어">DevOps 엔지니어</h2>
<p>올바른 DevOps 문화를 위해 서비스 혹은 S/W LifeCycle에서 반복적인 일들을 자동화하고, 기술적 문제 혹은 팀의 차이를 기술적으로 예방하고, 해소시키는 사람</p>
<ul>
<li>DevOps 엔지니어의 역량에 따라 차이가 큰 편임</li>
<li>파이프라인 구성, 빌드 자동화</li>
</ul>
<h2 id="infrastructure-as-code-코드로써의-인프라">Infrastructure as Code (코드로써의 인프라)</h2>
<p>Infrastructure as Code, 즉 코드로써의 인프라는 인프라를 이루는 서버, 미들웨어 그리고 서비스 등 인프라 구성요소들을 코드를 통해 구축하는 것.</p>
<p>IaC는 코드로써의 장점, 즉 작성용이성, 재사용성, 유지보수 등의 장점을 가진다.</p>
<h2 id="terraform">Terraform</h2>
<p>.tf 의 파일 형식을 가짐 (확장자) </p>
<h3 id="terraform구성요소">Terraform구성요소</h3>
<ul>
<li>provider - 테라폼으로 생성할 인프라의 종류를 의미</li>
<li>resource - 테라폼으로 실제로 생성할 인프라 자원을 의미</li>
<li>state - 테라폼을 통해 생성한 자원의 상태를 의미</li>
<li>output - 테라폼으로 만든 자원을 변수 형태로 state에 저장하는 것을 의미</li>
<li>module - 공통적으로 활용할 수 있는 코드를 문자 그대로 모듈 형태로 정의하는 것을 의미</li>
<li>remote - 다른 경로의 state를 참조하는 것을 말한다. output변수를  불러올 때 주로 사용</li>
</ul>
<h3 id="terraform-provider">Terraform provider</h3>
<pre><code class="language-go">provider &quot;aws&quot; {
  region = &quot;ap-northeast-2&quot;
  version = &quot;~&gt; 3.0&quot;

}</code></pre>
<p>provider 안에서 다양한 Arguments를 가진다.</p>
<p>AWS resource를 다루기 위한 파일들을 다운로드 하는 역활을 합니다. (aws-sdk)</p>
<h3 id="terraform-resource">Terraform resource</h3>
<pre><code class="language-go"># main.tf, vpc.tf 등 원하는 형태로 파일이름을 사용
# Create a VPC
resource &quot;aws_vpc(리소스 명)&quot; &quot;실제 이름&quot; {
  cidr_block = &quot;10.0.0.0/16&quot;
  ...
}</code></pre>
<p>테라폼으로 VPC를 생성하는 코드 일부</p>
<h3 id="terraform-state">Terraform state</h3>
<pre><code class="language-go"># terrafrom.tfstate 라는 파일명을 가진다.
{
  &quot;version&quot;: 4,
  &quot;terrafrom_version&quot;: &quot;0.12.24&quot;,
  &quot;serial&quot;: 3,
  &quot;lineage&quot;: &quot;3c77XXXX-2de4-7736-0447-038974a2c127&quot;,
  &quot;outputs&quot;: {},
  &quot;resources&quot;: [
    {...},
    {...},
  ]
}</code></pre>
<p>테라폼으로 작성된 코드를 실행하게 되면 생성되는 파일(package.json 같은?)</p>
<p>현재 인프라의 실제 상태를 의미하는 것이 아님</p>
<p>그러므로 가장 중요한 것은 현재 인프라 상태와 state를 똑같이 유지하는 것이다.</p>
<p>현업에서는 원격 저장소인 ‘backend’에 저장하여 사용</p>
<h3 id="terraform-output">Terraform output</h3>
<pre><code class="language-go">resource &quot;aws_vpc(리소스 명)&quot; &quot;실제 이름&quot; {
  cidr_block = &quot;10.0.0.0/16&quot;
  ...
}

output &quot;vpc_id&quot; {
  value = aws_vpc.default.id
}

output &quot;cidr_block&quot; {
  value = aws_vpc.default.cidr_block
}</code></pre>
<p>resource를 통해 무언가를 생성하였을 때 나오는 id, cidr 값 등을 참조해서 <strong>변수</strong>(ex. vpc_id, cidr_block)를 통해 <strong>state 파일에 저장</strong>을 하는 것</p>
<p>리모트를 통해 사용가능</p>
<h3 id="terraform-module">Terraform module</h3>
<pre><code class="language-go">module &quot;vpc&quot; {
  source = &quot;../_module/vpc&quot;

  cidr_block = &quot;10.0.0.0/16&quot;
}</code></pre>
<p>module은 한 번 만들어진 테라폼 코드로 같은 형태를 반복적으로 만들어 낼 때 주로 사용</p>
<p><strong>재사용에 대한 강점</strong> ( module의 사용을 보고 테라폼을 잘 쓰냐 못 쓰냐를 판단할 수 있을 정도)</p>
<h3 id="terraform-remote">Terraform remote</h3>
<pre><code class="language-go"># remote는 원격 참조 개념으로 이해
data &quot;terraform_remote_state&quot; &quot;vpc(고유값)&quot; {
  backend = &quot;remote&quot;

  config = {
    bucket    = &quot;terraform-s3-bucket&quot;
    region    = &quot;ap-northeast-2&quot;
    key       = &quot;terraform/vpc/terraform.tfstaqte&quot;
  }
}</code></pre>
<p>state 파일에서 output으로 저장된 변수를 가져오는 것</p>
<h2 id="terraform-기본-명령어">Terraform 기본 명령어</h2>
<p>Process</p>
<p>Init - 작성한 코드에서 init 명령어를 입력 - 테라폼의 다른 명령어들을 위한 설정 진행</p>
<p>P<strong>lan - 실제로 작성한 테라폼 코드가 어떻게 만들어질지 예측 - 가장 많이 쓰이는 명령어로 plan에서 문제가 없어야 apply도 문제가 없을 확률이 높다. ⇒ 습관화</strong></p>
<p>Apply - 실제로 작성한 코드로 명령어를 생성하는 명령어 - 실제 인프라에 영향을 끼치는 명령어로 주의가 필요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS-SDK S3 getObject(with lambda)]]></title>
            <link>https://velog.io/@chs_0303/AWS-SDK-S3-getObjectwith-lambda</link>
            <guid>https://velog.io/@chs_0303/AWS-SDK-S3-getObjectwith-lambda</guid>
            <pubDate>Mon, 09 Jan 2023 03:13:27 GMT</pubDate>
            <description><![CDATA[<p>이전에 lambda에 대한 글을 쓸 때 썼던 간단한 SDK이지만 수정하면서 겪은 일도 있고 나중에 잊을 수도 있다는 생각에 다시 정리해본다.</p>
<h3 id="코드">코드</h3>
<pre><code>// import AWS from &#39;aws-sdk&#39;;
const AWS = require(&#39;aws-sdk&#39;);

const s3 = new AWS.S3();

exports.handler = async (event, context, callback) =&gt; {
    try{
        const { id } = event;

        const arCategory = id.split(&#39; &#39;);

        let bucket, key;

        if (arCategory[0] === &#39;video&#39;) {
            bucket = &#39;버켓명&#39;;
            key = arCategory[1] + &#39;.arexport&#39;;
        }
        if (arCategory[0] === &#39;makers&#39;) {
            bucket = &#39;버켓명&#39;;
            key = arCategory[1] + &#39;/&#39; + id + &#39;.arexport&#39;;
        }

        // console.log(bucket,key)

        const params = {
            Bucket: bucket,
            Key: key,
        };

        // return params;

        return s3.getObject(params, async (err, data) =&gt; {
            if (err) {
                err.message = &#39;Not yet!!!!!!!!&#39;;
                return err.message;
                // next(err); // an error occurred
            } else {
                const jsonData = data.Body;
                // 여기 수정 현재는 파일안의 링크 =&gt; 파일 자체 링크
                // const jsonText = `https://${params.Bucket}.s3.ap-northeast-2.amazonaws.com/${params.Key}`;
                const jsonText = JSON.parse(jsonData.toString());
                console.log(jsonText);
                if (jsonText) {
                    callback(null, jsonText);
                    return {statusCode: 200, body: JSON.stringify(jsonText)};
                //    res.status(200).json(jsonText);
                } else {
                    return params;
                //    res.sendStatus(400);
                }
            } // successful response
        }).promise();
    }catch(e){
        console.log(e);
    }
}</code></pre><p>기능을 간단하게 설명하면 S3에 파일이 업로드 되었는지 체크하는 API이다. </p>
<h3 id="1-리전-문제">1. 리전 문제</h3>
<p>해당 lambda 함수는 최초에 오레곤(us-west-2)에서 작성이 되었고 s3 또한 오레곤에 존재했었다. 하지만 메인 서버는 서울(ap-northeast-2)에 존재 하였고 이에 맞춰 S3를 서울에 새로 생성하니 테스트시 지속적인 에러가 발생하였다.</p>
<blockquote>
<p>Error Log</p>
</blockquote>
<pre><code class="language-{">  &quot;errorType&quot;: &quot;NoSuchKey&quot;,
  &quot;errorMessage&quot;: &quot;Not yet&quot;,
  &quot;trace&quot;: [
    &quot;NoSuchKey: Not yet&quot;,
    &quot;    at Request.extractError (/opt/nodejs/node_modules/aws-sdk/lib/services/s3.js:711:35)&quot;,
    &quot;    at Request.callListeners (/opt/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:106:20)&quot;,
    &quot;    at Request.emit (/opt/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:78:10)&quot;,
    &quot;    at Request.emit (/opt/nodejs/node_modules/aws-sdk/lib/request.js:686:14)&quot;,
    &quot;    at Request.transition (/opt/nodejs/node_modules/aws-sdk/lib/request.js:22:10)&quot;,
    &quot;    at AcceptorStateMachine.runTo (/opt/nodejs/node_modules/aws-sdk/lib/state_machine.js:14:12)&quot;,
    &quot;    at /opt/nodejs/node_modules/aws-sdk/lib/state_machine.js:26:10&quot;,
    &quot;    at Request.&lt;anonymous&gt; (/opt/nodejs/node_modules/aws-sdk/lib/request.js:38:9)&quot;,
    &quot;    at Request.&lt;anonymous&gt; (/opt/nodejs/node_modules/aws-sdk/lib/request.js:688:12)&quot;,
    &quot;    at Request.callListeners (/opt/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:116:18)&quot;
  ]
}</code></pre>
<p>해결
S3와 Lambda는 같은 리전에서 사용한다.</p>
<h3 id="2-timeout-시간-제한">2. Timeout (시간 제한)</h3>
<p>제일 처음 나왔던 에러메세지는 다음과 같다.</p>
<blockquote>
<p>Task timed out after 3.00 seconds</p>
</blockquote>
<p>이런 에러 메세지와 함께 함수가 아예 실행되지 않았다.
찾아보니 Lambda의 기본 제한 시간 설정이 3초로 되어 있어 설정한 시간동안 함수가 끝이 나지 않으면 Timeout을 리턴하는 것이였다.</p>
<blockquote>
<ul>
<li>람다 함수의 제한 시간 설정<ul>
<li>설정한 시간이 지날 때까지 함수의 실행이 끝나지 않으면 함수는 실패함</li>
<li>함수의 실행이 예상 외로 오래걸리거나 무한로딩되지 않도록 제한 시간이 지나면 실패시킴</li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li>발생 원인<ul>
<li>원격 API에 연결할 수 없거나 API 호출에 응답하는 데 오래 걸림</li>
<li>API 호출 시 소켓 제한 시간 내에 응답을 받지 못함</li>
<li>API 호출 시 Lambda 함수의 제한 시간 내에 응답을 받지 못함</li>
<li>네트워크 문제로 인해 재시도 및 중복 API 요청이 발생시</li>
</ul>
</li>
</ul>
<p>해결</p>
<p>[구성] -&gt; [일반구성] -&gt; [편집] 에 들어가 제한 시간을 늘려주면 된다.</p>
<h3 id="3-trycatch">3. try/catch</h3>
<p>위와 같이 timeout이 발생했을때에 관하여 찾아보다가 알게된 부분이다.
단순 handler 함수라고 생각하고 기능적인 부분만 생각을 하고 작성을 하였었는데
람다 함수의 성능 개선 부분에서 try/catch를 통해 API가 설정한 제한 시간까지 무한정 기다리지 않게 작성을 해주는 것이 좋다.</p>
<blockquote>
<p>[참고] - <a href="https://dev.classmethod.jp/articles/lim-lambda-performance/">https://dev.classmethod.jp/articles/lim-lambda-performance/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jenkins Credentials.xml]]></title>
            <link>https://velog.io/@chs_0303/Jenkins-Credentials.xml</link>
            <guid>https://velog.io/@chs_0303/Jenkins-Credentials.xml</guid>
            <pubDate>Tue, 18 Oct 2022 00:48:04 GMT</pubDate>
            <description><![CDATA[<p>Jenkins를 활용한 웹을 개발하고자 API와 cli를 사용하여 테스트를 진행하던 중 발생한 일을 정리해둔다.</p>
<h3 id="시작">시작</h3>
<p>Github과 jenkins를 연동하여 웹에서 프로젝트 생성시 Github repo가 생성 됨과 동시에 jenkins job 또한 자동으로 빌드 되어 생성되는 기능을 개발하고자 하였다.</p>
<h3 id="문제">문제</h3>
<p>기존에 Github과 Jenkins의 연결을 ssh-key를 통한 연결을 하였다. 이 부분에서 문제가 생겼는데, Jenkins에서 생성한 Credential 하나에 ssh-keygen을 통해 생성한 key를 등록하고 동일한 key를 Github repo에 등록하는 식으로 하였다. 하지만 Github는 서로 다른 Repo에서 동일한 ssh key를 사용할 수 없었고 이에 새로운 repo당 Credential을 하나씩 생성해 주어야 됐다.</p>
<h3 id="첫번째-방향성">첫번째 방향성</h3>
<p>단순히 ssh-keygen을 통해 생성된 키를 github api와 jenkins api 혹은 cli로 각각 Repo 생성, job생성을 하면 될 줄 알았다.</p>
<p>하지만 jenkins cli는 생각보다 어렵고 난해했다.</p>
<pre><code class="language-bash">[참고]
[https://stackoverflow.com/questions/29616660/how-to-create-jenkins-credentials-via-the-rest-api](https://stackoverflow.com/questions/29616660/how-to-create-jenkins-credentials-via-the-rest-api)
- 위 방법에도 curl을 활용한 방법 밖엔 없었다. 원하던 ssh키를 넣는 방법은 존재하지 않았다.</code></pre>
<h4 id="시도">시도</h4>
<p>jenkins 서버에 다이렉트로 접속하여 /var/lib/jenkins에 직접 들어가 xml을 수정하는 방법을 생각하였다. </p>
<p>해당 경로에 가면 jobs라는 디렉토리가 존재하고 해당 디렉토리 안의 job을 복사하고 내부의 config.xml, credential.xml 파일을 수정할 생각이였다.</p>
<h4 id="첫번째-방향성의-문제">첫번째 방향성의 문제</h4>
<ol>
<li>xml 파일을 거의 처음 접해보았다. 구조가 약간 html 과 비슷(?) 하여 태그로 구분하였지만 정확히 구분 하였는지는 잘 알지 못하였다.</li>
<li>jenkins cli 중 해당 job의 credential.xml을 가져오는 <strong>get-credentials-as-xml</strong> 을 사용하였는데 </li>
</ol>
<pre><code class="language-xml">&lt;com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey plugin=&quot;ssh-credentials@291.v8211e4f8efb_c&quot;&gt;
  &lt;scope&gt;GLOBAL&lt;/scope&gt;
  &lt;id&gt;sshtest&lt;/id&gt;
  &lt;description&gt;&lt;/description&gt;
  &lt;username&gt;quantum-universe&lt;/username&gt;
  &lt;usernameSecret&gt;false&lt;/usernameSecret&gt;
  &lt;privateKeySource class=&quot;com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource&quot;&gt;
    &lt;privateKey&gt;
      &lt;secret-redacted/&gt;
    &lt;/privateKey&gt;
  &lt;/privateKeySource&gt;
&lt;/com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey&gt;</code></pre>
<p>이러한 형식으로 직접적으로 ssh 키를 넣는 것이 아닌 패키지를 활용하여 값을 넣는 방식이라 직접적으로 파일을 수정하여 적용시키기엔 어려움이 있었다. 즉, ssh키를 직접적으로 넣어 credential을 생성할 수 없었다.</p>
<ol>
<li>제일 큰 문제로는 <strong>get-job</strong> 을 통한 job의 설정을 변경하는데 있었다. config.xml의 파일이다.</li>
</ol>
<pre><code class="language-xml">&lt;?xml version=&#39;1.1&#39; encoding=&#39;UTF-8&#39;?&gt;
&lt;project&gt;
  &lt;actions/&gt;
  &lt;description&gt;&lt;/description&gt;
  &lt;keepDependencies&gt;false&lt;/keepDependencies&gt;
  &lt;properties/&gt;
  &lt;scm class=&quot;hudson.plugins.git.GitSCM&quot; plugin=&quot;git@4.11.3&quot;&gt;
    &lt;configVersion&gt;2&lt;/configVersion&gt;
    &lt;userRemoteConfigs&gt;
      &lt;hudson.plugins.git.UserRemoteConfig&gt;
        &lt;url&gt;git@github.com:quanutum-universe/test.git&lt;/url&gt;
        &lt;credentialsId&gt;ec2joo&lt;/credentialsId&gt;
      &lt;/hudson.plugins.git.UserRemoteConfig&gt;
    &lt;/userRemoteConfigs&gt;
    &lt;branches&gt;
      &lt;hudson.plugins.git.BranchSpec&gt;
        &lt;name&gt;*/main&lt;/name&gt;
      &lt;/hudson.plugins.git.BranchSpec&gt;
    &lt;/branches&gt;
    &lt;doGenerateSubmoduleConfigurations&gt;false&lt;/doGenerateSubmoduleConfigurations&gt;
    &lt;submoduleCfg class=&quot;empty-list&quot;/&gt;
    &lt;extensions/&gt;
  &lt;/scm&gt;
  &lt;canRoam&gt;true&lt;/canRoam&gt;
  &lt;disabled&gt;false&lt;/disabled&gt;
  &lt;blockBuildWhenDownstreamBuilding&gt;false&lt;/blockBuildWhenDownstreamBuilding&gt;
  &lt;blockBuildWhenUpstreamBuilding&gt;false&lt;/blockBuildWhenUpstreamBuilding&gt;
  &lt;triggers&gt;
    &lt;com.cloudbees.jenkins.GitHubPushTrigger plugin=&quot;github@1.34.4&quot;&gt;
      &lt;spec&gt;&lt;/spec&gt;
    &lt;/com.cloudbees.jenkins.GitHubPushTrigger&gt;
  &lt;/triggers&gt;
  &lt;concurrentBuild&gt;false&lt;/concurrentBuild&gt;
  &lt;builders&gt;
    &lt;hudson.tasks.Shell&gt;
      &lt;command&gt;#!/bin/sh
&lt;/command&gt;
      &lt;configuredLocalRules/&gt;
    &lt;/hudson.tasks.Shell&gt;
  &lt;/builders&gt;
  &lt;publishers/&gt;
  &lt;buildWrappers&gt;
    &lt;jenkins.plugins.nodejs.NodeJSBuildWrapper plugin=&quot;nodejs@1.5.1&quot;&gt;
      &lt;nodeJSInstallationName&gt;node16_ec2&lt;/nodeJSInstallationName&gt;
      &lt;cacheLocationStrategy class=&quot;jenkins.plugins.nodejs.cache.DefaultCacheLocationLocator&quot;/&gt;
    &lt;/jenkins.plugins.nodejs.NodeJSBuildWrapper&gt;
  &lt;/buildWrappers&gt;
&lt;/project&gt;</code></pre>
<p>기존의 job dir를 복사하여 name이나 repo 주소, branch 명 등을 변경하고 script를 추가할 수 있었다. 그 후 복사된 job이 적용되었나 jenkins에 접속하여 확인해 보았지만 적용이 되지않았다. <del>나중에 그냥 create-job cli로 변경하니 문제는 해결되었다.</del></p>
<ul>
<li><p>원인</p>
<p>  jenkins 서버내의 파일들은 레플리카( 백업 복제본 ) 인 것 같았다. 그렇기에 설정파일을 변경해도 실제 서버에 적용 되지않았다. 이를 적용하기 위해선 </p>
<p>  jenkins 관리 &gt;  Reload Configuration from Disk 를 통해 다시 적용 시켜주어야 했다.</p>
</li>
</ul>
<h3 id="두번째-방향성">두번째 방향성</h3>
<p>찾다보니 ssh를 통한 Git 연결외에 Token을 통한 연결이 가능하다는 사실을  알게 되었다. 이전에 초기 셋팅을 할때 private repo는 ssh로만 연결이 가능할 줄 알았었지만 그렇지 않았다. </p>
<pre><code class="language-xml">[참조]
https://kitty-geno.tistory.com/m/88
감사합니다</code></pre>
<p>이전에 Token으로 했을 때 실패했던 원인에 대해서 좀 정리해보자면</p>
<p>총 2개의 Credential이 필요했었다. </p>
<ol>
<li>Github Token 발행 </li>
<li>jenkins 관리 &gt; 시스템 설정 &gt; Github &gt; Add Github Server &gt;</li>
</ol>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/4e1edd81-50dd-41bb-84e7-cde8293a90ed/image.png" alt=""></p>
<p>Add를  통해 새로운 Credential 생성</p>
<ul>
<li><p>kind - Secret text</p>
</li>
<li><p>Secret - Git Token</p>
</li>
<li><p>ID - Credential 이름 (정해주면 된다)</p>
<p>그 후 Test connection을 해준뒤 </p>
</li>
</ul>
<pre><code>Credentials verified for user {Credential 이름}, rate limit: 4998</code></pre><p>이러한 형식으로 나오면 저장하면된다.</p>
<ol>
<li>새롭게 job 생성에 들어간 후 </li>
<li>Repo url을 적어주고 branch를 적용할 branch로 변경 해준 후 </li>
</ol>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/25941b7d-177c-49ae-ad50-0284962bb5d4/image.png" alt=""></p>
<p>Credentials Add를 통해 새로운 Credential을 만들어준다.</p>
<ul>
<li><strong>Kind</strong> - Username with password</li>
<li><strong>Username :</strong> GitHub 계정 입력</li>
<li><strong>Password :</strong> Github token 입력 (계정 비밀번호 X)</li>
<li><strong>ID :</strong> Credentials의 이름 ( 직접 작성 )</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS lambda]]></title>
            <link>https://velog.io/@chs_0303/AWS-lambda</link>
            <guid>https://velog.io/@chs_0303/AWS-lambda</guid>
            <pubDate>Thu, 29 Sep 2022 08:02:17 GMT</pubDate>
            <description><![CDATA[<h1 id="aws-lambdawith-api-gateway">AWS lambda(with API gateway)</h1>
<h3 id="lambda">Lambda</h3>
<ul>
<li><p>함수 생성</p>
<ul>
<li>생성시 실행 역할 설정이 <strong>중요</strong><ul>
<li>요번에 만든 함수는 S3 버킷 내부의 파일들을 읽는 것임으로 IAM에서 S3 read에 관한 권한만 준다.</li>
</ul>
</li>
</ul>
</li>
<li><p>Lambda Test 필수</p>
<ul>
<li>lambda는 default</li>
</ul>
</li>
<li><p>함수 생성 후 중요한 것 두가지</p>
<ul>
<li>트리거 추가</li>
<li>Layers 설정</li>
</ul>
</li>
</ul>
<h3 id="트리거-추가">트리거 추가</h3>
<ul>
<li>API Gateway이다. Lambda에 대한 요청들을 걸러주는(?) 곳이다.</li>
<li>생성할 때 REST API로 생성해 주었다.</li>
<li>생성 후에 API 리소스에 들어간 후 메소드를 수정해준다. (ex. 쿼리 파라미터, 요청 본문 (body)</li>
<li>리소스<ul>
<li>작업에서 CORS에 대한 설정을 해준다.</li>
<li><strong>중요. TEST를 한 후 API 배포를 해주어야 POSTMAN 등 외부에서 사용이 가능하다.</strong></li>
</ul>
</li>
</ul>
<h3 id="layers-설정">Layers 설정</h3>
<ul>
<li>계층 설정으로 nodeJS 패키지 등 모듈들을 저장해 두는 곳이다.</li>
<li>호환 런타임이 설정 사항이라 되어 있지만 입력하지 않을시 추가 할 때 보이지 않음. 꼭 넣어주자.</li>
<li>파일을 .zip 형태 혹은 S3에서 다이렉트로 가져오는 두가지 방법이 있다.<ul>
<li>.zip파일로 넣을 때 node_modules 파일만 압축해서 넣으면 인식이 되지않는다.<ul>
<li>꼭 <strong>nodejs/node_modules</strong> 의 형태로 넣어야된다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="작성시-주의-사항">작성시 주의 사항</h3>
<ol>
<li>지속적으로 처음 요청을 보냈을 때 ERROR가 발생 ( error code : PermanentRedirect )<ul>
<li>원인을 찾아보니 S3에 접근할 때 리전이 서로 달라서 생기는 현상 ( lambda는 오레곤, s3는 오하이오 였음) 수정하니 에러 발생 X</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker 조금씩 사용해보기]]></title>
            <link>https://velog.io/@chs_0303/Docker-%EC%A1%B0%EA%B8%88%EC%94%A9-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@chs_0303/Docker-%EC%A1%B0%EA%B8%88%EC%94%A9-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 14 Sep 2022 01:24:40 GMT</pubDate>
            <description><![CDATA[<h3 id="docker-컨테이서-생성">docker 컨테이서 생성</h3>
<ul>
<li>docker build ./ (현재 디렉토리)<ul>
<li>-t : tag의 약자. 컨테이너 이름 짓기</li>
</ul>
</li>
<li>Node 베이스 이미지를 통해 <strong>임시 컨테이너</strong>를 생성한 후 docker 이미지를 만든다.<ul>
<li>Base image → 임시 컨테이너 → docker image</li>
<li>그렇기 때문에 생성할 때 none 이라는 이미지가 생성되었다가 사라진다.</li>
</ul>
</li>
</ul>
<h3 id="docker-이미지-실행">docker 이미지 실행</h3>
<ul>
<li>docker run { 이미지 이름 }<ul>
<li>-it : 실행을 이어 가겠다. 바로 종료시키지 않겠다.</li>
<li>-p : 포트 포워딩. 어떤 포트로 접속해도 연 서버로 프록시</li>
<li>-d : detach 터미널을 바로 탈출</li>
</ul>
</li>
</ul>
<h3 id="dockerfile">Dockerfile</h3>
<pre><code class="language-docker"># 베이스 이미지를 명시해준다.
FROM node:{Version}

# 모든 파일들을 포함시키기 위해서 (ex. package.json, app.ts)
COPY ./ ./

# 추가적으로 필요한 파일들을 다운로드 한다.
RUN npm install

#컨테이너 시작시 실행될 명령어들을 작성한다.
CMD [ &quot;npm&quot;, &quot;run&quot;,&quot;start&quot;]</code></pre>
<p>위가 기본틀의 예시이다. (NestJS 기준)</p>
<pre><code class="language-docker">WORKDIR /usr/src/app</code></pre>
<p>WORKDIR = 이미지 안에서 어플리케이션 소스 코드를 가지고 있을 디렉토리를 생성하는 것</p>
<p>이 디렉토리가 어플리케이션의 working 디렉토리가 된다.</p>
<p><strong>문제점 및 사용이유</strong></p>
<ul>
<li>Root 디렉토리에 모두 합쳐지기 때문에<ol>
<li>원래 있던 home, lib 등 이름이 겹치는 것들은 덮어 씌우기가 된다.</li>
<li>정리정돈이 안된다.</li>
</ol>
</li>
</ul>
<h3 id="packagejson">package.json</h3>
<p>위 Dockerfile 에서는 COPY를 통해 한 번에 모든 파일을 불러왔지만 실제로는 조금 다르다.</p>
<pre><code class="language-docker">FROM node:{Version}

WORKDIR /usr/src/app

COPY package.json ./

RUN npm install

COPY ./ ./

CMD [ &quot;npm&quot;, &quot;run&quot;,&quot;dev&quot; ]</code></pre>
<p>이러한 이유는 코드가 수정이 되고 다시 빌드를 할 때 발생한다.  ( 위의 Dockerfile을 수정전, 아래의 Dockerfile을 수정후라 부르겠다. )</p>
<p>핵심 - Dockerfile을 빌드 할 때 <strong>캐싱</strong>이 발생한다.</p>
<p>그렇기 때문에 수정전에 경우 재빌드를 할 때 종속성이 변경된 것이 아니라 소스만 변경되었으면 package.json을 통해 다시 한 번 RUN npm install 을 수행하게 된다. </p>
<p>이에 따라 수정후의 경우는 소스 변경의 경우 npm install 이 따로 실행되지 않는다.</p>
<ul>
<li><p>문제 발생</p>
<p>  package-lock.json은 실제 package.json에 정의된 node module의 dependencies tree를 정의하기 때문에 package-lock.json파일도 반드시 필요하다. </p>
<p>  그렇기에 </p>
<pre><code class="language-docker">  COPY package*.json ./</code></pre>
<p>  와 같이 써야한다.</p>
<pre><code class="language-bash">  npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I&#39;ll try to do my best with it!</code></pre>
<p>  또 위와 같은 에러가 나타났는데 이는 npm 버전에 나타났던 문제인데 핵심은 </p>
<pre><code class="language-docker">  FROM node:{Version}</code></pre>
<p>  이 부분에서의 문제였다. package</p>
</li>
</ul>
<h3 id="docker-volume">Docker Volume</h3>
<p>기존 방식은 COPY를 통해 도커 컨테이너에 새롭게 빌드하여 진행</p>
<p>Volume 방식은 도커 컨테이너에서 로컬의 소스들을 <strong>참조(Mapping)</strong> 하여 진행</p>
<pre><code class="language-bash">docker run -d -p 4000:8080 -v /usr/src/app/node_modules -v $(pwd):/usr/src/app chsu00/hello-docker</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cloudfront(with S3)]]></title>
            <link>https://velog.io/@chs_0303/Cloudfrontwith-S3</link>
            <guid>https://velog.io/@chs_0303/Cloudfrontwith-S3</guid>
            <pubDate>Wed, 03 Aug 2022 04:51:36 GMT</pubDate>
            <description><![CDATA[<h1 id="cloudfrontwith-s3">Cloudfront(with S3)</h1>
<p>배포 생성으로 들어간다.</p>
<p>처음엔 전부 셋팅하지만 나중에 수정할 땐 3부위로 나뉜다.</p>
<ul>
<li>일반 - 설정</li>
<li>원본 도메인</li>
<li>동작</li>
</ul>
<h3 id="원본-원본-도메인-">원본( 원본 도메인 )</h3>
<p>원본 도메인 선택</p>
<ul>
<li>S3, Elastic Load Balancer등을 연결 시킬 원본 도메인(엔드포인트?)</li>
</ul>
<p>이름 - 이름</p>
<h3 id="기본-캐시-동작--동작-">기본 캐시 동작 ( 동작 )</h3>
<p>경로 패턴 - 기본값</p>
<p>자동으로 객체 압축 - Yes</p>
<p><em>뷰어 엑세스 제한 - *</em>따로 정리**</p>
<p>캐시 키 및 원본 요청</p>
<p>캐시 정책 - CachingOptimized</p>
<p>원본 요청 정책 - CORS-S3Origin (S3의 Cors 정책을 똑같이 따르겠다.)</p>
<p>응답 헤더 정책 - CORS-with-prefilght-and-SecurityHeadersPolicy( 모든 Cors 정책과 보안 헤더 정책을 적용시키겠다?)</p>
<h3 id="설정-일반---설정-">설정( 일반 - 설정 )</h3>
<p>다 필요 없고 - 물론 아직은</p>
<p>기본값 루트 객체 - S3라면 빌드 파일의 시작 index.html</p>
<h2 id="뷰어-엑세스-제한"><strong>뷰어 엑세스 제한</strong></h2>
<p>서명 URL이라고도 하며 인증이 된 URL을 통해서만 접근이 가능하도록 URL을 만들어 전달한다.</p>
<p>이 URL은 수명을 줄 수도 있다.</p>
<p>YES를 선택할 경우</p>
<p>Trusted key groups 와 Trusted signer 이렇게 있는데 </p>
<h3 id="trusted-key-groups">Trusted key groups</h3>
<ul>
<li>키 그룹을 만들어 허용된 키들만 접근이 가능하게 한다.</li>
</ul>
<h3 id="trusted-signer">Trusted signer</h3>
<ul>
<li>인증된 사용자들만 사용할 수 있게 하는 것인데 아마 이 부분에서 필요했던거 같다..</li>
<li>바로 cloudfront public key, private key, access key id 이렇게 3개를 만들어 준다. (말이 3개지 그냥 하나 만들면 다 만들어진다)</li>
<li>일단 Node 버전으로 다 실패 했고 Python으로 성공하였다. ( 나머지 : Perl, PHP, Java, C#)</li>
</ul>
<p><a href="https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/PrivateCFSignatureCodeAndExamples.html">서명 URL에 대한 서명을 만드는 코드 예제</a></p>
<pre><code class="language-python">import datetime

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from botocore.signers import CloudFrontSigner

def rsa_signer(message):
    with open(&#39;&lt;private key 경로&gt;&#39;, &#39;rb&#39;) as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend()
        )
    return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())

key_id = &#39;access key id&#39;
url = &#39;&lt;cloudfront 도메인 + /index.html&#39;
expire_date = datetime.datetime(2023, 1, 1) # 수명 주기를 정할 수 있다.

cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

# Create a signed url that will be valid until the specific expiry date
# provided using a canned policy.
signed_url = cloudfront_signer.generate_presigned_url(
    url, date_less_than=expire_date)
print(signed_url)</code></pre>
<p>위 코드를 실행하면 생성되는 URL을 통해 접근을 하면 된다. ( 기존 cloudfront 도메인으로 접속시 Missing key만 뜬다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Singleton&Prototype]]></title>
            <link>https://velog.io/@chs_0303/SingletonPrototype</link>
            <guid>https://velog.io/@chs_0303/SingletonPrototype</guid>
            <pubDate>Thu, 09 Jun 2022 07:53:01 GMT</pubDate>
            <description><![CDATA[<h3 id="singleton">Singleton</h3>
<p>전체 시스템에서 클래스의 인스턴스를 하나만 존재하도록 하는 패턴이다.</p>
<ul>
<li>객체 리터럴</li>
</ul>
<pre><code class="language-jsx">// object literal
const obj = {
    name: &#39;singleton&#39;,
};

console.log(obj);</code></pre>
<p>하지만 이 객체는 캡슐화가 되어 있지 않아 결합도가 커지며 데이터를 보호(모든 속성이 다 공개 되어 있음)하는데 문제가 생길 수 있다.</p>
<ul>
<li>ES5</li>
</ul>
<pre><code class="language-jsx">// ES5
const ES5_Singletion = (function () {
    let instance;

    function setInstance() {
      instance = {
        id: 1,
        text: &#39;hello&#39;,
      };
    }

    return {
        getInstance() {
            if (!instance) {
                setInstance();
            }
            return instance;
        }
    };
})();

const es5Singletion1 = ES5_Singletion.getInstance();
const es5Singletion2 = ES5_Singletion.getInstance();

console.log(&#39;Singleton function&#39;, es5Singletion1 === es5Singletion2);</code></pre>
<p>즉시 실행함수를 활용해서 instance 와 setInstance 함수를 캡슐화 </p>
<ul>
<li>ES7</li>
</ul>
<pre><code class="language-jsx">// ES7
let instance;

class Singleton {
  static instance
  constructor(){
    if (instance) return instance;
    this.id = 1;
    this.name = &#39;hello&#39;;
    instance = this;
  }
}

const es6Singleton1 = new Singleton();
const es6Singleton2 = new Singleton();

console.log(&quot;singleton class&quot;, es6Singleton1 === es6Singleton2);</code></pre>
<p>정적 필드</p>
<p>게임 을 실행 했을 때 게임은 한 번만 켜져야하기 때문에 싱글톤 패턴이 적절하다.</p>
<h3 id="prototype">Prototype</h3>
<p>프로토타입 패턴은 객체를 생성할때 원본객체를 복사해서 생성하는 방식을 말한다.</p>
<p><strong>JS에선 Class가 아닌 함수로 객체를 생성한다.(ES5)</strong></p>
<pre><code class="language-jsx">function A(){
    this.user = &quot;aaa&quot;;
};

// new 연산자
var new_a = new A();

// Object.create
var object_a = Object.create(A);

A.prototype.admin = &quot;test&quot;;</code></pre>
<p>위의 A함수를 생성자로 호출하면서 생성된 new_a객체는 A.prototype 객체를 원본 객체로하여 복제된 객체이다.</p>
<pre><code class="language-jsx">console.log(new_a);
// A { user: &#39;aaa&#39; }

console.log(new_a.user);
// aaa

console.log(new_a.admin);
// test

console.log(new_a.__proto__);
// { admin: &#39;test&#39; }

console.log(A.prototype === new_a.__proto__);
// true</code></pre>
<p>A의 prototype 객체와 new_a의 원본 객체가 일치한다.</p>
<pre><code class="language-jsx">console.log(object_a);
// A {}

console.log(object_a.user);
// undefined

console.log(object_a.admin);
// test

console.log(object_a.__proto__);
// { admin: &#39;test&#39; }

console.log(object_a.__proto__ === A.prototype);
// true</code></pre>
<ul>
<li>A의 prototype 객체와 object_a의 원본 객체가 일치하지만 new 연산자와는 다르게 A의 user는 받아오지 못하였다.<ul>
<li>이는 new 연산자는 Object.create()에 coustructor를 추가해 실행하였기 때문이였다.</li>
<li>그럼에도 Object.create가 생겨난 이유<ul>
<li>constructor가 있는 prototype을 새로운 객체로 덮어 씌우면 원래 자기 자신의 constructor를 상실하게 된다.</li>
<li>즉, JS내부에서 constructor가 망가지는 것을 원하지 않았기에</li>
<li>하지만 더이상 거의 쓰이지 않는다..</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS SES (with aws-sdk)]]></title>
            <link>https://velog.io/@chs_0303/AWS-SES-with-aws-sdk</link>
            <guid>https://velog.io/@chs_0303/AWS-SES-with-aws-sdk</guid>
            <pubDate>Thu, 09 Jun 2022 06:39:44 GMT</pubDate>
            <description><![CDATA[<p>사용자에게 메일주소를 받아 SMTP를 사용하여 메일을 보내는 방법들이 있다.
과거 학생때는 이를 구글 계정 설정에서 &quot;보안 수준이 낮은 앱의 액세스&quot;를 허용하여 프로그램과 연결해서 사용했었다. 하지만 2022년 5월 30일부로 이 기능에 대해 지원이 중지되어 더이상 사용할 수 없게되었다.</p>
<p>그래서 찾아보다 발견한 것이 <strong>&quot;AWS SES&quot;</strong> (Simple Email Service) 였다.</p>
<blockquote>
<p>이 글은 Nodejs를 기반으로 작성되었습니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/a588f2e2-2a9f-4f63-8dc7-f43acebea7d7/image.png" alt=""></p>
<p>프리티어는 매 달 62,000건의 메세지까지는 무료이고 요금은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/61894844-4b14-411c-b38d-01ee30628086/image.png" alt=""></p>
<h2 id="사용">사용</h2>
<p>우선 AWS SES에 접속하여준다.
<img src="https://velog.velcdn.com/images/chs_0303/post/8441004c-9849-4091-85ca-73842602e699/image.png" alt=""></p>
<p>오른쪽의 Create Identity를 클릭하여 자격증명에 들어간다.
<img src="https://velog.velcdn.com/images/chs_0303/post/4e5b87d6-f2f0-499e-91ab-909c522f59f2/image.png" alt=""></p>
<p>자격증명을 할 이메일을 적어준다.
메일에 들어가 aws 측에서 온 메일을 5분 안에 확인하고 링크를 클릭하여 자격 증명을 해준다.</p>
<h2 id="aws-sdk">aws-sdk</h2>
<p>aws-sdk에서 사용할 IAM을 생성해주고 액세스키와 비밀키를 가져와 저장해준다.
아래 코드를 형식에 맞게 작성하고 보내주면 된다.</p>
<pre><code class="language-javascript">import nodemailer from &#39;nodemailer&#39;;
import dotenv from &#39;dotenv&#39;;
import AWS from &#39;aws-sdk&#39;;
import { logger } from &#39;./winstonLog&#39;;

AWS.config.update({ region: (SES 리전) });

const transporter = nodemailer.createTransport({
    host: &#39;email-smtp.(SES 리전).amazonaws.com&#39;,
    port: 465,
    secure: true,
    auth: {
        user: (IAM 액세스 키 ID),
        pass: (IAM 비밀키),
    },
});

export function SendMail(email: string, link: string | undefined) {
    transporter.sendMail(
        {
            from: (보내는 메일 주소(어디서)),
            to: (보낼 메일 주소(어디로)),
            subject: (메일 제목),
            html:(메일 내용)
        },
        (err: any, info: { envelope: object; messageId: string }) =&gt; {
            if (err) {
                logger.error(err);
            }
            logger.info(&#39;sendEmail: &#39; + JSON.stringify(info.envelope));
            logger.info(info.messageId);
        },
    );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS EC2 Mac 인스턴스]]></title>
            <link>https://velog.io/@chs_0303/AWS-EC2-mac-server</link>
            <guid>https://velog.io/@chs_0303/AWS-EC2-mac-server</guid>
            <pubDate>Tue, 07 Jun 2022 07:22:43 GMT</pubDate>
            <description><![CDATA[<p>회사에서 <strong>Mac</strong> 서버를 운용해야 할 일이 있었어서 셋팅하고 운영하면서 생겼던 일들을 기록하고 공유하기 위해 글을 쓴다.</p>
<h3 id="1-mac-window">1. Mac? Window?</h3>
<p>활용하는 AR툴이 Window 버전과 Mac버전 두개밖에 존재하지 않고 linux가 따로 없었다. GUI 때문에 그런거 같기에 처음엔 AMI가 많고 더 사용하기 쉽고 비용이 저렴한 Window서버를 사용해서 진행을 하였다. 
 하지만 개발을 하다보니 만들어진 AR파일들이 핸드폰에선 모두 깨지는 현상이 일어났다. 이때 AWS에 존재하는 모든 Window AMI들을 다 사용해 보았고 이유를 발견할 수 있었다. </p>
<p> 바로 <strong>그래픽카드</strong></p>
<p>그래서 GPU 인스턴스인 p2,p3,p4,g3 등등 사용하려 했지만 
<img src="https://velog.velcdn.com/images/chs_0303/post/c0a905da-e683-4a71-bb14-10a5563dc113/image.png" alt=""></p>
<p>GPU 인스턴스들의 vCPU가 0으로 기본 설정이 되어 있어 이를 사용하기 위해서는 aws측에 요청을 해야했다.
요청을 보내도 언제 답이 올지 모르는 상황이고 해서 요청을 보내 놓고 결국 Mac 인스턴스를 사용하기로 하였다.</p>
<h3 id="2-mac-인스턴스">2. Mac 인스턴스</h3>
<p><a href="https://suyeon96.tistory.com/19">Suyeon&#39;s Blog - [AWS] EC2에서 MAC 인스턴스 생성 및 접속</a>
위 블로그 외에는 한글로 된 정보를 찾기 어려웠다. (감사합니다)
Mac 인스턴스는 단 하나의 인스턴스 유형만 사용이 가능하다. </p>
<p><a href="https://aws.amazon.com/ko/ec2/instance-types/mac/"><strong>mac1.metal</strong></a></p>
<p>또한 다른 인스턴스들과 다르게 <strong>전용 호스트</strong>를 먼저 할당을 해준 후에 생성이 가능하다.</p>
<blockquote>
<p>전용 호스트란?</p>
</blockquote>
<ul>
<li>사용자 전용의 EC2 인스턴스 용량을 갖춘 물리적 서버</li>
<li>간단하게 PC한대를 빌리면 된다고 생각하면 좋을꺼 같다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/chs_0303/post/14b5717e-7cd9-41a5-8eb6-16df67850e9d/image.png" alt=""></p>
<p>위와 같이 간단하게 설정을 해주고 생성을 하면된다.</p>
<h3 id="주의할-점">주의할 점</h3>
<pre><code>- 전용 호스트는 생성 후 24시간 동안 삭제가 불가하며 하루치 만큼의 비용이 반드시 청구 된다.</code></pre><p> 그 후 다시 인스턴스 생성으로 돌아가 전용호스트에 만들어준 전용 호스트를 넣고 생성해주면 된다.</p>
<h3 id="3-연결-안됨">3. 연결 안됨</h3>
<p>이게 왠걸 하라는데로 했는데 생성된 인스턴스가 상태 검사에서 1/2로 계속 실패한다.
<img src="https://velog.velcdn.com/images/chs_0303/post/57abb1c5-9b13-418b-aaf9-e34e99e9bbb3/image.png" alt="">
참조 - <a href="https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/monitoring-system-instance-status-check.html">aws 인스턴스 상태 확인</a></p>
<p>위와 같이 아무리 만들고 지우고를 반복해도 생성되지 않았다..</p>
<p>한참을 삽질한 결과 원인은 간단했다.</p>
<h3 id="4-해결">4. 해결</h3>
<p>이걸 따로 써야할지도 의문이다만 원인은 <strong>리전</strong>이였다.
처음 서버를 만든 리전은 싱가포르였고 삽질을 하던 곳도 싱가포르였다.
혹시 모르는 생각에 리전을 오하이오로 옮겼고 정말 무슨 문제가 있었냔 듯이 깔끔하게 생성이되었다.</p>
<p><del>아마 싱가포르 리전에 Mac이 없었을지도</del></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django ORM (select_related & prefetch_related)]]></title>
            <link>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94-3-selectrelated-prefetchrelated</link>
            <guid>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94-3-selectrelated-prefetchrelated</guid>
            <pubDate>Wed, 08 Dec 2021 17:56:13 GMT</pubDate>
            <description><![CDATA[<p>지난 글에 이어 프로젝트를하며 앞으로 개발을 하면서 가장 크게 작용할 부분이라 생각했던 부분이다.</p>
<blockquote>
<p>ORM을 쓰는 이유
데이터베이스의 접근을 줄이는 방법
Django를 좀 더 활용하는 법</p>
</blockquote>
<p>드디어 그 세번째 Django를 좀 더 활용하는 법(ft.데이터베이스 접근을 줄이는 방법)에 대해 정리한다.</p>
<h2 id="select_related-prefetch_related">select_related, prefetch_related</h2>
<p>이 두 가지를 알기 위해선 좀 더 django에 대해 생각해보아야한다.</p>
<h3 id="n1-problem">N+1 problem</h3>
<p>N+1 Problem이란 쿼리 1번으로 N건을 가져왔을때, 관련 컬럼을 얻기 위해 쿼리를 N번 추가 수행하는 문제이다.
Django ORM은 Lazy-Loading방식이므로 ORM에서 명령을 사용할 때마다 DB에서 데이터를 가져오는 것이 아닌 모든 처리가 끝나고 호출 시정에서 쿼리를 실행하는 것이다.
이러한 Lazy-Loading의 문제를 해결할 방법이 Eager-Loading이다. Eager-Loaging은 사전에 쓸 데이터를 포함하여 쿼리를 날려준다.</p>
<p>여기서 Eager-Loading이 바로 <strong>select_related</strong>와 <strong>prefetch_related</strong>이다.</p>
<blockquote>
<p><strong>쿼리셋과 캐싱</strong>
각각의 쿼리셋에는 데이터베이스 액세스를 최소화하기 위한 캐시가 포함되어 있다. 처음 생성된 쿼리셋에서는 캐시가 비어있기 때문에 Query가 발생한다. 그 후 동일한 쿼리셋이 사용되어 질 경우 추가적인 Query가 발생하지 않고 캐시에서 꺼내 사용하게 된다.</p>
</blockquote>
<h3 id="select_related">select_related</h3>
<p>select_related는 foreign-key, OneToOne처럼 single-valued relationship에서 사용이 가능하며 INNER JOIN 하여 데이터를 가져온다.</p>
<h3 id="prefetch_related">prefetch_related</h3>
<p>prefetch_related는 foreign-key, OneToOne뿐만이 아닌 ManyToMany, ManyToOne과 같은 모든 관계에서 사용이 가능하다. prefetch_related는 데이터를 가져와서 MEMORY JOIN하는 형식으로 저장해 둔다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Allmytour 인턴쉽 프로젝트를 하며 -2]]></title>
            <link>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94-2</link>
            <guid>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94-2</guid>
            <pubDate>Sat, 04 Dec 2021 13:01:53 GMT</pubDate>
            <description><![CDATA[<p>지난 글에 ORM을 사용하는 이유에 대해 정리를 해보았다. 이번에는 이어서 생각했던 것에 대한 2번째 정리시간이다.</p>
<blockquote>
<p>ORM을 쓰는 이유
데이터베이스의 접근을 줄이는 방법
Django를 좀 더 활용하는 법</p>
</blockquote>
<p>바로 &quot;데이터베이스의 접근을 줄이는 방법&quot;에 대한 생각이다.</p>
<h2 id="데이터-베이스-접근-줄이기최적화">데이터 베이스 접근 줄이기(최적화)</h2>
<p>시리즈와 가장 알맞는 고민이였던 같다. 가장 기본적인 DB hit수를 줄여 최적화 시키는 방법에 대해 고민을 해봤다.</p>
<p>정말 단순하게 생각을 해서 Queryset 사용횟수를 최소화하기로 했다. 아래와 같다.</p>
<pre><code># 이전
&quot;sns&quot;: [sns.id for sns in Sns.objects.filter(maker_id=maker.id)],

# 이후
&quot;sns&quot;: list(maker.sns_set.values_list(&quot;address&quot;, flat=True)),</code></pre><p>이와 같이 반복문을 통해 모든 결과값마다 DB를 Hit시키는 방법을 values() 혹은 values_list()를 통해 한번에 값들만 가져 나올 수 있게 진행하리라 생각했다. 하지만 착각한 것이 있었다.</p>
<h3 id="착각">착각</h3>
<p>훨씬 더 전에 코드를 작성할 때 썻던 append()와 list comprehension과의 차이를 간과하고 있었다. for loop를 돌리면서 append를 하면 루프가 돌 때마다 DB hit이 이뤄지는 것이 맞지만 list comprehension은 그렇지 않았다.
하지만 덕분에 의외의 것들을 찾아내었다.</p>
<h3 id="list-vs-list-comprehension">list() vs list comprehension</h3>
<p><a href="https://stackoverflow.com/questions/29356846/python-list-vs-list-comprehension-building-speed">Python list() vs list comprehension building speed
</a>
내용을 정리하자면 list comprehension은 일반 루프와 마찬가지로 Python bytecode로 루프를 실행한다.
반면, list()은 C code로만 반복되며, 훨씬 더 빠르다.</p>
<h3 id="다음">다음</h3>
<p>또한 마찬가지로 데이터베이스 접근을 줄이는 방법에 대해 찾았지만 세번째 주제와 공통적인 부분으로 다음 글에서 정리하려한다. 앞으로 개발을 하면서 가장 크게 작용할 부분이라 생각이든다. 또한 앞의 글들은 사실 다음 글을 위한 빌드라고 생각해도 무방할정도로 중요한 내용일 것이다.</p>
<h2 id="21-12-08-추가">21-12-08 추가</h2>
<p>너무 간단하게 생각했던것 같다..
list() 메소드와 list comprehension을 비교한다는 것은 좋았으나 내부의 쿼리가 일치하지 않다는 것을 알아버렸다. 그렇기 때문에 코드 수정한것만 가지고 list() 와 list comprehension을 비교하는 것은 옳지 않다는 것을 알았다.. 단순히 결과 값이 잘 나왔을 뿐..
또한 글 제목과 내용이 일치하지 않아서 어떻게 할지 고민중이다. ㅠㅠ 일단 제목은 바꿀 것이다.</p>
<p>또한 시리즈에서 제외했다..!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django ORM]]></title>
            <link>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@chs_0303/Django-ORM-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Tue, 30 Nov 2021 05:35:00 GMT</pubDate>
            <description><![CDATA[<p>전에 프로젝트를 진행하면서 조금 더 깊게 Django에 대해 생각 할 수 있는 기회가 생겼다. 그 때 생각 했던 것들을 정리해 보려한다.</p>
<blockquote>
<ol>
<li>ORM을 쓰는 이유</li>
<li>데이터베이스의 접근을 줄이는 방법</li>
<li>Django를 좀 더 활용하는 법</li>
</ol>
</blockquote>
<p>그 중 첫번째 ORM을 쓰는 이유에 관한 글이다.</p>
<h2 id="django-orm을-사용하는-이유">Django ORM을 사용하는 이유</h2>
<p>ORM(Object Relational Mapping)은 객체 관계 매핑으로 객체 지향 언어와 데이터 베이스의 통역사 같은 역활을 한다.
전에 생각으론 단순히 <strong>ORM을 사용하는 이유</strong>는 보다 <strong>직관적</strong>이고 사람이 이해하기가 쉬워서만 사용한다고 생각하였다.
하지만 찾아보니 좀 더 많은 이유가 있었고 그에 따른 단점들도 존재 하였다.</p>
<h3 id="장점">장점</h3>
<p><strong>1. 직관성(객체 지향적 코드)</strong></p>
<ul>
<li>SQl문이 아닌 클래스의 메서드를 통해 데이터베이스를 조작하기 때문에 객체 모델만을 이용해 프로그래밍을 할 수 있다.</li>
<li>CRUD를 위한 SQL문을 작성할 필요가 없다.(쿼리 작성은 필요)</li>
<li>각 객체 별로 작성하기 때문에 가독성이 높다.</li>
</ul>
<p><strong>2. 재사용, 유지보수의 편리성이 증가</strong></p>
<ul>
<li>ORM은 독립적으로 작성이되어있고, 객체로 작성되었기 때문에 재활용이 가능하다.</li>
<li>매핑하는 정보가 명확하여 ERD를 보는 의존도를 낮출 수 있다.</li>
</ul>
<p><strong>3.DBMS 종속성 하락</strong></p>
<ul>
<li>객체 간의 관계를 바탕으로 SQL문을 자동으로 생성하고, 객체의 자료형 타입까지 사용할 수 있기 때문에 RDBMS의 데이터 구조와 객체 지향 모델 사이의 간격을 좁힐 수 있다.</li>
<li>객체에만 집중할 수 있기 때문에 DBMS를 교체하는 큰 작업에도 리스크가 적다.</li>
</ul>
<h3 id="단점">단점</h3>
<p><strong>1. 완벽한 ORM 으로만 서비스를 구현하기가 어렵다.</strong></p>
<ul>
<li>사용하기는 편하지만 설계는 매우 신중하게 해야한다.</li>
<li>프로젝트의 복잡성이 커질경우 난이도 또한 올라갈 수 있다.</li>
<li>잘못 구현된 경우에 속도 저하 및 심각할 경우 일관성이 무너지는 문제점이 생길 수 있다.</li>
<li>일부 자주 사용되는 대형 쿼리는 속도를 위해 SP를 쓰는등 별도의 튜닝이 필요한 경우가 있다.</li>
<li>DBMS의 고유 기능을 이용하기 어렵다. (하지만 이건 단점으로만 볼 수 없다 : 특정 DBMS의 고유기능을 이용하면 이식성이 저하된다.)</li>
<li>복잡한 쿼리문의 경우 SQL문이 좀 더 직관적이면서 효율적일 수 있다.</li>
</ul>
<p><strong>2. DB와 바로 연결하는 것보다 초기설정이 더 많아지거나 복잡해 질 수있다.</strong></p>
<p><strong>3. DB와 ORM 프레임워크, 내부코드에 대한 충분한 이해가 없는 경우 문제 해결이 힘들다.</strong></p>
<p><strong>4. DB에 직접 Query문을 보내는 것이 아니기 때문에 성능저하가 발생한다.</strong></p>
<h2 id="정리">정리</h2>
<p>Django ORM을 사용하면서 느꼈던 장단점도 있고 생각하지 못한 장단점도 있다. 직관성이나 재사용성은 생각을 해보았지만 데이터베이스 측면 즉, 서버에서의 생각을 많이 하지 못한것 같다. 단순히 ORM을 사용만 하는 것이 아닌 동작 방식을 이해하고 작성해야 서버의 성능을 향상시킬 수 있다는 것을 알게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 더 맵게]]></title>
            <link>https://velog.io/@chs_0303/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94-%EB%A7%B5%EA%B2%8C</link>
            <guid>https://velog.io/@chs_0303/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94-%EB%A7%B5%EA%B2%8C</guid>
            <pubDate>Wed, 10 Nov 2021 17:04:31 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같이 특별한 방법으로 섞어 새로운 음식을 만듭니다.</p>
<p><code>섞은 음식의 스코빌 지수 = 가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)</code></p>
<p>Leo는 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복하여 섞습니다.Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때, 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는 최소 횟수를 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>scoville의 길이는 2 이상 1,000,000 이하입니다.</li>
<li>K는 0 이상 1,000,000,000 이하입니다.</li>
<li>scoville의 원소는 각각 0 이상 1,000,000 이하입니다.</li>
<li>모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우에는 -1을 return 합니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>scoville</th>
<th>K</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[1,2,3,9,10,12]</td>
<td>7</td>
<td>2</td>
</tr>
<tr>
<td>### 입출력 예 설명</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<ol>
<li>스코빌 지수가 1인 음식과 2인 음식을 섞으면 음식의 스코빌 지수가 아래와 같이 됩니다.새로운 음식의 스코빌 지수 = 1 + (2 * 2) = 5가진 음식의 스코빌 지수 = [5, 3, 9, 10, 12]</li>
<li>스코빌 지수가 3인 음식과 5인 음식을 섞으면 음식의 스코빌 지수가 아래와 같이 됩니다.새로운 음식의 스코빌 지수 = 3 + (5 * 2) = 13가진 음식의 스코빌 지수 = [13, 9, 10, 12]</li>
</ol>
<p>모든 음식의 스코빌 지수가 7 이상이 되었고 이때 섞은 횟수는 2회입니다.</p>
<h2 id="풀이">풀이</h2>
<pre><code class="language-c">import heapq

def solution(scoville, K):
    answer = 0
    heapq.heapify(scoville)
    while len(scoville) &gt;= 2:
        s = heapq.heappop(scoville) + (2*heapq.heappop(scoville))
        heapq.heappush(scoville, s)
        answer += 1
        if scoville[0] &gt;= K:
            return answer
    return -1</code></pre>
<h2 id="힙-heap-특징">힙 (heap) 특징</h2>
<ul>
<li>heapq를 import하여 좀 더 간결하게 코드를 짤 수 있음</li>
<li>부모 노드가 자식 노드보다 무조건 값이 작아야한다.</li>
<li>최소값 혹은 최대값을 구하는 문제에서 많이 사용된다.</li>
<li>주어진 리스트를 heapq.heapifty()를 통해 정렬을 먼저 해줘야한다.</li>
<li>heapq.heappop()를 통해 최상단의 노드를 꺼내며 출력할 수 있다.</li>
<li>heapq.heappush()를 통해 새로운 노드를 추가할 수 있다.</li>
</ul>
<h2 id="순서">순서</h2>
<ol>
<li>heapq.heapify(scoville)을 통해 scoville을 정렬한다.</li>
<li>while문을 통해 scoville의 길이가 2이상인 경우에만 반복할 수 있게한다.</li>
<li>연산방법에 따라 제일 작은 노드와 그 다음 노드를 연산해준다.</li>
<li>연산된 값을 push해준다.</li>
<li>그만큼 answer(count)를 1씩 증가시킨다.</li>
<li>push된 scoville에서 제일 부모노드(제일 작은 노드)가 K보다 작다면 answer를 return 한다.</li>
<li>만약 이 조건이 맞지 않아 반복문을 탈출할경우 -1을 리턴한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 타겟넘버]]></title>
            <link>https://velog.io/@chs_0303/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%83%80%EA%B2%9F%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@chs_0303/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%83%80%EA%B2%9F%EB%84%98%EB%B2%84</guid>
            <pubDate>Wed, 10 Nov 2021 17:02:32 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>n개의 음이 아닌 정수가 있습니다. 이 수를 적절히 더하거나 빼서 타겟 넘버를 만들려고 합니다. 예를 들어 [1, 1, 1, 1, 1]로 숫자 3을 만들려면 다음 다섯 방법을 쓸 수 있습니다.</p>
<ul>
<li>`-1+1+1+1+1 = 3</li>
<li>1-1+1+1+1 = 3</li>
<li>1+1-1+1+1 = 3</li>
<li>1+1+1-1+1 = 3</li>
<li>1+1+1+1-1 = 3`</li>
</ul>
<p>사용할 수 있는 숫자가 담긴 배열 numbers, 타겟 넘버 target이 매개변수로 주어질 때 숫자를 적절히 더하고 빼서 타겟 넘버를 만드는 방법의 수를 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li>주어지는 숫자의 개수는 2개 이상 20개 이하입니다.</li>
<li>각 숫자는 1 이상 50 이하인 자연수입니다.</li>
<li>타겟 넘버는 1 이상 1000 이하인 자연수입니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>numbers</th>
<th>target</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[1,1,1,1]</td>
<td>3</td>
<td>5</td>
</tr>
</tbody></table>
<h2 id="풀이">풀이</h2>
<pre><code class="language-c">count = 0
def dfs(n, numbers, target): 
    global count
    if not numbers:
        if n == target:
            count += 1
    else:
        dfs(n+numbers[0], numbers[1:], target)
        dfs(n-numbers[0], numbers[1:], target)

def solution(numbers, target):
    dfs(0,numbers,target)
    answer = count
    return answer</code></pre>
<h2 id="dfs-와-bfs의-특징">DFS 와 BFS의 특징</h2>
<ul>
<li>트리로 문제를 구현해야하며 최하단의 모든 노드의 값들이 필요하기 때문에 입력되는 데이터의 크기가 크지 않다.</li>
<li>위와 같은 이유로 모든 값을 구하기 위해 재귀를 많이 사용함</li>
</ul>
<h2 id="순서">순서</h2>
<ol>
<li>전역 변수로 count를 선언해둔다.</li>
<li>dfs 함수에 number와 target과 함께 현재까지의 계산 결과를 저장할 n값을 같이 보내준다.</li>
<li>dfs함수 안에서 number가 없으면 조건을 넣어준다.(최하단 노드까지 도착했다는 뜻 = 모든 값들을 연산에 넣었다.)</li>
<li>그 상태에서 최종 노드값이 target값과 일치하면 count에 1을 더해준다.</li>
<li>만약 최종 노드까지 도달하지 못한 것이라면 값을 양수로 계산했을 경우 하나 음수로 계산했을 경우하나 이렇게 두가지의 값을 가지고 재귀함수를 실행한다.</li>
<li>재귀함수를 돌리기 전 n 값에는 현재 인덱스 위치의 값을 더해주거나 뺴주며, numbers 배열같은 경우는 현재 계산된 값을 제외한 다음 인덱스부터 끝까지의 배열을 다시 보내준다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[게시판 Restful API]]></title>
            <link>https://velog.io/@chs_0303/%EA%B2%8C%EC%8B%9C%ED%8C%90-Restful-API</link>
            <guid>https://velog.io/@chs_0303/%EA%B2%8C%EC%8B%9C%ED%8C%90-Restful-API</guid>
            <pubDate>Wed, 03 Nov 2021 09:02:10 GMT</pubDate>
            <description><![CDATA[<p>Aimmo 개발 과제로 게시판 Restful API를 작성해 보았다.</p>
<p>필수 조건은 아래와 같다.</p>
<ul>
<li>게시물 카테고리 및 필터 적용</li>
<li>게시글 검색 기능 추가</li>
<li>댓글 및 대댓글 기능 추가</li>
<li>게시물 조회수 구현</li>
<li>Unit Test</li>
<li>RestFul API</li>
<li>mongodb</li>
</ul>
<h3 id="팀원">팀원</h3>
<table>
<thead>
<tr>
<th align="center">Name</th>
<th>Git</th>
<th>Blog</th>
</tr>
</thead>
<tbody><tr>
<td align="center">김정수</td>
<td><a href="https://github.com/hollibleling">https://github.com/hollibleling</a></td>
<td><a href="https://velog.io/@hollibleling">https://velog.io/@hollibleling</a></td>
</tr>
<tr>
<td align="center">윤현묵</td>
<td><a href="https://github.com/fall031-muk">https://github.com/fall031-muk</a></td>
<td><a href="https://velog.io/@fall031">https://velog.io/@fall031</a></td>
</tr>
<tr>
<td align="center">최현수</td>
<td><a href="https://github.com/filola">https://github.com/filola</a></td>
<td><a href="https://velog.io/@chs_0303">https://velog.io/@chs_0303</a></td>
</tr>
</tbody></table>
<h3 id="개발기간">개발기간</h3>
<p>21.11.1 ~ 21.11.3</p>
<h3 id="기술스택">기술스택</h3>
<ul>
<li>python, django</li>
<li>mongoDB, djongo</li>
<li>AWS EC2</li>
<li>Slack, Git</li>
</ul>
<h3 id="endpoint">ENDPOINT</h3>
<table>
<thead>
<tr>
<th align="center">Method</th>
<th>endpoint</th>
<th>Request Header</th>
<th>Request Body</th>
<th>Remark</th>
</tr>
</thead>
<tbody><tr>
<td align="center">POST</td>
<td>/user/signup</td>
<td></td>
<td>name, nickname, email, password</td>
<td>회원가입</td>
</tr>
<tr>
<td align="center">POST</td>
<td>/user/login</td>
<td></td>
<td>email, password</td>
<td>로그인</td>
</tr>
<tr>
<td align="center">POST</td>
<td>/post/list</td>
<td>Authorization</td>
<td>title, body, category</td>
<td>게시물 작성</td>
</tr>
<tr>
<td align="center">GET</td>
<td>/post/detail/<a href="int:post_id">int:post_id</a></td>
<td>Authorization, cookie</td>
<td></td>
<td>게시물 조회 및 댓글 조회</td>
</tr>
<tr>
<td align="center">DELETE</td>
<td>/post/detail/<a href="int:post_id">int:post_id</a></td>
<td>Authorization</td>
<td></td>
<td>게시물 삭제</td>
</tr>
<tr>
<td align="center">PUT</td>
<td>/post/detail/<a href="int:post_id">int:post_id</a></td>
<td>Authorization</td>
<td>title, body, category</td>
<td>게시물 수정</td>
</tr>
<tr>
<td align="center">GET</td>
<td>/post/list?page=</td>
<td></td>
<td></td>
<td>게시물 목록 조회</td>
</tr>
<tr>
<td align="center">GET</td>
<td>/post/list?category=</td>
<td></td>
<td></td>
<td>게시물 카테고리 필터</td>
</tr>
<tr>
<td align="center">POST</td>
<td>/post/<a href="int:post_id">int:post_id</a>/comments</td>
<td></td>
<td>content, parent_comment_id</td>
<td>댓글/대댓글 작성</td>
</tr>
<tr>
<td align="center">GET</td>
<td>/post/<a href="int:post_id">int:post_id</a>/comments?limit=&amp;offset=&amp;comment_id=</td>
<td></td>
<td></td>
<td>대댓글 조회</td>
</tr>
<tr>
<td align="center">PATCH</td>
<td>/post/<a href="int:post_id">int:post_id</a>/comments</td>
<td></td>
<td>comment_id</td>
<td>댓글/대댓글 수정</td>
</tr>
<tr>
<td align="center">DELETE</td>
<td>/post/<a href="int:post_id">int:post_id</a>/comments?comment_id=</td>
<td></td>
<td></td>
<td>댓글/대댓글 삭제</td>
</tr>
</tbody></table>
<h3 id="개발">개발</h3>
<p>초기 셋팅 및 기본 게시판 CRUD구현은 기존에 pre-onboarding 지원과제로 제출했던 git을 클론해와서 진행하였다.</p>
<p>필수 요청 사항 중, 기술적으로 3가지로 나눠 각자 하나씩 맡아 진행하였다.
그 중 내가 맡은 부분은 <strong>게시물 카테고리 적용</strong>과 <strong>게시물 조회수 구현</strong> 이였다.</p>
<h3 id="mongodb">mongodb</h3>
<p>django와 mongodb를 연결하기 위해 <strong>djongo</strong> 모듈을 사용하여 진행하였다.
djongo는 django의 ORM을 지원해주는 모델이다.
연결을 위해 아래의 강의를 보며 학습했다.</p>
<p><a href="https://www.youtube.com/watch?v=FLZYLog369s">Using Django with MongoDB | MongoDB + Django CRUD API</a></p>
<p>또한 mongodb를 좀 더 쉽게 사용하기 위해 GUI환경인 MongoDB Compass를 사용하였다.</p>
<p>MongoDB Compass<img src="https://images.velog.io/images/chs_0303/post/168cf089-bf87-4749-b6ca-1111e13ecba4/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.11.35.png" alt=""></p>
<h3 id="🏷-게시물-카테고리">🏷 게시물 카테고리</h3>
<p>카테고리는 RDBMS로 보았을 때 게시물과 1:M 관계로 모델링을 추가하고 카테고리가 출력 되어져야 하는 부분에 각각 데이터를 받아와주었다.</p>
<pre><code># models.py
class Post(TimeStamp):
   ...
   category = models.ForeignKey(&quot;Category&quot;, on_delete=models.CASCADE)
   ...

 class Category(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        db_table=&quot;categories&quot;</code></pre><p>또한 카테고리를 통한 필터를 가능하게 만들어 필터에 대한 정보가 주어졌을 때 그에 맞는 카테고리를 가지고 있는 게시글들만 출력하게 구현하였다.</p>
<pre><code># view.py
class ListView(View):
    def get(self, request):
    if category:
            category_id = Category.objects.get(name=category).id
            posts = Post.objects.filter(category_id=category_id).order_by(&#39;-id&#39;)
        else:
            posts = Post.objects.all().order_by(&#39;-id&#39;)</code></pre><p>또한 전체 게시글을 출력할 때 pagination을 통해 출력되는 게시글의 개수를 정해줬었는데 이 또한 동일하게 구현하였다.</p>
<pre><code># view.py
class ListView(View):
    def get(self, request):
        try:
            page       = request.GET.get(&quot;page&quot;)
            page       = int(page or 1)
            page_size  = 10
            limit      = page_size * page 
            offset     = limit - page_size
        ...
        result = [{
                &quot;count&quot; : len(posts)-(offset+i),
                &quot;title&quot; : post.title,
                &quot;hit&quot; : post.hit,
                &quot;body&quot; : post.body,
                &quot;nickname&quot; : post.user.nickname
            } for i,post in enumerate(posts [offset:limit])]</code></pre><blockquote>
<p>데이터를 가져올 때 게시글 앞에 번호를 매겨주고 싶었는데 데이터를 hard delete로 지울 경우 id값이 밀리는 현상이 생겨 고민을 하다가 전체 게시글의 수에서 offset과 반복되는 인덱스만큼의 값을 빼줘 중간에 데이터가 비어있다해도 번호가 이어지게 만들었다.</p>
</blockquote>
<h3 id="🏷-게시물-조회수-구현">🏷 게시물 조회수 구현</h3>
<p>기존에 구현한 조회수 같은 경우 get으로 게시글이 불러오질 때마다 hit을 1씩 추가시켜 저장을 하여 구현했었다.</p>
<pre><code>post.hit += 1
post.save()</code></pre><p>하지만 위와 같이 구현할 경우 한 사용자가 중복으로 조회수를 올릴 수 있는 문제 점이 존재하였다.
이에 관하여 찾아보니 3가지 방법이 있었다.</p>
<blockquote>
<ol>
<li>session</li>
<li>ip</li>
<li>cookie</li>
</ol>
</blockquote>
<p>세 가지 방식 중 가장 많이 사용되는 방식이 <strong>cookie</strong> 를 통하여 조회수를 관리하는 방법이였다. 기존 방식에는 cookie 만료 시간을 주어 지정한 시간이 지나면 자동으로 쿠키가 삭제되는 방식이였다. 하지만 이번 과제에는 &quot;중복된 user가 조회수를 올릴 수 없게 구현&quot; 이라 서술되어 있어 만료시간을 주지 않았다. </p>
<pre><code>class PostView(View):
    @transaction.atomic
    @login_decorator
    def get(self, request,post_id):
        try:
            post = Post.objects.get(id=post_id)

            ...

            response = JsonResponse({&quot;Result&quot; : result, &quot;Comment&quot; : Result_comment}, status=200)

            if request.COOKIES.get(&#39;hit&#39;):
                cookies = request.COOKIES.get(&#39;hit&#39;)
                cookies_list = cookies.split(&#39;|&#39;)
                if str(post.id) not in cookies_list:

                    post.hit += 1
                    post.save()

                    result[&quot;hit&quot;] = post.hit
                    response = JsonResponse({&quot;Result&quot; : result, &quot;Comment&quot; : Result_comment}, status=200)

                    response.set_cookie(&#39;hit&#39;, cookies+f&#39;|{post.id}&#39;, expires=None)
            else:
                post.hit += 1
                post.save()

                result[&quot;hit&quot;] = post.hit
                response = JsonResponse({&quot;Result&quot; : result, &quot;Comment&quot; : Result_comment}, status=200)

                response.set_cookie(&#39;hit&#39;, post.id, expires=None)
            return response

        except Post.DoesNotExist:
            return JsonResponse({&quot;message&quot; : &quot;POSTS_NOT_FOUND&quot;}, status=404)</code></pre><ol>
<li>첫번째 조건문을 통해 cookie에 hit이 존재하는지를 확인한다. 만약 없을 경우 바로 조회수를 &#39;1&#39; 추가 해주고 return 한다.</li>
<li>존재할 경우 쿠키를 &#39;|&#39;를 기준으로 나누어준다.(cookie hit=1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1)</li>
<li>게시물의 id가 cookie_list에 존재하지 않을 경우 &#39;1&#39; 추가해준다.</li>
</ol>
<h3 id="🏷-아쉬운-점">🏷 아쉬운 점</h3>
<ol>
<li>IP를 통해 접속기록을 저장하여 하는 방식도 구현해보고 싶었는데 어렵고 이를 습득하여 적용하기엔 시간이 부족하였다.</li>
<li>mongodb 즉, NoSQL을 사용하는데 이를 재대로 활용하기에는 시간이 많이 모자랐다. 하지만 기존에 사용하던 RDBMS만 사용해서 구현을 하다 NoSQL에 관해서 보았는데 굉장히 흥미로웠다. 어떤 점에서 보면 좀 더 가용성이 좋아보이기도 하고 재밌어보였다.</li>
<li>게시물 상세 get 함수 상단에 &quot;@transaction.atomic&quot;이 있는데 이는 쿠키에 저장하기 위해 트랜젝션을 써주었다. 하지만 지금 생각해보니 굳이 함수 전체를 원자성으로 묶을 필요가 있었을까 싶다. 쿠키에 저장하는 부분만 with을 통해 원자성을 부여했다면 더 좋은 코드가 되었을 것 같다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS-EC2]]></title>
            <link>https://velog.io/@chs_0303/AWS-EC2</link>
            <guid>https://velog.io/@chs_0303/AWS-EC2</guid>
            <pubDate>Sun, 12 Sep 2021 14:42:05 GMT</pubDate>
            <description><![CDATA[<p>클론 프로젝트를 하다 2~3년 전에 공부했었던 AWS를 이용하여 배포를 하게되었다. 이에 대해 정리하고자 글을 쓴다.</p>
<h3 id="ec2">EC2</h3>
<p>Amazon Elastic Compute Cloud의 줄임말로써 공식 문서에는 클라우드에서 확장 가능 컴퓨팅 용량을 제공한다라고 써져있다. 또한 Amazon EC2를 사용하면 하드웨어에 선투자할 필요가 없어 더 빠르게 애플리케이션을 개발하고 배포할 수 있습니다. Amazon EC2를 사용하여 원하는 수의 가상 서버를 구축하고 보안 및 네트워킹을 구성하며 스토리지를 관리할 수 있습니다. Amazon EC2에서는 확장 또는 축소를 통해 요구 사항 변경 또는 사용량 스파이크를 처리할 수 있으므로 트래픽을 예측할 필요성이 줄어듭니다라고 작성되어있다. 간단하게 말해 서버를 제공해준다라고 할 수 있을꺼 같다. 내 pc로 항상 서버를 켜둘 수 없으니 서버를 빌려줄테니 이걸로 자신만의 서버를 돌려봐라는 것이다. 이제 사용법에 대해 간단하게 알아보자.</p>
<h3 id="단계-1-amazon-machine-imageami-선택">단계 1: Amazon Machine Image(AMI) 선택</h3>
<p>인스턴스를 시작하는데 필요한 운영 체제를 선택하는 부분이다. 프리티어를 사용하는 사람입장에서는 다른 선택보다는 우분투 서버를 이용하는것이 가장 좋은 것 같다.
<img src="https://images.velog.io/images/chs_0303/post/18e4ef3e-617e-4eb7-b74b-6e58d17ddfcf/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.20.51.png" alt=""></p>
<h3 id="단계-2-인스턴스-유형-선택">단계 2: 인스턴스 유형 선택</h3>
<p>인스턴스의 유형을 선택하는 부분이다. CPU, 메모리, 스토리지 및 네트워킹 등 다양한 조합이 있으나 이전과 마찬가지로 프리티어는 선택지가 하나밖에 없다..
<img src="https://images.velog.io/images/chs_0303/post/f1899ed7-af8b-486f-87ce-d14a35509c19/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.24.31.png" alt=""></p>
<h3 id="단계-3-인스턴스-세부-정보-구성">단계 3: 인스턴스 세부 정보 구성</h3>
<p>인스턴스를 구성하는 부분에서 가장 중요한 설정인거 같다. VPC를 생성해서 네트워크를 설정해주거나 private 서브넷과 public서브넷을 생성하여 설정해주거나 우발적인 종료로부터 보호등 많은 설정등이 가능하다 하지만 가장 기본적인 부분만을 사용하기 위해 지금은 기본값으로 설정해주겠다.
<img src="https://images.velog.io/images/chs_0303/post/80174963-3b0e-416d-a7a6-97d8ec782035/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.28.46.png" alt=""></p>
<h3 id="단계-4-스토리지-추가">단계 4: 스토리지 추가</h3>
<p>인스턴스의 디바이스 설정을 하는 부분이다. 서버의 용량이나 SSD등 설정을 하는 부분이다. 크기 부분을 함부로 수정했다가는 요금폭탄을 맞을 수 있으니 조심하자.
<img src="https://images.velog.io/images/chs_0303/post/6795004a-0e0d-4b14-868b-e8e93c5e0cdf/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.32.39.png" alt=""></p>
<h3 id="단계-5-태그-추가">단계 5: 태그 추가</h3>
<p>태그를 추가하는 부분이다. 이 부분은 지금까지 해오면서 특별히 어떠한 기능을 하는 것 같진 않는다. 하지만 나중에 따로 알아보고 정리할 예정이다.
<img src="https://images.velog.io/images/chs_0303/post/957ce8c7-2d4a-44d7-a096-10924bfec112/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.33.07.png" alt=""></p>
<h3 id="단계-6-보안-그룹-구성">단계 6: 보안 그룹 구성</h3>
<p>보안 그룹은 인스턴스이 트래픽을 제어하는 방화벽을 정하는 부분이다. 쉽게 말하면 어떠한 프로토콜들이 이 서버에 접속이 가능한지 지정하는 부분이다. 이 부분에서 소스에는 종류가 3가지가 존재하는데 사용자 지정, 위치 무관, 내 IP이렇게 존재한다. 차례대로 내가 지정한 아이피만, 누구든지, 나만 이러한 뜻이다.
<img src="https://images.velog.io/images/chs_0303/post/6bb23a8e-1dff-46f6-ac1a-3fe643a82750/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.38.29.png" alt=""></p>
<h3 id="단계-7-인스턴스-시작-검토">단계 7: 인스턴스 시작 검토</h3>
<p>이제 지금까지 작성한 부분에 대해 검토를 하는 부분이다. 지금까지의 인스턴스 설정이 맞는지 확인을 하고 시작하기를 누르면 아래와 같은 화면이 나온다.
<img src="https://images.velog.io/images/chs_0303/post/3a91374a-d166-4324-bdf0-e9a9f9099a28/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-09-12%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.39.49.png" alt="">
이 부분에서 암호화?를 한다고 생각하면 되는데 기존에 존재하지 않으면 새로 키 페어를 생성해주고 키 페어를 다운받으면 된다. 그러면 pem형식의 파일이 다운로드되는데 이것이 키페어 파일이다. 이 파일이 없으면 이 서버에는 더 이상 접근을 할 수가 없다. </p>
]]></description>
        </item>
    </channel>
</rss>