<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>채록.log</title>
        <link>https://velog.io/</link>
        <description>🍎 🍊 🍋 🍏 🍇</description>
        <lastBuildDate>Mon, 21 Aug 2023 06:54:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>채록.log</title>
            <url>https://images.velog.io/images/c_hyun403/profile/6944b1a7-e24b-4f4e-b844-26cc200c983f/순돌이랑나.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 채록.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/c_hyun403" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[S3] 퍼블릭 객체의 403 에러]]></title>
            <link>https://velog.io/@c_hyun403/S3-%ED%8D%BC%EB%B8%94%EB%A6%AD-%EA%B0%9D%EC%B2%B4%EC%9D%98-403-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@c_hyun403/S3-%ED%8D%BC%EB%B8%94%EB%A6%AD-%EA%B0%9D%EC%B2%B4%EC%9D%98-403-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Mon, 21 Aug 2023 06:54:27 GMT</pubDate>
            <description><![CDATA[<p>업무를 하던 중 요상한 버그가 발견되었다.
퍼블릭으로 &quot;읽기&quot;가 권한이 설정된 S3 버킷 내 이미지를 외부 api 보냈을때, 외부 사이트에서 우리쪽 S3이미지를 조회할때 403 에러가 난다는것이다.</p>
<p>언제부터 해당 이슈가 발생했는지는 파악이 어려우나, 갈수록 이슈 발생률이 높아져 상품등록 10개중 9개가 실패하는 지경에 이르렀다.</p>
<p>간헐적으로 성공하기도 하여, 우리쪽 문제가 아니라 판단했지만 이슈의 발생 정도가 너무 빈번하여 우리쪽 유저의 불편사항이 날로 커지고 있어서 원인 파악에 착수했다!</p>
<blockquote>
<p>우리쪽 문제가 아니지만,
상대쪽 스펙으로 인해 우리쪽 버그가 되는 경우...........</p>
</blockquote>
<h1 id="1-s3-액세스-로그-활성화">1. S3 액세스 로그 활성화</h1>
<p>먼저 해당 S3이미지 접근시 상황을 파악하기 위해 S3 액세스 로그를 살펴보고자 한다.</p>
<p>그러나 활성화가 되어있지 않다!</p>
<p>먼저 S3호출시 상황을 파악하기 위해 &quot;액세스 로그&quot; 활성화를 지정한다.
// 활성화 하는 법</p>
<h1 id="2-aws-athena-사용">2. AWS athena 사용</h1>
<p>액세스 로그가 저장되는 버킷의 내용을 살펴보려고 하는데..
이거 일일히 하나하나 열람하는게 영 보통일이 아니다.</p>
<p>보다 효율적으로 저장된 로그를 파악하는 방법을 찾아보다가 aws 공식문서에서 &quot;athena&quot;를 지원함을 알수 있었다.</p>
<h1 id="3-공백이-포함된-s3-객체명">3. 공백이 포함된 S3 객체명</h1>
<p>객체 자체에는 문제가 없음이 확실하기에, 동일한 객체인데도 어떨때 403 에러가 발생하는지 찾아보았다.
aws 공식문서에 따르면 객체의 이름이 정확하지 않을때 404 에러가 아닌 403 에러가 반환된다고 한다.
// 403 에러난다는 문서 캡쳐본</p>
<p>실제로 postman을 통해 객체명 뒤에 &quot;공백&quot;을 하나 추가하여 호출하니 Access Denied가 발생했다.</p>
<p>오류가 발생한 외부 api는 json 형태로 데이터 처리 후, xml형태로 변환하여 utf-8로 인코딩 하여 byte 형태로 데이터를 전송하고 있다.
즉, byte 형태의 데이터를 해당 외부 사이트에서 가공하여 다시 우리쪽 s3이미지를 호출할때, 가비지 캐릭터가 추가된건 아닐까 하는 의심이 생겼다.</p>
<p>간헐적으로 통신요청이 성공할때와 그렇지 않을때 요청보낸 데이터에 어떠한 차이가 있는지 파악이 필요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] swagger 적용하기]]></title>
            <link>https://velog.io/@c_hyun403/Spring-swagger-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/Spring-swagger-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 02 Nov 2022 06:28:42 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[[Spring] 프로젝트 생성하기]]></title>
            <link>https://velog.io/@c_hyun403/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 02 Nov 2022 05:54:51 GMT</pubDate>
            <description><![CDATA[<p>메인 프로젝트 어드민 사이트를 별도로 생성해야 하는데 어차피 백엔드는 나 혼자니까 내가 해보고싶은거 다할꺼다.! - 무덤파기 - </p>
<blockquote>
</blockquote>
<ul>
<li>java</li>
<li>spring boot</li>
<li>ningx, ec2</li>
<li>mongodb</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Golang] mvc pattern + mongodb]]></title>
            <link>https://velog.io/@c_hyun403/Golang-mvc-pattern-mongodb</link>
            <guid>https://velog.io/@c_hyun403/Golang-mvc-pattern-mongodb</guid>
            <pubDate>Sun, 29 May 2022 11:56:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>controller -&gt; service -&gt; repository 구조로 mongodb에 새로운 document를 insert하고, read, update 진행하기</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[소셜연동] 인스타그램 그래프 API 연동하기]]></title>
            <link>https://velog.io/@c_hyun403/%EC%86%8C%EC%85%9C%EC%97%B0%EB%8F%99-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B7%B8%EB%9E%98%ED%94%84-API-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/%EC%86%8C%EC%85%9C%EC%97%B0%EB%8F%99-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B7%B8%EB%9E%98%ED%94%84-API-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 29 May 2022 11:43:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>언어 : golang
소셜 : Facebook (Instagram Graph API 사용)
기타 스펙 : API Gateway, AWS Labmda</p>
<p>어려워 죽는줄</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[소셜연동] NAVER, GOOGLE 로그인 하기]]></title>
            <link>https://velog.io/@c_hyun403/%EC%86%8C%EC%85%9C%EC%97%B0%EB%8F%99-NAVER-GOOGLE-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/%EC%86%8C%EC%85%9C%EC%97%B0%EB%8F%99-NAVER-GOOGLE-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 29 May 2022 11:42:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>언어 : golang, python
소셜 : NAVER, GOOGLE ( Youtube Data API 사용)
기타 스펙 : API gateway, AWS Lambda</p>
<p>언어가 두개인 이유는.. python으로 다 해놨는데 golang으로 다시 하라고 했기 때문이지.......</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] S3에 xml 업로드 하기]]></title>
            <link>https://velog.io/@c_hyun403/AWS-S3%EC%97%90-xml-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/AWS-S3%EC%97%90-xml-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 03 Nov 2021 14:15:25 GMT</pubDate>
            <description><![CDATA[<p>지난번 글 <a href="https://velog.io/@c_hyun403/AWS-S3%EB%A1%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C">S3로 이미지 업로드</a> 에 이어서 이번에는 <strong>S3에 xml업로드 하기</strong>이다.</p>
<hr>
<br>

<p>들은바에 의하면 (?) 옛날 동작 방식으로 데이터를 xml형식으로 만들어 해당 데이터가 저장된 url로 api 통신을 하는 방법도 있다고 한다. 이번에 이런 방식을 사용해 데이터를 주고 받아야 하는 일이 있어서 지난번과 마찬가지로 버킷을 생성하고 정책설정을 해서 퍼블릭 접근이 가능하도록 하였다.</p>
<br>

<h1 id="iam">IAM</h1>
<p>S3에 업로드, 다운로드, 읽기 등 모든 권한을 가진 역할자를 하나 만들어준다.</p>
<h1 id="버킷-생성하기">버킷 생성하기</h1>
<p>버킷의 이름은 &quot;test-for-xml-just-text&quot;로 하였다. region도 아시아/서울로.
앞서 말했듯 퍼블릭 접근이 가능해야 하기 때문에 버킷을 생성하면서 <strong>&quot;이 버킷의 퍼블릭 액세스 차단 설정&quot;</strong> 에 <strong>모든 퍼블릭 액세스 차단</strong>을 해제해 주었다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/5d21eb59-6ed7-4564-8fc0-d95780b0e99e/%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%2010.54.53.png" alt=""></p>
<p>다른건 건드릴것이 없어서 바로 생성하였다.</p>
<br>

<h2 id="1-버킷-정책-설정">1. 버킷 정책 설정</h2>
<p>이 버킷에 대한 정책을 설정한다. 버킷에 xml을 업로드하고, 업로드된 xml을 자유롭게 읽어올수 있어야 하기 때문이다.</p>
<br>

<p>&quot;정책 생성기&quot;를 통해 먼저 생성할 정책의 상태를 선언한다.
정확하게 사용할 actions 지정해도 되지만 번거로우므로 s3에 대한 모든 action들을 허용하도록 했다. 그리고 ARN에는 생성된 버킷의 ARN을 입력하면 된다.</p>
<p>설정한 후 &quot;Add statement&quot;를 누르면 정책json 예제가 생성되고 &quot;Generate Policy&quot;로 정책 내용을 확인하여 복사해둔다.</p>
<pre><code>{
  &quot;Id&quot;: &quot;{정책번호}&quot;,
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;{생성된 statement의 ID}&quot;,
      &quot;Action&quot;: &quot;s3:*&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Resource&quot;: &quot;arn:aws:s3:::test-for-xml-just-text&quot;,
      &quot;Principal&quot;: &quot;*&quot;
    }
  ]
}</code></pre><p>그리고 나서 버킷 정책 편집창에 붙여넣고 저장한다.
이때 에러가 난다면 에러 메세지를 잘 보고 해결하도록 한다!</p>
<br>

<h2 id="2-cors-설정">2. CORS 설정</h2>
<p>나의 경우 외부 사이트에서도 해당 객체 url을 호출하기 때문에 CORS 정책도 설정해야 한다.
모든 요청을 다 허용해 주면 된다.!</p>
<pre><code>[
    {
        &quot;AllowedHeaders&quot;: [
            &quot;*&quot;
        ],
        &quot;AllowedMethods&quot;: [
            &quot;GET&quot;,
            &quot;PUT&quot;,
            &quot;POST&quot;,
            &quot;HEAD&quot;
        ],
        &quot;AllowedOrigins&quot;: [
            &quot;*&quot;,
            &quot;http://localhost:*&quot;
        ],
        &quot;ExposeHeaders&quot;: [
            &quot;ETag&quot;
        ],
        &quot;MaxAgeSeconds&quot;: 3000
    }</code></pre><br>


<h1 id="객체-업로드-하기">객체 업로드 하기</h1>
<p>먼저 AWS UI를 통해 객체를 업로드 해본다. 이때 업로드 하는 파일 목록 아래에 보면 &quot;권한&quot; 설정 탭이 있다. 여기서 &quot;미리 정의된 ACL&quot;에 프라이빗 으로 하면 url 호출 시 &quot;Access denied&quot;가 뜬다.</p>
<h3 id="--프라이빗-설정-시">- 프라이빗 설정 시</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/9b753b39-f7f7-45b2-b860-19aa09c56a1d/%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%2011.08.32.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/a217f5a6-6267-4bd2-b0fe-79a7069b4b72/%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%2011.10.48.png" alt=""></p>
<p>따라서 &quot;퍼블릭 읽기 액세스 권한 부여&quot; 설정을 해준다.</p>
<h3 id="--퍼블릭-설정-시">- 퍼블릭 설정 시</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/98c9f39a-6edb-4cd7-9c3c-5fa641b53e93/%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%2011.11.36.png" alt=""></p>
<p>그러면 업로드 된 파일의 객체 url을 호출하면 내용이 정상적으로 보인다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/3fb9972e-73e8-4920-ab86-297f35b6dc21/%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%2011.12.34.png" alt=""></p>
<p>이제 python 로직을 통해 객체 업로드를 진행해 보자!</p>
<br>

<h1 id="xml-업로드-endpoint-작성">xml 업로드 endpoint 작성</h1>
<p>python 프로젝트로 작성하려고 한다. 이때 데이터를 구성할때부터 xml 형식으로하면 매우 까다로우므로 일단 dict 형식으로 작성후 xml로 converting 해주도록 한다.
이때 필요한 모듈은 &quot;xmltodict&quot;이다.</p>
<pre><code class="language-python">import boto3
import xmltodict

upload_dict = {
  &quot;data&quot;: {
    &quot;goods_data&quot;: {
    &quot;goodsNm&quot;: &quot;testgoods&quot;,
    &quot;goodsprice&quot;: &quot;1000&quot;
    }
  }
}

def convert_xml_to_dict(target):
    return loads(dumps(target))

upload_xml = convert_xml_to_dict(target=xmltodict.parse(upload_dict))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] EC2 구성요소]]></title>
            <link>https://velog.io/@c_hyun403/EC2</link>
            <guid>https://velog.io/@c_hyun403/EC2</guid>
            <pubDate>Tue, 03 Aug 2021 14:11:30 GMT</pubDate>
            <description><![CDATA[<p>회사 스터디 첫번째 주제는 AWS이다. 진행하는 책은 <strong>&quot;그림으로 이해하는 AWS구조와 기술&quot;</strong>이라는 책으로 컬러로 되어있어 이해가 편하고 넉넉히 하루만에도 다 읽을수 있을만큼 쉽게 풀어진 책이다.</p>
<p>스터디이기 때문에 각자 파트를 맡아 세미나 형식으로 진행하기로 했고 내가 맡은 부분은 AWS의 여러 도구와 EC2에 대한 챕터이다. 때문에 EC2에 대한 내용을 정리하면서 스터디를 준비하고자 한다.</p>
<hr>
<h1 id="1-ec2-">1. EC2 ?</h1>
<blockquote>
<p>*<em>EC2 ?? *</em>
<code>Elastic Compute Cloud</code>의 줄임말. C가 두번 들어가서 EC2이다.
컴퓨팅 용량을 제공하는 서비스로 <code>서버에 필요한 세트를 Cloud에서 빌릴수 있게 해준다.</code></p>
</blockquote>
<p>그리고 EC2는 managed service가 아니기 때문에 <strong>서버 및 네트워크 운영은 AWS가 담당</strong>하지만 그 외 <strong>OS를 포함한 소프트웨어는 사용자가 직접 설치하고 운영해야 한다.</strong></p>
<br>

<h3 id="-aws-managed-service">+) AWS managed service</h3>
<p>AWS에서 제공하는 서비스들은 크게 두 종류로 나눌수 있다. <code>managed service</code>인지 아닌지가 그 기준이다.</p>
<blockquote>
<p><strong>managed service ??</strong>
<code>AWS가 관리하는 서비스</code>의 통칭
ex) Amazon S3, Amazon RDS</p>
</blockquote>
<p>매니지드 서비스는 백업 및 업데이트가 <strong>자동</strong>으로 이뤄져 관리 부담이 줄어든다. S3의 경우 상황에 맞게 storage 용량이 자동으로 증가한다.
하지만 <strong>자동</strong>으로 이뤄지기때문에 업데이트 하고싶지 않은 소프트웨어가 업데이트 되거나 필요 이상으로 용량이 커져서 과한 요금이 부과될 수 있기 대문에 완전히 신경을 쓰지 않을 수는 없다.</p>
<h4 id="--매니지드-서비스의-기능">- 매니지드 서비스의 기능</h4>
<ul>
<li>자동 백업</li>
<li>자동 알림</li>
<li>모니터링</li>
<li>패치 관리</li>
<li>보안</li>
<li>이중화 : 같은 시스템을 여러개 준비해 장애시 서비스가 완전히 멈추는 것을 방지해 주는 것.</li>
</ul>
<br>

<h2 id="--ec2의-장점">- EC2의 장점</h2>
<p>EC2는 Cloud 서비스이다. 즉, 어디서든지 EC2에 접근이 가능하고, 가상화 기술로 백업이 용이하며, AWS의 여러 리전과 가용영역을 활용하여 물리적으로 다른 여러 장소에 서버를 생성할 수 있다.</p>
<p>또한 EC2의 관리 콘솔 화면을 통해 클릭한번으로 생성, 삭제, 중지 등 여러가지 조작이 가능하다. 기능 조작이 즉각적이므로 장애에 대비해 즉각적인 대응도 가능하다.</p>
<ul>
<li>고장 =&gt; 서버 복제, 복구</li>
<li>부하 증가 =&gt; 서버 복제, 분산</li>
<li>접속량 감소 =&gt; 스케일 다운</li>
<li>일시 사용 =&gt; 잠깐동안만 EC2를 사용</li>
</ul>
<br>

<h2 id="--ec2-사용하기">- EC2 사용하기</h2>
<p>EC2를 생성하기 위해서는 <strong>인스턴스 유형</strong>, <strong>OS 종류</strong>, <strong>소프트웨어 설치 유무</strong>, <strong>AMI 종류</strong> 등 여러가지를 선택해야 한다. 역시 AWS에서 다양한 유형을 제공해 준다.</p>
<br>

<h3 id="1-운영">1) 운영</h3>
<p>EC2가 아닌 직접 서버를 구축한다면 물리적인 환경을 조성하기 위해 현지에 가서 케이블을 연결해야 한다. 하지만 EC2를 사용하면 <strong>관리 콘솔</strong>을 통해 이를 대신할 수 있다. 또한 소프트웨어적인 작업을 진행할 때에는 SSH를 이용해 원격으로 로그인하여 조작할 수 있다.</p>
<blockquote>
<p>** SSH ?? **
<code>Secure Shell Protocol</code>의 줄임말로 네트워크 프로토콜 중에 하나이며 서버를 원격에서 작업하기 위한 프로토콜이다.원격제어 뿐만이 아니라 데이터를 전송할 때에도 사용되며 <code>FTP</code>나 <code>Telnet</code> 보다 <strong>보안</strong>적인 면에서 더욱 우수하다.
<br>
<strong>SSH의 보안</strong>
SSH의 보안은 한쌍의 key로 구성되어 있다. 바로 <code>public key</code>와 <code>private key</code>이다.
(이전에 잠깐 봤던 SSL과 마찬가지. 두가지는 다르다.) =&gt; <a href="https://velog.io/@c_hyun403/HTTP%EC%99%80-SSL#ssl-%EC%97%90%EC%84%9C%EB%8A%94-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%95%94%ED%98%B8%ED%99%94-%ED%95%B4%EC%84%9C-%EC%A0%84%EC%86%A1%ED%95%9C%EB%8B%A4"> HTTP와 SSL </a>
<code>public key</code>를 이용해 &quot;암호화&quot;는 가능하되 &quot;복호화&quot;는 불가능 하다. 때문에 정보를 받는 입장에서는 <code>private key</code>를 이용해 정보를 복호화 하여야 한다.
<br>
따라서 private key를 처음 발급 받으면 사용자는 이를 컴퓨터에 꼭 저장해야 한다. (두번 발급이 안된다.)</p>
</blockquote>
<br>

<h3 id="2-서비스-기능">2) 서비스 기능</h3>
<p>EC2 서비스에는 다양한 기능이 있다.</p>
<table>
<thead>
<tr>
<th align="center">항목</th>
<th align="center">내용</th>
</tr>
</thead>
<tbody><tr>
<td align="center">인스턴스</td>
<td align="center">AWS 클라우드 생성한 가상 서버. 같은 구성으로 이뤄진 서로 다른 인스턴스가 존재할 수 있다.</td>
</tr>
<tr>
<td align="center">AMI</td>
<td align="center">가상 이미지. 인스턴스를 생성하는 기준이 되는 금형.</td>
</tr>
<tr>
<td align="center">키 페어</td>
<td align="center">인스턴스에 접속할 때 인증을 위해 사용되는 키 (SSH)</td>
</tr>
<tr>
<td align="center">EBS</td>
<td align="center">AWS 클라우드에 사용할 수 있는 storage. 인스턴스 스토리지로 사용한다.</td>
</tr>
<tr>
<td align="center">보안 그룹</td>
<td align="center">가상 방화벽. 1개 이상의 인스턴스 트래픽을 제한한다.</td>
</tr>
<tr>
<td align="center">Elastic IP</td>
<td align="center">정적 IPv4 주소</td>
</tr>
</tbody></table>
<blockquote>
<p>** AMI ?? **
<code>Amazon Machine Image</code>의 줄임말로 Amazon EC2안에 가상 머신을 생성하기 위해 사용되는 특수한 유형의 <strong>가상 어플라이언스</strong> 이며 EC2를 사용하여 배포하는 서비스의 디플레이의 기본 단위 역할을 한다.
<br>
** AMI의 주요 구성 요소**
모든 가상 어플라이언스와 마찬가지로 운영체제(리눅스, 마이크로소프트 윈도우 등)와 서비스 전달에 필요한 추가 소프트웨어를 포함하는 읽기 전용 파일 시스템 이미지로 구성되어 있다.
<a href="https://ko.wikipedia.org/wiki/%EC%95%84%EB%A7%88%EC%A1%B4_%EB%A8%B8%EC%8B%A0_%EC%9D%B4%EB%AF%B8%EC%A7%80"> Wiki - AMI </a></p>
</blockquote>
<br>

<blockquote>
<p><strong>IPv4 ??</strong>
요즘 사용되는 정적 주소는 IPv4와 IPv6 두종류가 있다.
IPv4는 4개로 구분된 10진수의 숫자로 표시되며 생성되는 수의 종류는 약 43억개이다. 하지만 점점 잔여 수가 부족해지고 있어 현재는 IPv6가 대체되고 있다. IPv6는 8개로 구분되어 16진수로 표현되 약 340간개를 사용할 수 있다.</p>
</blockquote>
<hr>
<h3 id="3-사용-절차">3) 사용 절차</h3>
<blockquote>
<ol>
<li>AWS로그인 : 리전을 선택하고 관리 콘솔을 열어 EC2 대시보드를 선택</li>
<li>EC2 인스턴스 생성 : AMI와 인스턴스 유형 선택 / 네트워크와 IP주소 할당 / 스토리지 추가 / 보안그룹 설정 / 키페어 선택(작성)</li>
<li>EC2 접속 : SSH로 EC2에 접속</li>
<li>소프으웨어 설치 &amp; 설정 (SSH 사용)</li>
</ol>
</blockquote>
<br>

<h4 id="--인스턴스-설정-항목">- 인스턴스 설정 항목</h4>
<table>
<thead>
<tr>
<th align="center">항목</th>
<th align="left">내용</th>
</tr>
</thead>
<tbody><tr>
<td align="center">AMI</td>
<td align="left">사용할 OS, 소프트웨어 등 어떤 인스턴스를 만들 것인지 정하고 이에 해당하는 AMI 선택</td>
</tr>
<tr>
<td align="center">인스턴스 유형</td>
<td align="left">EC2 인스턴스의 장비 사양. CPU, 메모리, 장비 유형 결정</td>
</tr>
<tr>
<td align="center">리전</td>
<td align="left">서버를 설치할 지역</td>
</tr>
<tr>
<td align="center">네트워크</td>
<td align="left">EC2 인스턴스를 배치할 네트워크로 AWS의 VPC에서 선택한다.</td>
</tr>
<tr>
<td align="center">서브넷</td>
<td align="left">설치할 네트워크의 범위. VPC내에 어떤 서브넷을 설치할지 선택한다. 서브넷을 선택하면 가용 영역이라고 하는 설치 장소와 어떤 범위에 사설 IP가 부여될지 결정된다.</td>
</tr>
<tr>
<td align="center">IAM 역할</td>
<td align="left">인스턴스의 접속 권한 정책 설정</td>
</tr>
<tr>
<td align="center">스토리지의 용량과 종류</td>
<td align="left">서버 장비의 스토리지. OS가 설치된 장소에 있다.</td>
</tr>
<tr>
<td align="center">태그</td>
<td align="left">인스턴스에 부여할수 있는 임의의 태그 (이름 부여)</td>
</tr>
<tr>
<td align="center">보안 그룹</td>
<td align="left">프로토콜별로 포트 혹은 IP주소, 아니면 양쪽 모두 필터링을 설정한다.</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>VPC ??</strong>
AWS 계정 전용 가상 네트워크</p>
</blockquote>
<br>

<h3 id="4-요금-책정">4) 요금 책정</h3>
<p>4가지 요소로 요금이 부과된다.</p>
<ol>
<li>인스턴스 사용량(가동 시간 X 단가) : 인스턴스가 가동한 시간(초) 단위로 과금</li>
<li>EBS(스토리지) 요금 (용량 X 단가) : 인스턴스가 사용하는 EBS 요금</li>
<li>통신 요금 (아웃바운드 통신 요금) : 인스턴스 통신 요금</li>
<li>그 외 옵션</li>
</ol>
<ul>
<li>Elastic IP 서비스 등의 옵션을 선택했을 경우 해당 요금 추가</li>
</ul>
<br>

<h1 id="-----------------------------">-----------------------------</h1>
<br>

<h1 id="2-ec2-이용-관련">2. EC2 이용 관련</h1>
<h2 id="1-ami">1) AMI</h2>
<p>AMI를 통해 같은 환경의 서버를 쉽게 재구축 할수 있다.
AMI는 공식 AWS의 AMI만 존재하는 것은 아니다. 커뮤니티나 기업에서 생성한 AMI도 이용 가능하며 종류에 따라서는 이용에 따른 별도 요금이 발생할 수 있다.
개인이 만들수도 있다. 생성한 AMI는 <code>마켓플레이스</code>에서 배포 가능하다.</p>
<p><strong>그러나</strong> AMI가 반드시 안전하다는 보장이 없기 때문에 새로운 AMI를 사용할땐 항상 유의해야한다.</p>
<h2 id="2-인스턴스-유형">2) 인스턴스 유형</h2>
<p>인스턴스 유형이란 <code>머신의 용도</code>이다. 용도는 다섯가지로 구분되며 각 용도에 따라 선택할수 있는 여러 유형이 존재한다.</p>
<table>
<thead>
<tr>
<th align="center">용도</th>
<th align="left">유형</th>
<th align="left">내용</th>
</tr>
</thead>
<tbody><tr>
<td align="center">범용</td>
<td align="left">A1, T3, T2, M5, M5a, M4, T3a</td>
<td align="left">일반적인 서버. 부하가 일정한 서버의 경우 사용하며 버스트 기능을 지원하는 유형도 있다.</td>
</tr>
<tr>
<td align="center">컴퓨팅 최적화</td>
<td align="left">C5, C5n, C4</td>
<td align="left">연산 능력이 높은 서버</td>
</tr>
<tr>
<td align="center">메모리 최적화</td>
<td align="left">R5, R5a, R4, X1e, X1, 고성능 메모리, z1d</td>
<td align="left">대용량 메모리를 탑재한 서버</td>
</tr>
<tr>
<td align="center">가속화된 컴퓨팅 최적화</td>
<td align="left">P3, P2, G3, F1</td>
<td align="left">머신 러닝 등에 사용되는 GPU를 탑재한 유형 및 그래픽 기능이 높은 유형</td>
</tr>
<tr>
<td align="center">스토리지 최적화</td>
<td align="left">H1, I3, D2</td>
<td align="left">스토리지를 최적화한 유형. I3는 SSD 기반이다.</td>
</tr>
</tbody></table>
<p>유형을 선택 한 후에는 크기를 선택해야 한다. 이 두가지를 모두 선택하면 <code>t2.micro</code>와 같이 표기된다.
프리티어를 사용중일땐 보통 t2.micro를 선택한다. 가장 작은 크기로 해야 무료로 이용할 수 있기 때문이다.</p>
<br>

<h2 id="3-ebs">3) EBS</h2>
<p><code>AWS managed service 아님.</code>
EBS 영구적인 블록 스토리지 볼륨으로 EC2와 조합하여 사용 가능하다. 스토리지 곧, 데이터를 기록하는 장소의 대표적인 예시는 HDD와 SSD이다.</p>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="left">기능</th>
</tr>
</thead>
<tbody><tr>
<td align="center">HDD</td>
<td align="left">대용량 지원, 비교적 가격이 저렴하고 성능도 낮다.</td>
</tr>
<tr>
<td align="center">SSD</td>
<td align="left">비교적 가격이 비싸지만 IOPS(1초당 처리할수 있는 입출력의 수)가 빠르다.</td>
</tr>
<tr>
<td align="center">성능과 비용을 따져가며 적당한 것을 선택하도록 한다.</td>
<td align="left"></td>
</tr>
</tbody></table>
<br>

<h2 id="4-elastic-ip">4) Elastic IP</h2>
<p><code>AWS managed service 아님.</code>
AWS는 정적인 공인 IP로 Elastic IP를 제공한다. (IPv4) 이는 AWS계정에 속해있으므로 인스턴스를 삭제해도 계속 사용할 수 있다.</p>
<p>참고로 EC2는 서버를 껏다 키면 공인 IP주소가 변경되므로 Elastic IP를 사용해 변경되지 않는 IP주소를 사용할 수 있다.
그러나 인스턴스에 기본적으로 제공되는 IP가 아닌 추가 IP를 연결할 경우 추가 요금이 부과된다. 이는 인스턴스가 중단되거나 했을때도 계속해서 요금이 부과되니 사용하지 않는 IP는 반드시 해지해야 한다.</p>
<br>

<h2 id="5-elastic-load-balancing">5) Elastic Load Balancing</h2>
<p><code>AWS managed service 아님.</code>
AWS가 제공하는 로드 밸런서. (분산 처리 장치) 3가지 종류가 있다. <strong>ALB, NLB, CLB</strong></p>
<p>다음의 OSI 모형을 기준으로 어느 계층에서 작동하는지 확인 할 수 있다.
<img src="https://images.velog.io/images/c_hyun403/post/f2f05bd9-3b6f-4a22-a0d0-4e5ae5e38919/image.png" alt=""></p>
<h3 id="1-alb-application-load-balancer">1. ALB (Application Load Balancer)</h3>
<p>HTTP및 HTTPS에 가장 적합한 로드 밸런서.애플리케이션 계층(구체적인 통신을 제공하는 계층)에서 작동한다.</p>
<p>내용을 판단하기 때문에 대상의 URL 디렉토리 단위로 분배가 가능하고, 인스턴스와 로드 밸런서 사이의 통신을 암호화 할 수 있다. 단, IP할당은 못한다.</p>
<blockquote>
<p>지원 프로토콜 : HTTP, HTTPS</p>
</blockquote>
<br>

<h3 id="2-nlb-network-load-balancer">2. NLB (Network Load Balancer)</h3>
<p>네트워크 계층 (전송된 데이터의 제어를 담당하는 계층)에서 작동한다. 패킷 (단편)데이터만 확인할수 있으므로 ALB보단 덜 섬세하지만 분배 대상의 정적 IP주소를 설정할수 있다는 차이가 있다. 또한 서버에 접속한 클라이언트의 IP주소를 그대로 서버에 전송하도록 설정할 수 있다.</p>
<blockquote>
<p>지원 프로토콜 : TCP, TLS</p>
</blockquote>
<br>

<h3 id="3-clb-classic-load-balancer">3. CLB (Classic Load Balancer)</h3>
<p>지원하는 프로토콜은 많지만 오래됐기 때문에 새로 구축할때는 사용을 권장하지 않는다.</p>
<blockquote>
<p>지원 프로토콜 : TCP, SSL/TLS, HTTP, HTTPS</p>
</blockquote>
<br>

<h2 id="6-스냅샷--서버-데이터-백업">6) 스냅샷 : 서버 데이터 백업</h2>
<p><code>AWS managed service 아님.</code></p>
<p>스냅샷이란 어떤 시점의 서버 디스크 상태를 <strong>통째로</strong> 보존한 파일이나 폴더 등의 집합으로 스냅샷을 작성해 Amazon EBS볼륨의 데이터를 Amazon S3에 백업할 수 있다.
스냅샷의 데이터 보존 장소는 S3이지만 사용자가 그 파일을 자유롭게 다운로드 할수 있는 것은 아니며 S3 서비스를 사용할때와는 다른 영역의 S3에 저장되는 것이다. 때문에 볼 수 없지만 이로 인한 S3요금은 발생하지 않는다.</p>
<br>

<h2 id="7-오토-스케일링">7) 오토 스케일링</h2>
<p><code>AWS managed service 아님.</code></p>
<p>오토 스케일링이란 서버의 액세스 상태에 따라 서버 대수를 늘리거나 줄이는 기능이다.</p>
<blockquote>
<p>** Scale **
서버 부하와 관련해 해결책으로 <code>Scale ##</code> 방법이 있다. 4가지 이다.</p>
</blockquote>
<ol>
<li>Scale-Up : 성능을 향상시킨다. <code>ex) 4GB =&gt; 8GB</code></li>
<li>Scale-Out : 물리 갯수를 늘린다. <code>ex) 1개 =&gt; 2개</code></li>
<li>Scale-Down : 성능을 저하시킨다. <code>ex) 8GB =&gt; 4GB</code></li>
<li>Scale-In : 물리 갯수를 줄인다. <code>ex) 2개 =&gt; 1개</code></li>
</ol>
<p>오토 스케일링은 <code>Scale-out</code>이다.
이를 사용하기 위해선 Auto Scaling 그룹 (인스턴스의 집합)을 생성하고 그 그룹에 인스턴스(서버)의 최소 대수와 최대 대수를 설정해야 한다. 그 범위 안에서 오토 스케일링이 활성화 되는 것이다. 또한 기능을 위해선 서버 가동에 필요한 AMI와 키페어, 보안그룹 등을 설정해야 한다.</p>
<p>요금은 기본적으로 <strong>무료</strong>이지만 CloudWatch를 사용할 경우 모니터링에 관련된 요금이 부과된다.
<br></p>
<h1 id="------------------------------1">-----------------------------</h1>
<br>

<h1 id="3-ec2-생성-실습">3. EC2 생성 실습</h1>
<p>프리티어 등급에서 EC2의 새 인스턴스를 생성해보자</p>
<h3 id="1-ami-선택">1) AMI 선택</h3>
<p>AWS에서 제공하는 AWS 공식 AMI를 선택한다. 이때 <code>나의 AMI</code>혹은 <code>AMI MarketPlace</code>를 이용한다.
<img src="https://images.velog.io/images/c_hyun403/post/f20fd579-13bb-4e93-914b-b850702d5ea8/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.29.22.png" alt="">
먼저 화면의 가장 최신 Ubuntu 버전을 선택하고, <code>64비트(x86)</code>을 선택한다. 이때 m1이라서 <code>Arm</code>했더니 프리티어 버전으로 선택할수 있는 인스턴스 유형이 없었다.</p>
<h3 id="2-인스턴스-유형-선택">2) 인스턴스 유형 선택</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/6b3e3a50-21f3-4dc8-822e-275bc597f107/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.31.48.png" alt=""></p>
<p>프리티어 등급으로 이용 가능한 <code>t2.micro</code>를 선택한다.</p>
<blockquote>
<p>*<em>t2.micro ?? *</em>
t는 instance family를 나타낸다. (t 외에도 M, C, 등이 있다)
t2의 2는 인스턴스의 세대를 나타낸다.
micro 는 인스턴스의 크기를 나타낸다.</p>
</blockquote>
<p>** 마이크로(T) ?? **
인스턴스 크기별 기본 수준의 CPU 성능 제공
간헐적으로 높은 성능이 필요할 때 유휴시간에 모아 놓은 크레딧을 기반으
로 버스팅하여 높은 성능을 제공
적용사례 : 개발환경, 소규모 웹, 마이크로 서비스</p>
<h3 id="3-인스턴스-구성">3) 인스턴스 구성</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/8c9ca8f5-fd98-47c1-9586-9a505ade5987/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.33.55.png" alt="">
VPC와 서브넷 / IAM역할 / CloudWatch 이용여부 등을 선택할 수 있다. 앞서 말했듯 <strong>CloudWatch</strong>를 이용할 경우 과금되니 주의가 필요하다.</p>
<h3 id="4-스토리지-추가">4) 스토리지 추가</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/9f027f13-297d-4c04-819b-f10d13d0f9d0/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.35.14.png" alt="">
EBS 볼륨을 구성하는 단계이다.</p>
<h3 id="5-태그-추가">5) 태그 추가</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/009ea4e5-cb66-4011-87fd-694df53016d9/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.38.16.png" alt="">
임의로 한개 태그를 생성해 보았다.</p>
<h3 id="6-보안-그룹-구성">6) 보안 그룹 구성</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/27e75641-3a34-43bb-b216-6f7e669f5f55/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.39.35.png" alt=""></p>
<p>보안 그룹을 구성한다. 새로운 보안그룹을 생성할수도 있고, 기존에 생성한 보안그룹을 선택할 수도 있다.
이때 유형은 다양한 것이 있지만 Linux기준 <code>SSH</code>를 사용하기로 했다. </p>
<h3 id="7-인스턴스-시작-검토">7) 인스턴스 시작 검토</h3>
<p>마지막으로 설정한 값들을 검토하여 최종 시작하기를 진행한다.
이때 경고 문구도 확인하자.
<img src="https://images.velog.io/images/c_hyun403/post/9eb25fe7-f1a9-446e-8875-f789aba58b81/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.41.34.png" alt=""></p>
<h2 id="💡💡💡-키페어">💡💡💡) 키페어</h2>
<p>SSH유형의 보안그룹 선택으로 사용할 <code>키페어</code>를 지정한다. 이때 기존에 만든 키페어를 선택할수도 있고, 새로 생성할 수도 있다.</p>
<p>새로 생성하면 <strong>반드시 다운로드 받아 개인적인 곳이 보관해야 한다.</strong> 키페어는 <strong>생성할 때만 다운 가능하고, 추가로 다운로드 되지 않으므로 주의한다.</strong></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/151e7291-383a-449b-a4dd-c4bbe15e3552/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.42.41.png" alt=""></p>
<h2 id="생성완료--생성-대기-실행">생성완료 : 생성 대기, 실행</h2>
<p>생성하면 바로 실행되지 않고 시간이 조금 걸리며 이때 인스턴스 상태는 <code>대기중</code>으로 표기된다. 조금 기다리면 실행된다.
<img src="https://images.velog.io/images/c_hyun403/post/a1f983cf-5634-497e-a8c7-6b79643b68d3/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.45.46.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/2d6b69cc-6997-4cd2-875e-22fd2f64545b/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.47.03.png" alt=""></p>
<h2 id="종료--삭제">종료 == 삭제</h2>
<p>사용하지 않는 인스턴스는 &quot;중지&quot; 할수도 있지만 &quot;종료&quot; 할수도 있다. 종료 역시 시간이 조금 걸리며 종료는 곧 <strong>삭제</strong>를 의미한다.
하지만 콘솔 화면상에서 &quot;종료&quot; 상태로 계속 보이긴 한다.
<img src="https://images.velog.io/images/c_hyun403/post/c1d4a093-62af-4658-959e-e3bf59bae5d9/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.47.44.png" alt="">
<img src="https://images.velog.io/images/c_hyun403/post/ff2bd72c-1763-4aac-85d6-b6bd79f4939e/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.47.59.png" alt="">
<img src="https://images.velog.io/images/c_hyun403/post/37253945-9a3e-4207-8782-02a141f35e7b/%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-08-18%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.49.15.png" alt=""></p>
<br>

<h1 id="끝">끝!</h1>
<p>간단히 개념과 생성까지 정리해보았다. 너무 길게 정리한것 같지만 빠뜨릴 내용이 없어서 그랬던...것...!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] S3 파일 속 내용 읽어서 mongoDB에 저장하기]]></title>
            <link>https://velog.io/@c_hyun403/AWS-s3-mongodb</link>
            <guid>https://velog.io/@c_hyun403/AWS-s3-mongodb</guid>
            <pubDate>Sun, 20 Jun 2021 16:03:27 GMT</pubDate>
            <description><![CDATA[<p>하려는 것 : S3에 저장된 파일 속의 내용을 읽어서 원하는 형태로 mongoDB에 새 document로 생성하기!
저장할 파일 : 이전에 블로깅한 txt file =&gt; s3 upload진행한 내용</p>
<h3 id="환경">환경</h3>
<h4 id="--s3">- S3</h4>
<p>[S3 내 구조]</p>
<pre><code class="language-bash">.
├── 조건1
│   ├── 파일1
│   └── 파일2
├── 조건2
│   ├── 파일1
│   └── 파일2
└── 조건3
    ├── 파일1
    ├── 파일2
    └── 파일3

# 파일 속 내용 : 다음의 형태가 string으로 기록됨
{time : 기록 날짜, name : 이름, age : 나이, hobby : 취미}

# 이때 기록날짜는 datetime.datetime 형태이다</code></pre>
<h4 id="--db--mongodb">- DB : mongoDB</h4>
<br>

<h1 id="------------------------------">------------------------------</h1>
<br>

<h1 id="0-s3에-접근하기--aws-액세스키">0. S3에 접근하기 : aws 액세스키</h1>
<p>먼저 S3에 접근하는 경로를 선택해야 한다.</p>
<ol>
<li>퍼블릭 bucket에 접근한다</li>
<li>bucket에 접근할수 있는 정책을 가진 사용자를 사용한다.</li>
</ol>
<p>나의 경우 처음에 퍼블릭 bucket임에도 불구하고 <code>aws_access</code> 키를 요구했어서.. 꽤나 고생하면서 액세스 키까지 발급받고 진행하긴 했다. 나중에 다시해보니 해당 키 내용을 제거하고 bucket이름만 명시해도 접근이 되더라! 그치만 둘다 해봤으므로 모두기록한다.</p>
<h2 id="1-유효한-정책을-지닌-사용자-이용하기">1) 유효한 정책을 지닌 사용자 이용하기</h2>
<p>먼저 이용하고자 하는 사용자의 <code>액세스 키</code>를 발급받아야 한다.
이를 위해선 로그인 후 화면 우측 상단의 username을 클릭해 드롭다운박스에 보이는 <code>내 보안 자격 증명</code>을 들어간다.</p>
<p><img src="https://images.velog.io/images/jello/post/7cb219c8-42ba-4106-b14f-df1de62adebc/%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-06-16%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.49.27.png" alt=""></p>
<p>이후 <code>액세스 키</code>를 들어가 새로운 액세스 키를 발급받도록 한다!
해당 정보가 기록된 문서는 <strong>생성시에만 다운 가능하다!! 이점유의해서 꼭 명확한 경로에 저장하도록 하자</strong>
(혹시나 분실했을 경우 해당 액세스 키를 삭제하고 다시 발급받으면 된다. 주기적으로 교체해 주는것도 보안상 중요!)</p>
<br>

<h4 id="aws-client">aws client</h4>
<p>터미널상에 액세스키에 대한 정보를 입력하고 해당 계정으로 aws 서비스를 이용하는것으로 이해했다..! 처음엔 무조건 이를 수행해야 되는줄 알았는데 그냥 로직상에 액세스키 번호를 입력해주면 되더라! 일단 해보긴 했다.</p>
<p>=&gt; 터미널상에서 aws client를 사용하기 위해 <code>awscli</code> 모듈을 설치해 준다. 그리고 액세스 키에 대한 정보를 입력한다.</p>
<pre><code class="language-bash">&gt; pip install awscli
&gt; aws configure --profile #사용할name
&gt;&gt;&gt; access_key 입력
&gt;&gt;&gt; secret_key 입력
&gt;&gt;&gt; region 입력 (나의 경우 ap-northease-2)</code></pre>
<p>그럼 설정 끝!</p>
<p>이걸 하면 로직상 액세스키를 입력안해줘도 되는거같긴한데 쓰는 방법은 모르겠다.. 그래서 그리고 나서 코드상에 boto3를 이용해 s3와 연결하면서 bucket에 접근할때 해당 키값을 다시 명시해 주었다. ㅎㅎ;
<img src="https://images.velog.io/images/jello/post/c0bac686-e657-44d5-9dc6-fc8828835807/image.png" alt=""></p>
<br>
<br>

<h2 id="2-퍼블릭-bucket에-접근하기">2) 퍼블릭 bucket에 접근하기</h2>
<p>이거는 여러번 삽질끝에 한 것이라 한번에 성공하는 법은 모른다!</p>
<p>하지만 bucket의 버킷정책에 다음과 같이 입력하였다.</p>
<pre><code class="language-bash">{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Sid&quot;: &quot;PublicRead&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Principal&quot;: &quot;*&quot;,
            &quot;Action&quot;: [
                &quot;s3:*Object&quot;,
                &quot;s3:*ObjectVersion&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:s3:::{내 bucket이름}/*&quot;
        }
    ]
}</code></pre>
<p>*은 전부를 뜻한다는거!</p>
<p>이 설정의 경우 다음과 같이 접근할수 있는걸로 알고있다...! (이렇게 말하는 이유는 내가 한번에 성공하지 못했기 때문)
<img src="https://images.velog.io/images/jello/post/709c4e62-6a41-4dd7-84f4-07efc9aa05a8/image.png" alt=""></p>
<br>

<h1 id="-------------------------------1">------------------------------</h1>
<br>

<h1 id="2-bucket내의-파일-이름-읽어오기--list_objects">2. bucket내의 파일 이름 읽어오기 : list_objects</h1>
<p>boto3.client의 속성을 검색해 보았다.
<img src="https://images.velog.io/images/jello/post/7b043d8a-ab4b-4c37-a6c5-c658cc8b2b1e/%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-06-16%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%202.03.01.png" alt=""></p>
<p>엄청많다...! 궁금하면 boto3가 설치된 환경에서</p>
<pre><code class="language-bash">#python shell
&gt;&gt;&gt; import boto3
&gt;&gt;&gt; s3 = boto3.client(&#39;s3&#39;)
&gt;&gt;&gt; s3. #입력 후 tap 키를 누른다.</code></pre>
<p>구글링 하면서 알아낸 것은 해당 bucket의 모든 object들을 읽어오는 <code>s3.list_objects</code>라는 속성이다.</p>
<br>

<h3 id="하위개념-x">하위개념 X</h3>
<p>S3에 저장할때 폴더 내에 파일을 저장할 수 있다. 그렇게 되면 폴더와 파일의 이름형식은 다음과 같이 설정된다.</p>
<pre><code class="language-bash"># 폴더1
폴더1/

# 폴더1 내의 파일1
폴더1/파일1</code></pre>
<p>하지만 위의 <code>list_objects</code>를 사용하면 폴더 자체로도 object로 여겨져 이를 불러온다.</p>
<pre><code class="language-bash"># list_objects 결과
- 폴더1/
- 폴더1/파일1</code></pre>
<br>

<hr>
<br>

<p><code>list_objects</code>를 사용해 bucket에 저장된 object들을 확인해보자. 전부를 가져오면 hostid나 기타등등의 정보도 다 불러와지니까 <strong>딱 원하는 정보인 저장된 객체에 대한 것만 확인해보자</strong> 그러기 위해선 가져온 object의 <code>Contents</code>키의 내용을 확인하면 된다.
이말은 즉 <code>list_objects</code>로 가져온 s3의 objects들은 <strong>dictionary</strong>형태로 온다는 말이다.</p>
<pre><code class="language-python">import boto3
s3 = boto3.client(&#39;s3&#39;, accesskey, secretykey)
obj = s3.list_objects(Bucket=&#39;velog-practice&#39;)
print(&quot;저장된 것 :&quot;, obj[&#39;Contents&#39;]

&gt;&gt;&gt;
저장된것 [
{&#39;Key&#39;: &#39;2021-06-20-PM05/green5.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 49, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 78, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}},
{&#39;Key&#39;: &#39;22021-06-20-PM05/purple5.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 50, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 79, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;2021-06-20-PM05/red5.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 49, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 78, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;2021-06-20-PM05/yellow5.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 50, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 79, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;22021-06-20-PM12/green.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 49, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 76, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;2021-06-20-PM12/purple.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 50, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 77, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;2021-06-20-PM12/red.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 49, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 79, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}, 
{&#39;Key&#39;: &#39;2021-06-20-PM12/yellow.log&#39;, &#39;LastModified&#39;: datetime.datetime(2021, 6, 20, 14, 21, 50, tzinfo=tzutc()), &#39;ETag&#39;: &#39;&quot;~~~~&quot;&#39;, &#39;Size&#39;: 80, &#39;StorageClass&#39;: &#39;STANDARD&#39;, &#39;Owner&#39;: {&#39;ID&#39;: &#39;~~~&#39;}}
]</code></pre>
<p>내가 원한것은 폴더 안에 속한 파일들은 dictionary속 list로 묶는 형태였으므로 이를 위해 가공이 필요했다.</p>
<br>

<h1 id="3-폴더--폴더-내-파일들--python의-lambda">3. {폴더 : [폴더 내 파일들]} : python의 lambda</h1>
<p>저장된 objects의 형태를 보면 <code>폴더명/파일명</code>의 형태이다. 폴더명은 통일되도록 <strong>일시</strong>로 저장되게 하였고, 같은 일시에 작성된 것들은 같은 폴더에 저장된다.</p>
<p>오랜만에 python의 lambda 함수를 사용해 보자!</p>
<p>순서는 다음과 같다.</p>
<blockquote>
</blockquote>
<ol>
<li>s3의 Contenst내용을 가져온다. 이때 file들은 list형태의 Contenst의 dictionary속 <code>Key</code>값에 저장되어 있다.</li>
<li>folder이름만을 담은 list를 선언한다.</li>
<li>lambda를 활용해 folder명이 같은 file들끼리 묶어준다.</li>
</ol>
<pre><code class="language-python"># 과정

obj = s3.list_objects(Bucket=bucket)[&#39;Contents&#39;]
obj_key = [row[&#39;Key&#39;] for row in obj]

folder_list = []
for row in obj_key:
    if row[:15] not in folder_list:
        folder_list.append(row[:15])

res_list = []
for row in folder_list:
  gather_files = list(filter(lambda x:x[:15] == row, obj_key))
  tmp_dict = {}
  for row in gather_files:
    if not tmp_dict:
      tmp_dict = {row[:15]: []}
    else:
      tmp_dict[row[:15]].append(row[16:])
  if tmp_dict:
    res_list.append(tmp_dict)

&gt;&gt;&gt; res_list = [
{&#39;2021-06-20-PM05&#39;: [&#39;purple5.log&#39;, &#39;red5.log&#39;, &#39;yellow5.log&#39;]}, 
{&#39;2021-06-20-PM12&#39;: [&#39;purple.log&#39;, &#39;red.log&#39;, &#39;yellow.log&#39;]}
]
</code></pre>
<br>

<h1 id="4-파일-내-data-읽기--get_object">4. 파일 내 data 읽기 : get_object</h1>
<p>반복문을 통해서 진행한다.</p>
<pre><code class="language-python">from ast import literal_eval

for row in res_list:
    for file_name in list(row.values())[0]:
        folder_name = row.keys()
        log_file = s3.get_object(Bucket=&#39;velog-practice&#39;, Key=(list(folder_name)[0]+&#39;/&#39;+file_name))
        tmp_content = log_file[&#39;Body&#39;].read().decode(&#39;utf-8&#39;)
        log_content = literal_eval(tmp_content)</code></pre>
<p>여기서 <code>get_object</code>가 사용되었는데 흐름상 알수 있듯이 해당 파일 속의 내용을 읽는 속성이다. 이때 <code>byte</code>
타입으로 기록되기 때문에 decoding을 해주어야 하며, 단순 string 형태이기 때문에 dictionary 형태를 원한다면 형변환을 해주어야 한다. 따라서 <code>literal_eval</code>을 사용하였다.</p>
<br>

<h1 id="5-mongodb에-저장하기">5. mongoDB에 저장하기</h1>
<p>나는 반복문을 통해 s3내용을 읽을때마다 mongoDB에 저장하는걸 택했다. 따라서 위의 반복문에 이 코드를 추가한다.</p>
<pre><code class="language-python">        db.velog_practice.insert_one(log_content)</code></pre>
<br>

<h1 id="6-저장된것-확인하기">6. 저장된것 확인하기</h1>
<p>tool로 확인할수도 있지만 귀찮으니 명령어로 확인하자.</p>
<pre><code class="language-python">for row in list(db.velog_practice.find({})):
    print(&quot;db에 저장된 data==========&gt; : &quot;, row)

db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab836&#39;), &#39;name&#39;: &#39;purple5&#39;, &#39;age&#39;: 4, &#39;hobby&#39;: &#39;hiking&#39;, &#39;time&#39;: &#39;2021-06-20-17-00-00&#39;}
db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab837&#39;), &#39;name&#39;: &#39;red5&#39;, &#39;age&#39;: 1, &#39;hobby&#39;: &#39;dancing&#39;, &#39;time&#39;: &#39;2021-06-20-17-00-45&#39;}
db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab838&#39;), &#39;name&#39;: &#39;yellow5&#39;, &#39;age&#39;: 2, &#39;hobby&#39;: &#39;reading&#39;, &#39;time&#39;: &#39;2021-06-20-17-00-30&#39;}
db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab839&#39;), &#39;name&#39;: &#39;purple&#39;, &#39;age&#39;: 4, &#39;hobby&#39;: &#39;hiking&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-00&#39;}
db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab83a&#39;), &#39;name&#39;: &#39;red&#39;, &#39;age&#39;: 1, &#39;hobby&#39;: &#39;dancing&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-30&#39;}
db에 저장된 data==========&gt; :  {&#39;_id&#39;: ObjectId(&#39;60cf5e051ea5f74dca0ab83b&#39;), &#39;name&#39;: &#39;yellow&#39;, &#39;age&#39;: 2, &#39;hobby&#39;: &#39;reading&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-20&#39;}</code></pre>
<br>

<h1 id="-------------------------------2">------------------------------</h1>
<br>

<h1 id="끝">끝!!!</h1>
<p>그런데 일할때랑 혼자 연습할때랑 다른점이있다...! 연습때는 <code>list_objects</code>로 s3 버킷 내 모든 objects들을 불러올때 folder만 불러와지진 않은듯..? 이거때문에 구분하는 고생을 한건데.. 다시 확인해 보았다.</p>
<p>이를 위해 연습때는 aws 사이트에서 폴더생성없이 바로 file명에 &quot;/&quot;를 붙여 폴더 속에 파일로 생성되게 했었다.
이번에는 비교를 위해 aws 사이트에서 폴더를 생성하고, 그 속에 log 파일을 넣은 뒤 확인해 보았다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/108e1534-da81-48d3-beb0-3a360ef53f45/%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-06-21%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.39.17.png" alt=""></p>
<pre><code class="language-python"># list_objects로 확인 결과
[&#39;2021-06-20-PM05/green5.log&#39;, &#39;2021-06-20-PM05/purple5.log&#39;, &#39;2021-06-20-PM05/red5.log&#39;, &#39;2021-06-20-PM05/yellow5.log&#39;, 
&#39;2021-06-20-PM12/green.log&#39;, &#39;2021-06-20-PM12/purple.log&#39;, &#39;2021-06-20-PM12/red.log&#39;, &#39;2021-06-20-PM12/yellow.log&#39;, 
&#39;check_folder/&#39;, &#39;check_folder/check.log&#39;]</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/95fc5e17-c81e-4f6b-8686-3e8c891cdecd/image.png" alt=""></p>
<p>즉, <strong>aws내에서 임의로 폴더를 생성하고, 그 속에 file을 넣을 경우 <code>list_objects</code>로 읽을 시 folder만도 같이 읽힌다.</strong>
이와 달리 <strong>s3에 없로드 시 &quot;/&quot;사용을 통해 folder가 자동으로 생성되고, 그 속에 file이 저장되게 하면 <code>list_objects</code>사용 시 정확히 내가 넣은 file들만 읽힌다.</strong> 단, <code>폴더명/파일명</code>으로 읽힌다.</p>
<p>오.. 코드 수정할 수 있겠는걸? 월요일이 기대된다! 헷! 🥰</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] txt파일 생성부터 s3업로드까지 한방에]]></title>
            <link>https://velog.io/@c_hyun403/AWS-txt-s3</link>
            <guid>https://velog.io/@c_hyun403/AWS-txt-s3</guid>
            <pubDate>Tue, 15 Jun 2021 17:07:35 GMT</pubDate>
            <description><![CDATA[<p>테스트를 위해 s3내 dumy file들이 많이 필요했다.
일일이.. 손으로.. 업로드하는것은.. 진짜 아닌거 같았다. (최소 100개정도가 필요했다.)</p>
<p>그래서!! 원하는 조건으로 dictionary형태를 만들고 해당 형태의 내용을 담은 file로 s3에 업로드 되기까지 코드로 작성해버렸다.</p>
<br>

<h1 id="1-txt-파일-만들기">1. txt 파일 만들기</h1>
<p>알고리즘을 풀때 이와 같은 코드를 많이 작성했었다.</p>
<pre><code class="language-python">import sys
sys.stdin = open(&quot;파일명&quot;, &quot;rt&quot;)
n = input()
m = input()</code></pre>
<p>위 코드는 &quot;파일명&quot;의 내용을 한줄 읽고, n값에 대입하는 것이다. n 다음에 m에는 두번째 줄의 내용이 담긴다.</p>
<p>txt 파일을 <strong>작성</strong>할 때는 <code>sys.stdout</code>을 사용한다.</p>
<pre><code class="language-python">sys.stdout = open(&quot;폴더명/파일명.txt&quot;, &quot;a&quot;)</code></pre>
<p>&quot;폴더명&quot;에 해당하는 폴더는 사전에 생성해 두어야 한다. 이후 <code>폴더명/파일명</code>으로 지정해 놓으면 그 폴더 안에 &quot;파일명&quot;으로 txt파일이 자동으로 생성된다. 물론 txt 대신 다른 확장자명 쓰면 그걸로 생성된다.</p>
<p>작성될 파일까지 지정해 주었는데 어떻게.. 저장하지..?
싶으면
<code>print</code>문으로 한다!!!!</p>
<br>

<pre><code class="language-python">import sys
import datetime

sys.stdout = open(&#39;velog/velogpractice.txt&#39;, &#39;a&#39;)

date = datetime.datetime(2021, 6, 20)

def create_log(date):
  ex = [{&quot;name&quot;: &quot;purple&quot;, &quot;age&quot;: 4, &quot;hobby&quot;: &quot;hiking&quot;}, {&quot;name&quot;: &quot;green&quot;, &quot;age&quot;: 3, &quot;hobby&quot;: &quot;singing&quot;},
        {&quot;name&quot;: &quot;yellow&quot;, &quot;age&quot;: 2, &quot;hobby&quot;: &quot;reading&quot;}, {&quot;name&quot;: &quot;red&quot;, &quot;age&quot;: 1, &quot;hobby&quot;: &quot;dancing&quot;}]

  for i, row in enumerate(ex):
    row[&#39;time&#39;] = date+datetime.timedelta(minutes=10*i)
    print(row)

create_log(date)</code></pre>
<p>위 코드로 작성된 파일은 velog라는 폴더 내 <code>velogpractice</code>라는 txt 파일에 내용이 저장되어 있다. 순서는 위에서 아래이다.</p>
<pre><code class="language-txt">[velog/velogpractice.txt]

{&#39;name&#39;: &#39;purple&#39;, &#39;age&#39;: 4, &#39;hobby&#39;: &#39;hiking&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-00&#39;}
{&#39;name&#39;: &#39;green&#39;, &#39;age&#39;: 3, &#39;hobby&#39;: &#39;singing&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-10&#39;}
{&#39;name&#39;: &#39;yellow&#39;, &#39;age&#39;: 2, &#39;hobby&#39;: &#39;reading&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-20&#39;}
{&#39;name&#39;: &#39;red&#39;, &#39;age&#39;: 1, &#39;hobby&#39;: &#39;dancing&#39;, &#39;time&#39;: &#39;2021-06-20-12-00-30&#39;}</code></pre>
<br>

<h1 id="2-txt파일-읽어오기">2. txt파일 읽어오기</h1>
<p>s3업로드 하기 위해 txt파일 내 정보를 읽어와야 한다.</p>
<blockquote>
<p>굳이 txt파일로 안만들고 바로 s3에 저장할수도 있지만 나에게는 <code>s3에 저장</code> 과정이 필요했기 때문에 이렇게 했다.!!!</p>
</blockquote>
<p>이때에도 txt파일에 저장된 한줄씩 읽으면서 바로 s3에 저장할 것이기 때문에 다음과 같이 코드를 작성했다.</p>
<pre><code class="language-python">
# txt파일 읽어오기
sys.stdin=open(&quot;velog/velogpractice.txt&quot;, &quot;rt&quot;)

while True:
  n = str(input())
  save_to_s3_velog(n)</code></pre>
<p>이제 읽어온 한줄은 바로 <code>save_to_s3_velog</code>라는 메소드로 실행될 것이다.</p>
<br>

<h1 id="3-s3에-저장하기">3. s3에 저장하기</h1>
<p>특별히 제목을 가공하지 않는다면 저장 자체는 어렵지 않다.! 가공이 항상..문제야.. 그리고 난 가공을 한다. 😃 그게 중요한게아니니 가공 과정은 생략한다.</p>
<br>

<pre><code class="language-python">def save_to_s3_velog(data):
    bucket = &#39;velog-practice&#39;
    s3 = boto3.client(&#39;s3&#39;, aws_access_key_id, aws_secret_access_key)
    s3.put_object(Body=data, Bucket=bucket, Key=name, ContentType=&#39;text/plain&#39;)</code></pre>
<blockquote>
<p><strong>Body</strong>
S3에 저장되는것은 모두 <code>byte</code> 타입이다.
<br>
<strong>ContentType</strong>
이를 설정해주지 않으면 aws 사이트에서 해당 파일을 <code>열기</code> 했을때 바로 안열리고 <strong>download</strong>가 된다. 이.. 차이뿐인가..?
<br>
<strong>put_object</strong>
s3에 저장하는 속성이다.</p>
</blockquote>
<br>

<h1 id="---------------------------------">---------------------------------</h1>
<br>

<h1 id="끝">끝!</h1>
<p>이제 다음 포스팅에서 s3에 저장된 file의 내용을 읽어 mongoDB에 저장하는걸 다뤄보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] IAM - Lambda - API Gateway]]></title>
            <link>https://velog.io/@c_hyun403/AWS-lambda-event-keyerror-%EA%B7%B9%EB%B3%B5</link>
            <guid>https://velog.io/@c_hyun403/AWS-lambda-event-keyerror-%EA%B7%B9%EB%B3%B5</guid>
            <pubDate>Sun, 13 Jun 2021 15:06:14 GMT</pubDate>
            <description><![CDATA[<p>API Gateway를 사용해 RESTful API 를 생성하고, 이를 배포하여 만들어진 URL에 대해 aws의 lambda로 호출을 기록하고자 한다.</p>
<p>lambda의 기능! <strong>호출이 들어올때만 실행되는 함수</strong>임을 기억하면 이런 일도 가능하구나...! 싶다.</p>
<br>

<hr>
<br>

<p>먼저 이전 내용은 중구난방이었기에 다시한번 API Gateway와 lambda를 연결해보도록 하자.</p>
<h1 id="iam-권한-설정">IAM 권한 설정</h1>
<p>AWS 서비스를 사용하는데 권한을 부여할 역활을 생성해야 한다. <em><strong>이거 중요,, 개거생 했당!</strong></em></p>
<br>

<blockquote>
<p>** IAM ?? **
IAM 역할은 신뢰하는 개체에 권한을 부여하는 안전한 방법이다.</p>
</blockquote>
<br>

<p>내가 사용할 서비스는 API Gateway와 lambda이다. lambda의 트리거로 API Gateway를 연결할 것인데 이때 lambda를 동작하는 역활에게 API Gateway에 대한 여러 권한을 부여해 주어야 한다.</p>
<br>

<p>lambda를 사용하면서 API Gateway에 대한 권한을 가진 역활을 생성한다. 이때 어떤 API Gateway권한을 허용할 것인지 검색하면 4가지 옵션이 나온다.</p>
<blockquote>
</blockquote>
<ul>
<li>AmazonAPIGatewayAdministrator</li>
<li>AmazonAPIGatewayInvokeFullAccess</li>
<li>AmazonAPIGatewayPushToCloudWatchLogs</li>
<li>AmazonAugmentedAIIntegratedAPIAccess</li>
</ul>
<br>

<p>이중에서 <strong>AmazonAPIGatewayPushToCloudWatchLogs</strong> 정책을 연결해줄것...!!!!!!!!</p>
<br>

<p>역할 이름은 <code>lambda_main_iam</code>이라고 지었다. (역할이 14개라 헷갈려서...)</p>
<br>

<h1 id="lambda-함수-생성하기">lambda 함수 생성하기</h1>
<p>함수이름은 <code>favorite_fruit</code>라고 지었다!</p>
<p>그리고 나는 lambda함수를 <code>python</code>으로 작성할 것이고, 버전은 <code>3.7</code>이기 때문에 해당하는 언어를 선택했다.</p>
<p>그리고 중요한 <strong>기본 실행 역할 변경</strong> 아까 만든 IAM을 지정해 준다.
<img src="https://images.velog.io/images/c_hyun403/post/cb2026a1-534f-4448-bddb-d5efb36bcf52/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.31.14.png" alt=""></p>
<br>

<p>함수를 생성하면 내가 지정한 함수이름으로 만들어진 폴더 안에 <code>lambda_function.py</code>라는 파일이 만들어져 있고 그 안에 예제 lambda코드가 작성되어 있다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/9adc209a-305b-409f-b9ac-62a114d7a772/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.33.04.png" alt="">
먼저 이 내용을 건드리지 말고 API Gateway와 연결 후 테스트 해 보도록 하자!</p>
<br>

<h1 id="api-gateway-생성하기">API Gateway 생성하기</h1>
<p>REST API를 생성할것이기 때문에 해당 옵션을 선택해 생성하도록 하자! (사실 다른건 잘 모른다)</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/8ad165bf-ede1-4fbd-89cc-94c2c0383cdb/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.34.56.png" alt=""></p>
<p>엔드포인트 유형도 3가지가 있는데 일단 <code>지역</code>으로 선택...! 최적화된 에지도 해봤는데 사실 엄청 간단하게만 작동해봐서 다른점은 아직 모르게따</p>
<br>

<p>그리고 나서 URL경로를 만들어 준다.!
간단하게 /fruit/{fruit_name} 으로 <code>fruit_name</code>의 값을 path parameter로 받는 형태로 작성하였고, method는 GET만 작성하였다.</p>
<p>그리고 나서 method의 <code>통합 포인트</code>로 아까 만든 lambda함수를 연결하도록 한다. <strong>(lambda함수의 arn주소를 복붙하면 자동으로 lambda함수 이름으로 작성된다.)</strong></p>
<br>

<p><img src="https://images.velog.io/images/c_hyun403/post/9e646e74-071b-477d-bf90-b3b6c826772e/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.38.49.png" alt=""></p>
<p>내용을 저장한다고 하면 이처럼 해당 API Gateway에 lambda함수에 대한 권한을 추가한다고 알람이 뜬다!</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/b04d5391-3270-42dd-9c53-d31f81a01d49/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.36.55.png" alt=""></p>
<br>

<h2 id="mapping-template-설정">mapping template 설정</h2>
<p>lambda함수로 들어온 내용을 가공없이 그대로 출력하게 하기 위해 다음과 같이 설정하도록 한다.!</p>
<p>먼저 메소드 화면에서 <code>통합 요청</code>을 들어간다. 그리고 content/type으로 <code>application/json</code>을 선택하고 <code>메소드 요청 패스스루</code>를 선택하면 다음의 코드가 자동을 작성된다. 저장만 하면 끝!
<img src="https://images.velog.io/images/c_hyun403/post/4a682cb5-7d96-4523-b3fa-67abca741c9c/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.40.55.png" alt=""></p>
<h2 id="lambda-트리거로-api-gateway-지정됨">lambda 트리거로 API Gateway 지정됨.</h2>
<p>다시 lambda 함수 내용을 보면 트리거로 방금 생성한 API Gateway가 지정되어있는걸 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/c05bcc83-dde1-4ae2-9225-d1c2de853276/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.43.20.png" alt=""></p>
<br>

<hr>
<br>

<h1 id="테스트하기-api-gateway-test">테스트하기 (API Gateway test)</h1>
<p>먼저 API Gateway에서 제공하는 테스트를 사용해 보자!
아직 lambda함수 코드가 예제코드이기 때문에 큰 에러없이 <strong>&quot;Hello from Lambda!&quot;</strong> 가 출력될 것이다.</p>
<p>이를 lambda기능의 모니터링을 통해 <strong>CloudWatch</strong>에서 로그를 확인해 보자!</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/b0058ce4-3d91-4824-aab0-1bbaf347dcfb/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.53.09.png" alt=""></p>
<p>print문이 없기 때문에 log에도 별다른 출력문이 찍히지 않는다!</p>
<p>이제 <code>path parameter</code>에 대한 값이 출력되도록 코드를 수정해 보자. 이를 위해선 lambda_handler의 parameter값인 event의 활용이 필요하다.</p>
<p>먼저 event라는 값에는 어떤것이 담기는지 확인해 보자</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/bb4a78e1-0ed8-452a-908f-b64cedff2864/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.55.34.png" alt=""></p>
<p>헐 아무것도 없다. 있어야되는데 없다. <strong>정책 연결을 제대로 안해주었기 때문이다!!!!!!!</strong></p>
<br>

<h2 id="🔥☠️-정책-설정하기---대실패-keyerror">🔥☠️ 정책 설정하기 - 대실패 KEYERROR</h2>
<p>앞전에 IAM 만들때 API Gateway에 대한 정책을 하나만 연결했다. 그것만 하면 되는줄알았다. 그치만 event가 제대로 찍히지 않으니 다른 정책도 연결해 주어야 하나보다!! 이때 계속해서 event에 key가 없어 event속의 정보를 얻어내려 할때마다 <code>KeyError</code>가 나더라..........</p>
<p>하나씩 추가해보자</p>
<h4 id="amazonapigatewayadministrator">AmazonAPIGatewayAdministrator</h4>
<p>결과 : event 안찍힘</p>
<h4 id="amazonapigatewayinvokefullaccess">AmazonAPIGatewayInvokeFullAccess</h4>
<p>결과 : 안찍힘</p>
<h4 id="amazonaugmentedaiintegratedapiaccess">AmazonAugmentedAIIntegratedAPIAccess</h4>
<p>결과 : 안찍힘</p>
<p>lambda에 대한 정책도 추가로 연결해보자</p>
<h4 id="awslambda_fullaccess">AWSLambda_FullAccess</h4>
<p>결과 : 안찍힘</p>
<br>

<p>왜 또 안되...!</p>
<h2 id="🔥☠️-정책-설정하기---api-gateway-lambda-재생성">🔥☠️ 정책 설정하기 - API Gateway, lambda 재생성</h2>
<p>되야하는데 안되는것 같아 일단 다시 만들어보기로 했다.</p>
<p>방금 <strong>여러 정책이 추가된 역할을 사용해서</strong> lambda와 api gateway를 다시 만들어보자!</p>
<p>먼저 lambda는 <code>re_fruit</code>라는 이름으로 다시 만들었고, API Gateway의 경우 <strong>아까 만든 API에서 리소스만 새로 추가하여 만들었다</strong></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/0c05516a-cac9-40ca-b21e-662846e99f02/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.51.22.png" alt=""></p>
<p>그리고 나서 lambda함수에서 event를 출력하도록 코드를 수정하고 test를 진행해 보았다.</p>
<br>

<p><img src="https://images.velog.io/images/c_hyun403/post/0cb4bda3-7d92-4133-b359-9cf1cdda9529/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.52.21.png" alt=""></p>
<p><code>path parameter</code>입력 후
<img src="https://images.velog.io/images/c_hyun403/post/d54e62fe-9bad-460f-b9fd-062dc4483857/%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-06-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.52.04.png" alt=""></p>
<p>이 외에도 IP주소를 출력하게 할수도 있고 등등 다양한 값을 출력할 수 있다! 어떤 attribute가 있는지는 aws 문서에 나와있다!</p>
<br>

<h1 id="최종-형태-log-기록-확인용">최종 형태 (log 기록 확인용)</h1>
<p>예를들어 두가지 케이스에 대해 각각 log가 잘 찍히는지 확인하고 싶다고 가정하자.
그러면 배포된 API의 URL을 어딘가에 입력하고, 입력된 곳을 load되면 log가 기록될 것이다. 이때 그 log가 case1의 것인지, case2의 것인지 바로 판별하고 싶을때!!! 를 위해서 <strong>path parameter</strong>를 지정해 준 것이다.</p>
<h4 id="case1-의-url">case1 의 url</h4>
<pre><code> https://@주소~~.ap-northeast-2.amazonaws.com/dev/fruit/apple</code></pre><h4 id="case1-의-url-1">case1 의 url</h4>
<pre><code> https://@주소~~.ap-northeast-2.amazonaws.com/dev/fruit/banana</code></pre><p>간단하게 POSTMAN으로 확인해 보자!!</p>
<h3 id="case-1--apple">CASE 1 : apple</h3>
<p>lambda의 함수를 다음과 같이 작성하였다.</p>
<pre><code class="language-python">def lambda_handler(event, context):
    print (&quot;fruit name :&quot;, event[&#39;parmas&#39;][&#39;path&#39;][&#39;fruit&#39;])
    return event[&#39;parmas&#39;][&#39;path&#39;][&#39;fruit&#39;]</code></pre>
<p>여기서 print문은 log에 기록될 것이고 return값은 postman의 response에 띄워질 것이다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/c826e377-f2d0-477a-aa3e-c8377f9b7bfa/%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-06-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.01.18.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/00567d66-63cd-4943-8970-a942d5e91b19/%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-06-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.01.54.png" alt=""></p>
<h3 id="case-2--banana">CASE 2 : banana</h3>
<p><img src="https://images.velog.io/images/c_hyun403/post/8216b0db-700f-4733-bcc3-bcac64886081/%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-06-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.03.01.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/36d7509b-653f-4dc5-bece-cba98b8a8ea0/%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-06-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.03.21.png" alt=""></p>
<br>
<br>

<h1 id="끝">끝!</h1>
<p>API Gateway로 RESTful API를 생성하고, 이를 배포하여 lambda와 연동해 해당 url이 load될때마다 lambda가 실행되는것을 확인할 수 있었다.!!</p>
<p>아직도 너무나 어려운 <strong>정책 설정...</strong> 다양한 케이스로 사용해보고 얼른 익혀야겠다.!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[환경변수] os]]></title>
            <link>https://velog.io/@c_hyun403/%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@c_hyun403/%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98</guid>
            <pubDate>Thu, 10 Jun 2021 06:21:22 GMT</pubDate>
            <description><![CDATA[<h3 id="환경변수">환경변수</h3>
<pre><code>&gt;&gt;&gt; import os
&gt;&gt;&gt; os.environ
environ({&#39;PATH&#39;: &#39;/Users/@username/~~~&#39;, &#39;SHELL&#39;: &#39;/bin/zsh&#39;, &#39;USER&#39;: @username, ...이하생략...})</code></pre><h4 id="환경변수--dictionary">환경변수 =&gt; dictionary</h4>
<p>없는 값을 호출하면 똑같이 <code>KeyError</code>발생</p>
<pre><code>&gt;&gt;&gt; import os
&gt;&gt;&gt; os.environ[&#39;ENV&#39;]
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  File &quot;/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/os.py&quot;, line 681, in __getitem__
    raise KeyError(key) from None
KeyError: &#39;ENV&#39;</code></pre><h4 id="1-osenvironget">1. os.environ.get()</h4>
<p>기본으로 없으면 값을 가져오지 않는데 사용자 설정으로 값이 없을 경우 특정 값을 사용할 수 있다.</p>
<pre><code>&gt;&gt;&gt; import os
&gt;&gt;&gt; os.environ.get(&#39;ENV&#39;)
&gt;&gt;&gt; #아무것도 없음
&gt;&gt;&gt; os.environ.get(&#39;ENV&#39;, &#39;NOTHING&#39;)
&gt;&gt;&gt; NOTHING</code></pre><h4 id="2-osgetenv">2. os.getenv()</h4>
<p><code>os.environ.get()</code>과 같은 기능이다.</p>
<pre><code>&gt;&gt;&gt; import os
&gt;&gt;&gt; os.getenv(&#39;ENV&#39;)
&gt;&gt;&gt; os.getenv(&#39;ENV&#39;, &#39;NOTHING&#39;)
&#39;NOTHING&#39;</code></pre><hr>
<br>

<p>특정 프로젝트에서는 <strong>환경변수</strong>를 지정해 주기도 한다. 어떤 환경변수를 지정했는지 까먹을때 이 코드를 써보도록 하자! (해본적 없어서 잘 되는지는 모름! ㅎ)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[terminal] oh-my-zsh & powerline 설정하기]]></title>
            <link>https://velog.io/@c_hyun403/terminal-%EC%82%AC%EA%B3%BC..%EB%AA%BB%EC%9E%83%EC%96%B4</link>
            <guid>https://velog.io/@c_hyun403/terminal-%EC%82%AC%EA%B3%BC..%EB%AA%BB%EC%9E%83%EC%96%B4</guid>
            <pubDate>Wed, 09 Jun 2021 02:33:22 GMT</pubDate>
            <description><![CDATA[<p>m1으로 초기세팅을 다 하고난뒤 남은 허전함..
터미널을 열면 보이는건 검은배경에 희끄무리한 글씨뿐이다
심지어 내가 어떤 branch에 있는지도 안보이고! 무튼 못생겼다.!</p>
<p>여기서는 vim도 안쓰고 pycharm이나 intelliJ를 사용하기때문에 터미널로 뭔가 작업하진 않을것 같지만 pycharm에서도 내장 terminal은 쓴다구..!!</p>
<p>다시한번 꾸며보자!</p>
<hr>
<br>

<h1 id="0-환경">0. 환경</h1>
<ul>
<li>iterm2</li>
<li>homebrew 설치!</li>
<li>zsh이 기본 shell</li>
</ul>
<pre><code class="language-bash">- iterm2 download
- Home brew download
brew install zsh</code></pre>
<br>

<h1 id="1-highlighting--autosuggestions">1. highlighting / autosuggestions</h1>
<p>유용하게 쓰이는 기능 두가지 이다. 명령어 highlighting과 자동완성제공기능!</p>
<pre><code class="language-bash"># brew install zsh 이후 진행
brew install zsh-syntax-highlighting
brew install zsh-autosuggestions</code></pre>
<h1 id="2-zshrc-수정">2. ~/.zshrc 수정</h1>
<p>zshrc 파일은 음.. zsh의 config 파일 같은것...? 여기에 환경변수도 저장하고 플러그인도 입력하고, alias도 저장하는 등 여러 환경설정을 진행한다.</p>
<p><code>vi ~/.zshrc</code>로 방금 설치한 두가지 플러그인을 등록한다.</p>
<pre><code class="language-bash"># ~/.zshrc
source /opt/homebrew/Cellar/zsh-syntax-highlighting/0.7.1/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /opt/homebrew/Cellar/zsh-autosuggestions/0.7.0/share/zsh-autosuggestions/zsh-autosuggestions.zsh</code></pre>
<p>이때.. 저거 <code>.zsh</code>파일 두개가 어딨는지 한참을 찾았다!! ㅠㅠ homebrew로 설치했으니 저기 하위폴더 어딘가에 있겠지. .하면서 찾았다.</p>
<br>

<h1 id="3-theme--powerline-적용">3. THEME : powerline 적용</h1>
<p>그리고 나서 테마를 적용할 powerline을 git clone으로 받아온다.
현재는 10k 버전까지 나와있지만 9k 를 받아보자…!</p>
<pre><code class="language-bash">git clone https://hithub.com/powerline/fonts.git /tmp/powerline9k0fonts &amp;&amp; cd $_</code></pre>
<p>위 명령어를 입력하면 <code>powerline9k를 git clone</code> 과 동시에 <code>해당 위치로 이동</code> 된다.
해당 위치에서 다음의 명령어를 입력한다.</p>
<pre><code class="language-bash">sh ./install.sh</code></pre>
<br>

<h1 id="4-oh-my-zsh-설치">4. oh-my-zsh 설치</h1>
<p>oh-my-zsh은 ZSH에 가장 널리 사용되는 플러그인 프레임 워크이며 많은 내장 플러그인과 테마도 제공된다.</p>
<p>먼저 설치 명령어는 다음과 같다.</p>
<pre><code class="language-bash">sh -c &quot;$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;</code></pre>
<p><code>oh-my-zsh</code>을 사용하면 편리한점들이 많다.</p>
<h3 id="1-상위-폴더-이동하기">1. 상위 폴더 이동하기</h3>
<p>예를들면 기존 bash가 기본 shell일 경우 상위 폴더의 이동은 다음과 같다.</p>
<pre><code class="language-bash"># 상위
cd ..

# 두개 위
cd ../..</code></pre>
<p>하지만 oh-my-zsh이 적용된 zsh쉘을 사용하면 다음과 같이 이동할 수 있다.</p>
<pre><code class="language-bash"># 상위
cd ..

# 두개 위
cd …</code></pre>
<br>

<h3 id="2-tap키로-명령어-자동완성시키기">2. tap키로 명령어 자동완성시키기</h3>
<p>명령어를 입력할때 예를들면 rainbow를 쳐야한다고 가정하자.
rain까지 치고 <code>tap</code>키를 누르면 rain으로 시작하는 여러 명령어들이 후보로 보여지는데 이때 <strong>후보가 rainbow 단 한가지 라면 해당 명령어가 자동완성 된다</strong>
아주 편하다!</p>
<br>

<p>이거말고도 많다!</p>
<br>

<h1 id="5-테마-적용하기">5. 테마 적용하기</h1>
<p>터미널 테마를 적용해보자.
<code>vi ~/.zshrc</code>로 들어가면 다음과 같은 변수가 있을 것이다</p>
<pre><code class="language-bash">ZSH_THEME = “powerline9k/powerline9k”</code></pre>
<p>여기를 적용할 테마로 변경해주자! 보통 <code>agnoster</code>를 많이 쓰니까 나도 이걸로 저장한다.</p>
<pre><code class="language-bash">ZSH_THEME = “agnoster”</code></pre>
<p>그리고 터미널에 계속해서 username이 뜨는데 길기만하고 보기싫다! 하면 다음과 같은 변수도 저장해준다</p>
<pre><code class="language-bash"># DEFAULT_USER을 명시함으로써 이름 가리기
DEFAULT_USER = “chaehyun”</code></pre>
<p>이때 내 username을 입력하면 된다.</p>
<p>그리고 나서 <code>source ~/.zshrc</code>로 수정사항을 반영시켜준다. (혹은 그냥 teminal을 껐다 켜도 된다.)</p>
<br>

<h1 id="6-font-깨짐-수정">6. Font 깨짐 수정</h1>
<p>캡쳐본은 없지만 위의 것을 다 적용하고 나면 일부 폰트가 깨져서 ?박스가 뜨더라!
Iterm2 환경설정에서 font를 powerline9k에 맞게 변경해주면 된다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/a15db159-dde6-407e-b45a-518d9544fd19/%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-06-09%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.49.09.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/caeb9cf6-2049-4a22-bcdb-752b2695309c/%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-06-09%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.09.20.png" alt=""></p>
<p>그럼 끝!</p>
<br>

<h1 id="-테마-import하기">+) 테마 import하기</h1>
<p>색상변경으로 커스텀 하거나 여러 테마가 저장되어있는 폴더를 다운받아서 Import 해주어도 된다! </p>
<pre><code class="language-bash">http://github.com/mbadolato/iTerm2-Color-Schemes</code></pre>
<p>에서 다운받으면 된다.</p>
<br>

<h1 id="powerline10k">powerline10k</h1>
<p>터미널에서 위치바의 모양이 맘에들지않는다면...! 내 원래 설정대로 하고싶다면..! <code>powerline9k</code>가 아닌 <code>powerline10k</code>로 설치하면 된다! 끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[mongoDB] DB생성과 CRUD]]></title>
            <link>https://velog.io/@c_hyun403/mongoDB-DB%EC%83%9D%EC%84%B1%EA%B3%BC-CRUD</link>
            <guid>https://velog.io/@c_hyun403/mongoDB-DB%EC%83%9D%EC%84%B1%EA%B3%BC-CRUD</guid>
            <pubDate>Sun, 06 Jun 2021 14:06:27 GMT</pubDate>
            <description><![CDATA[<p>설치 이후 DB를 생성해보자</p>
<hr>
<h1 id="1-mongodb-실행">1. mongodb 실행</h1>
<p>지난번 언급했던 것 처럼 brew를 통해 mongodb server를 실행시킨다. 그리고 mongodb client역시 실행시켜 준다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/5db99dd6-ad68-48f2-9bc8-9f15c4bcb4b0/%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-06-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.28.36.png" alt=""> ...아래 명령어 중략...</p>
<p>이제 mongodb 커맨드를 입력할수 있는 shell이 활성화 되었다.</p>
<pre><code class="language-bash"># mongo
&gt;</code></pre>
<br>

<h1 id="--------------------------------">--------------------------------</h1>
<br>

<h1 id="2-db-생성">2. DB 생성</h1>
<hr>
<h4 id="-현재-사용중인-db-확인">+) 현재 사용중인 DB 확인</h4>
<p>생성하기 전에 현재 사용중인 DB가 무엇인지 확인해 보자</p>
<pre><code class="language-sql">&gt; db
test</code></pre>
<p>test라는 db가 있는걸 확인할 수 있다.
난.. 아무것도 안만든거같은데...? 저번에 설치하면서 만들었나보다..</p>
<hr>
<p>진짜로 만들어보자! mongodb에서 DB를 생성하는 명령어를 입력해 준다. 그리고 현재 사용중인 db를 확인해보자.</p>
<blockquote>
<p>use $사용할db이름</p>
</blockquote>
<pre><code class="language-sql">&gt; use mongo_practice1
switched to db mongo_practice1
&gt; db
mongo_practice1</code></pre>
<p>보다싶이 <code>use</code>명령어로 mongodb를 생성하면 생성과 동시에 해당 db가 활성되 된다.</p>
<h3 id="-db-list-확인">+) DB list 확인</h3>
<p>현재 어떤 db가 존재하는지 확인해 보자</p>
<pre><code class="language-sql">&gt; show dbs
admin     0.000GB
config    0.000GB
dbsparta  0.000GB
local     0.000GB</code></pre>
<p>기존에 있던 test와 방금 내가 만든 mongo_practice1이 목록에 없다!</p>
<p><strong>최소 한개이상의 document를 생성해야 List목록에 보이기 때문이다</strong></p>
<br>

<h1 id="3-mongodb의-crud">3. mongoDB의 C.R.U.D</h1>
<p>document를 생성한다는 것은 즉, CRUD에서 Create를 한다는 의미이다. 이를 위해선 먼저 mongoDB의 CRUD 명령어를 알아야 한다.</p>
<p>참고 사이트 : <a href="https://myjamong.tistory.com/57?category=835222">https://myjamong.tistory.com/57?category=835222</a></p>
<br>

<h3 id="--생성--createinsert">- 생성 : Create(insert)</h3>
<p>이때 collection명을 새로 입력하면 해당 collection이 즉시 생성됨가 동시에 그 collection의 document가 작성된다.</p>
<blockquote>
</blockquote>
<p>db.<span style="color:blue">$collection</span>.<span style="color:red">insert</span>({key:value})</p>
<br>

<h3 id="--조회--readfind">- 조회 : Read(find)</h3>
<blockquote>
<p><strong>전체조회</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">find</span>()</p>
</blockquote>
<p><strong>조건에 따른 조회</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">find</span>({key:value})</p>
<p>&lt;조건에 따른 조회 예시&gt;</p>
<br>


<h3 id="--갱신--update">- 갱신 : Update</h3>
<blockquote>
<p><strong>전체 document 수정</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">update</span>({기존 document를 특정 지을 key:value}, {새로변경할 내용의 key:value})</p>
</blockquote>
<p><strong>특정 값 수정 or 새로운 값 생성</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">update</span>({기존 document를 특정 지을 key:value}, {<span style="color:purple">$set</span> : {추가할 key : 추가할 value})</p>
<br>


<h3 id="--삭제--delete">- 삭제 : Delete</h3>
<blockquote>
</blockquote>
<p><strong>특정 field 삭제 (update 사용 - unset)</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">update</span>({기존 document를 특정 지을 key:value}, {<span style="color:purple">$unset</span> : {삭제할 key : 해당 value})</p>
<blockquote>
</blockquote>
<p><strong>특정 document삭제</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">remove</span>({조건 key : 해당 value})</p>
<blockquote>
</blockquote>
<p><strong>특정 collection 삭제</strong>
db.<span style="color:blue">$collection</span>.<span style="color:red">update</span>({})</p>
<br>

<hr>
<br>

<h1 id="4-document-생성하기">4. document 생성하기</h1>
<p>위 명령어를 토대로 <code>mongo_practice1</code>에 새로운 collection과 document를 생성해보자</p>
<blockquote>
</blockquote>
<p>이름 / 생년월일 / 이메일 / 취미(1~3개) / 코로나백신접종유무(T/F)</p>
<pre><code class="language-sql">&gt; use mongo_practice1
&gt; db.users.insert({name : &quot;jello&quot;, birth : 950403, email : &quot;jello@test.com&quot;, hobby : [&quot;hiking&quot;, &quot;watch movie&quot;], vaccination : False})</code></pre>
<p>값으로 <code>boolean</code>값을 바로 사용할수 있을줄 알았다! 그런데 에러가 발생했다.</p>
<pre><code class="language-bash">uncaught exception: ReferenceError: False is not defined :</code></pre>
<p>간단하다. 대문자 말고 소문자로 입력하면 된다.</p>
<pre><code class="language-sql">&gt; use mongo_practice1
&gt; db.users.insert({name : &quot;jello&quot;, birth : 950403, email : &quot;jello@test.com&quot;, hobby : [&quot;hiking&quot;, &quot;watch movie&quot;], vaccination : false})
WriteResult({ &quot;nInserted&quot; : 1 })</code></pre>
<p>이후 확인!</p>
<pre><code class="language-bash">&gt; use mongo_practice1
&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
&gt; show dbs
admin            0.000GB
config           0.000GB
dbsparta         0.000GB
local            0.000GB
mongo_practice1  0.000GB</code></pre>
<p><strong>💡 참고로 mongoDB는 document의 id로 <code>_id</code>라는 이름에 값이 자동 생성된다!</strong></p>
<p>몇개를 더 만들어보자</p>
<pre><code class="language-bash">&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9b91abf6cdd64bfc939&quot;), &quot;name&quot; : &quot;보라돌이&quot;, &quot;birth&quot; : 901105, &quot;email&quot; : &quot;purple@test.com&quot;, &quot;hobby&quot; : [ &quot;talking&quot;, &quot;listening music&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9d91abf6cdd64bfc93a&quot;), &quot;name&quot; : &quot;뚜비&quot;, &quot;birth&quot; : 930705, &quot;email&quot; : &quot;green@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9fd1abf6cdd64bfc93b&quot;), &quot;name&quot; : &quot;나나&quot;, &quot;birth&quot; : 940205, &quot;email&quot; : &quot;yellow@test.com&quot;, &quot;hobby&quot; : [ &quot;walking, singing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcda181abf6cdd64bfc93c&quot;), &quot;name&quot; : &quot;뽀&quot;, &quot;birth&quot; : 200605, &quot;email&quot; : &quot;red@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : false }</code></pre>
<p>😢 자세히보면 나나의 hobby에서 따옴표에 오타가 있다.. 근데 왜 에러가 안나....? 진짜 입력한대로 다 저장되는거니,,,?</p>
<p>바로 수정해주자!</p>
<pre><code class="language-bash">&gt; use mongo_practice1 #이건그냥 보험삼아 해주는것. 혹여나 다른db 쓰고있을경우도 있으니 습관처럼 입력하자
&gt; db.users.update({name:&quot;나나&quot;}, {$set : {hobby : [&quot;walking&quot;, &quot;singing&quot;]}})
WriteResult({ &quot;nMatched&quot; : 1, &quot;nUpserted&quot; : 0, &quot;nModified&quot; : 1 })
&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9b91abf6cdd64bfc939&quot;), &quot;name&quot; : &quot;보라돌이&quot;, &quot;birth&quot; : 901105, &quot;email&quot; : &quot;purple@test.com&quot;, &quot;hobby&quot; : [ &quot;talking&quot;, &quot;listening music&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9d91abf6cdd64bfc93a&quot;), &quot;name&quot; : &quot;뚜비&quot;, &quot;birth&quot; : 930705, &quot;email&quot; : &quot;green@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9fd1abf6cdd64bfc93b&quot;), &quot;name&quot; : &quot;나나&quot;, &quot;birth&quot; : 940205, &quot;email&quot; : &quot;yellow@test.com&quot;, &quot;hobby&quot; : [ &quot;walking&quot;, &quot;singing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcda181abf6cdd64bfc93c&quot;), &quot;name&quot; : &quot;뽀&quot;, &quot;birth&quot; : 200605, &quot;email&quot; : &quot;red@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : false }</code></pre>
<br>

<hr>
<h1 id="-이것저것-수행">+) 이것저것 수행</h1>
<h3 id="1-조건에-해당하는-여러-document수정하기">1. 조건에 해당하는 여러 document수정하기</h3>
<p>마치 Django의 ORM에서 filter를 통해 queryset에 해당하는 모든 객체의 값을 수정한것 처럼 조건에 해당하는 여러 객체의 값을 갱신할 수 있다.</p>
<p>백신을 맞은 객체에 대해 가장 좋아하는 과일 : strawberry field를 생성해 준다.</p>
<pre><code class="language-bash">&gt; use mongo_practice1
&gt; db.users.update({vaccination : true}, {fruit : &quot;strawberry&quot;}, {multi : true})</code></pre>
<p>여러가지 조건에 대해 수정할 것이기 때문에 <strong>multi의 값에 true라고 지정해 주어야 한다</strong>
안하면 가장 처음의 객체에만 수정사항이 반영된다.</p>
<h4 id="☠️-실수">☠️ 실수..</h4>
<p>... 흠.. 실수했다.
먼저 <code>$set</code> 명령어를 쓰지 않아 해당하는 document의 내용이 싹 갈아엎어졌다. 그나마 multi값을 주지 않았기에 가장 먼저 해당되는 객체만 수정되었다.</p>
<pre><code class="language-bash">&gt; db.users.update({vaccination : true}, {fruit : &quot;strawberry&quot;})
WriteResult({ &quot;nMatched&quot; : 1, &quot;nUpserted&quot; : 0, &quot;nModified&quot; : 1 })
&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9b91abf6cdd64bfc939&quot;), &quot;name&quot; : &quot;보라돌이&quot;, &quot;birth&quot; : 901105, &quot;email&quot; : &quot;purple@test.com&quot;, &quot;hobby&quot; : [ &quot;talking&quot;, &quot;listening music&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9d91abf6cdd64bfc93a&quot;), &quot;fruit&quot; : &quot;strawberry&quot; }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9fd1abf6cdd64bfc93b&quot;), &quot;name&quot; : &quot;나나&quot;, &quot;birth&quot; : 940205, &quot;email&quot; : &quot;yellow@test.com&quot;, &quot;hobby&quot; : [ &quot;walking&quot;, &quot;singing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcda181abf6cdd64bfc93c&quot;), &quot;name&quot; : &quot;뽀&quot;, &quot;birth&quot; : 200605, &quot;email&quot; : &quot;red@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : false }</code></pre>
<p>다시 원상복구 하고 제대로 갱신해 보자</p>
<pre><code class="language-bash">&gt; db.users.update({fruit:&quot;strawberry&quot;}, {name : &quot;뚜비&quot;, birth : 930705, email : &quot;green@test.com&quot;, hobby : [&quot;dancing&quot;], vaccination : true})
WriteResult({ &quot;nMatched&quot; : 1, &quot;nUpserted&quot; : 0, &quot;nModified&quot; : 1 })
&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9b91abf6cdd64bfc939&quot;), &quot;name&quot; : &quot;보라돌이&quot;, &quot;birth&quot; : 901105, &quot;email&quot; : &quot;purple@test.com&quot;, &quot;hobby&quot; : [ &quot;talking&quot;, &quot;listening music&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9d91abf6cdd64bfc93a&quot;), &quot;name&quot; : &quot;뚜비&quot;, &quot;birth&quot; : 930705, &quot;email&quot; : &quot;green@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9fd1abf6cdd64bfc93b&quot;), &quot;name&quot; : &quot;나나&quot;, &quot;birth&quot; : 940205, &quot;email&quot; : &quot;yellow@test.com&quot;, &quot;hobby&quot; : [ &quot;walking&quot;, &quot;singing&quot; ], &quot;vaccination&quot; : true }
{ &quot;_id&quot; : ObjectId(&quot;60bcda181abf6cdd64bfc93c&quot;), &quot;name&quot; : &quot;뽀&quot;, &quot;birth&quot; : 200605, &quot;email&quot; : &quot;red@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : false }

&gt; db.users.update({vaccination : true}, {$set : {fruit : &quot;strawberry&quot;}}, {multi : true})
WriteResult({ &quot;nMatched&quot; : 2, &quot;nUpserted&quot; : 0, &quot;nModified&quot; : 2 })
&gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;60bcd8101abf6cdd64bfc938&quot;), &quot;name&quot; : &quot;jello&quot;, &quot;birth&quot; : 950403, &quot;email&quot; : &quot;jello@test.com&quot;, &quot;hobby&quot; : [ &quot;hiking&quot;, &quot;watch movie&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9b91abf6cdd64bfc939&quot;), &quot;name&quot; : &quot;보라돌이&quot;, &quot;birth&quot; : 901105, &quot;email&quot; : &quot;purple@test.com&quot;, &quot;hobby&quot; : [ &quot;talking&quot;, &quot;listening music&quot; ], &quot;vaccination&quot; : false }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9d91abf6cdd64bfc93a&quot;), &quot;name&quot; : &quot;뚜비&quot;, &quot;birth&quot; : 930705, &quot;email&quot; : &quot;green@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : true, &quot;fruit&quot; : &quot;strawberry&quot; }
{ &quot;_id&quot; : ObjectId(&quot;60bcd9fd1abf6cdd64bfc93b&quot;), &quot;name&quot; : &quot;나나&quot;, &quot;birth&quot; : 940205, &quot;email&quot; : &quot;yellow@test.com&quot;, &quot;hobby&quot; : [ &quot;walking&quot;, &quot;singing&quot; ], &quot;vaccination&quot; : true, &quot;fruit&quot; : &quot;strawberry&quot; }
{ &quot;_id&quot; : ObjectId(&quot;60bcda181abf6cdd64bfc93c&quot;), &quot;name&quot; : &quot;뽀&quot;, &quot;birth&quot; : 200605, &quot;email&quot; : &quot;red@test.com&quot;, &quot;hobby&quot; : [ &quot;dancing&quot; ], &quot;vaccination&quot; : false }</code></pre>
<br>

<h3 id="2-gui로-확인해보기">2. GUI로 확인해보기</h3>
<p>전에 Studio 3T를 설치해두었다. 내 로컬 mongoDB와 연결하고 확인해보자!</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/e09e33be-4ac0-4b78-8dc1-10418ad682e2/%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-06-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.42.20.png" alt=""></p>
<p>data type을 보면 <code>ObjectId / String / Double / Array / Bool</code> 형태가 있음을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/1b62df5a-1586-4a69-9db7-0ea6facc4b57/%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-06-06%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.43.39.png" alt=""></p>
<br>

<h1 id="---------------------------------1">--------------------------------</h1>
<br>

<h1 id="5-db-삭제하기">5. DB 삭제하기</h1>
<p>더이상 필요없는 DB는 삭제하자!</p>
<pre><code class="language-sql">&gt; use #삭제할db이름
&gt; db.dropDatabase()</code></pre>
<p>아직 mongo_practice1은 삭제안해...!!🔥</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] docker-compose]]></title>
            <link>https://velog.io/@c_hyun403/Docker-docker-compose</link>
            <guid>https://velog.io/@c_hyun403/Docker-docker-compose</guid>
            <pubDate>Wed, 19 May 2021 09:45:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로젝트 주소 : <a href="https://github.com/JELLO-Kim/Labs_forum.git">https://github.com/JELLO-Kim/Labs_forum.git</a></p>
</blockquote>
<p>과제를 수행하면서 다음과 같은 조건이 있었다.
<strong>[DB 설정]</strong></p>
<ol>
<li>내장형 DB사용</li>
<li>Docker사용 : DB종류 제한 X</li>
</ol>
<p>내장형 DB의 경우 sqllite를 사용하면 될것 같지만 요새 Docker에 대해 조금 알아보기도 했고, 마침 docker-compose까지 구현해보고 싶었기에 2번의 방법을 사용하기로 하였다. <em>지옥의 시작</em></p>
<br>

<p>그리고 글을 정리하면서 도움을 받았던 글! : <a href="https://velog.io/@sayxyoung/docker-file%EA%B3%BC-compose"> sayxoung님의 블로그</a></p>
<hr>
<br>

<h1 id="docker-compose">Docker compose</h1>
<p>Docker compose란 <strong>여러 개의 docker를 정의하고 실행하는 툴</strong>을 의미한다. 내가 하려는 것도 docker를 이용해서 API image와 DB image를 하나의 container로 실행하려고 하는 것이다.</p>
<br>

<h2 id="db의-이미지화">DB의 이미지화</h2>
<p>이전에 프로젝트 <code>JELLOgram</code>으로 했던 것은 <strong>API를 image로 빌드하여 DB를 제외한 나머지를 모두 도커로 실행하도록 한것</strong>이다. 빌드된 이미지를 실행하게 되면 그 안에 저장된 내 로컬DB에 대한 것도 함께 연결되는것이었다.</p>
<p>즉, <code>API = 도커</code> / <code>DB = local DB</code> 의 형태였다.</p>
<p>하지만 과제나 배포와 같은 상황에서 API만 도커로 실행하는것은 의미가 없다. (연결된 DB는 작성자의 로컬 디비인데..? 아니면 RDS로?)</p>
<blockquote>
<p>** RDS 는? **
RDS를 사용해 DB를 관리할 수도 있다. 하지만 <strong>배포를 할땐 DB에 접속할 비밀번호를 공유해야 한다.</strong> 내 비밀번호를 깃헙에 올린순 없다..</p>
</blockquote>
<p>따라서 <strong>DB도 이미지로 빌드! 그리고 API 이미지랑 같이 실행!</strong>하는 방법이 <strong>Docker compose</strong>인 것이다.</p>
<br>
<br>

<h1 id="----------------------------">----------------------------</h1>
<br>
<br>

<h1 id="requirements">Requirements</h1>
<p>먼저 docker-compose의 설치가 필요하다. 나같은 경우 지난번 Docker를 설치할때 함께 설치해주었었다.</p>
<p><strong>🔥 너무 옛날 버전이면 이후 제대로 실행되지 않는다! 주기적으로 업데이트를 진행해 줄 것!</strong></p>
<pre><code class="language-bash">pip install docker-compose</code></pre>
<p>그리고 나서 API를 위한 <code>Dockerfile</code>과 docker-compose를 위한 <code>docker-compose.yml</code> 파일을 만들어 준다.</p>
<br>

<h3 id="dockerfile">Dockerfile</h3>
<p>이의 경우 지난번 블로깅을 통해 기록해 놓았다. 이중에서 다른 점은 <strong>gunicorn으로 실행하지 않는 것</strong> runserver의 조건은 Dockerfile이 아닌 이후에 작성할 docker-compose.yml에 작성한다.
<img src="https://images.velog.io/images/c_hyun403/post/8a7ab503-e9f3-429a-930e-3171ca594577/image.png" alt=""></p>
<br>

<h3 id="docker-composeyml">docker-compose.yml</h3>
<p>확장자명 <code>.yml</code>을 주의하자!
(참고로 yml 파일을 vscode로 열어보면 귀여운 <span style="color:red">핑크고래</span>가 보인다 ㅎㅎ Dockerfile은 <span style="color:blue">파란고래</span>!)</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/5e84410e-c47e-495e-8162-83bed88e079e/image.png" alt=""></p>
<p>한줄씩 살펴보자</p>
<h4 id="공통-부분">공통 부분</h4>
<ul>
<li>version : Docker의 버전이다. 3.X 도 있지만 3이라고만 표기하면 해당 버전들 중 가장 최신 버전이 적용된다.</li>
<li>services : 코드의 시작</li>
<li>forum / db : app과 db를 정의한다. 이름지어주는것은 자유지만 간단하게 명시했다!</li>
</ul>
<h4 id="forum-">forum :</h4>
<ul>
<li><code>build: context: . dockerfile: ./Dockerfile</code> : 적용될 Dockerfile의 위치와 이름</li>
<li><code>command</code> : Docker이미지가 빌드되고 실행될때 수행될 명령어들이다. 저기에 있는 <code>wait-for-it</code>에 관한것은 아래에서 다루도록 한다. / mm -&gt; mg -&gt; runserver 해주어서 이미지가 빌드되면 바로 서버가 가동된다.</li>
<li><code>volumes</code> : 실행되는 위치를 의미한다. 이미지가 실행될때 해당 위치가 형성된다.</li>
<li><code>ports</code> : 허용하는 포트정보</li>
<li><code>depends_on</code> : 도커의 이미지들끼리는 <strong>서로 인지하지 못한다</strong> 때문에 app의 이미지 정보에 <strong>db와 관련이 있음을 명시해 주어야 한다. 이때 사용하는 것이 <code>depends_on : db</code> 이다.</strong></li>
</ul>
<h4 id="db-">db :</h4>
<ul>
<li><code>image</code> : mysql ver5.7로 이미지를 만든다.</li>
<li><code>environment</code> : 생성된 mysql의 환경설정이다. 여기서 <strong>db의 이름 / db접속 비밀번호</strong>를 지정해 준다.</li>
<li><code>command</code> : DB가 생성될때 실행되는 명령어들이다. 나의 경우 DB의 data들이 한국어이기 때문에 <code>utf8mb4</code>설정을 진행해 준다.</li>
<li><code>volumes</code> : DB가 생성될때 <strong>sql파일을 사용해 자동으로 data를 채워준다.</strong> 따라서 경로에 맞게 최상위 directory에  <code>data</code>라는 폴더를 만들고 그 안에 <code>sql</code> 파일을 저장한다. (이름은 상관없다! 자동으로 sql파일을 사용해 data를 넣어준다)</li>
<li><code>expose</code> : port 번호를 의미한다. 도커 내부에서만 실행할 것이기 때문에 <code>ports 8000:8000</code> 으로 작성한것과 차이는 딱히 없는것 같다.</li>
</ul>
<br>

<h3 id="wait-for-itsh">wait-for-it.sh</h3>
<p>이 파일의 경우 작성된 코드들을 건들일 필요는 없다. 이미 잘 작성되어있기 때문에 <strong>공유</strong>하면 된다.
위에서 우리가 의도한 것은 docker-compose로 app과 db의 이미지를 <strong>함께</strong>실행하는 것이다. 하지만 app의 이미지가 실행될때 <strong>mm, mg</strong>를 수행한다. 알다싶이 이를 수행하기 위해선 <strong>DB가 먼저 준비되어 있어야 한다.</strong></p>
<p>따라서 이미지가 실행될때 <strong>DB의 이미지가 먼저 실행된 후 app의 이미지가 실행되도록</strong> app의 command에 <code>wait-for-it</code>를 입력했던 것!</p>
<br>

<h1 id="실행하기">실행하기</h1>
<p>위의 파일들을 모두 작성한 후 <code>requirements.txt</code>도 꼼꼼히 기록한다. =&gt; 놓치면 바로 에러난다.</p>
<p>그리고 나서 githup에 코드를 모두 올린 후 새로운 폴더에서 해당 프로젝트를 clone받아 온다.</p>
<pre><code class="language-bash">git clone $프로젝트

cd $프로젝트_최상위_directory

docker-compose up</code></pre>
<p>이거면 끝! 에러가 나지 않는 다면 서버가 가동될 것이고, 빠르게 httpie test로 데이터를 확인해 볼 수 있다!</p>
<br>

<hr>
<br>

<h2 id="🔥-errors">🔥 ERRORs</h2>
<br>

<h4 id="requirementstxt">requirements.txt</h4>
<p>이거저거 건드리다가 requirements를 제대로 작성하지 않았다. Django를 빼먹은 것. 어쩐지 Django를 실행할 수 없다고 뜨더라.. 잊지말자!</p>
<br>

<h4 id="permission-denied-1--임시-해결">permission denied #1 : 임시 해결</h4>
<p>db image는 문제가 없었는데 app image가 제대로 빌드되지 않았다. 계속해서 <strong>script와 관련해 permission denied</strong>라고 뜨는것.</p>
<p>이를 해결하기 위해선 <code>wait-for-it.sh</code> 파일의 권한을 풀어주어야 하는데 처음에는 <code>docker-compose.yml</code>의 command에 해당 명령어를 입력했었다.</p>
<pre><code class="language-bash"># docker-compose.yml
command: &gt;
    bash -c ” RUN chmod +x ./wait-for-it.sh
    &amp;&amp; ./wait-for-it.sh db:3306 -t 15
    &amp;&amp; python manage.py makemigrations
    &amp;&amp; python manage.py migrate
    &amp;&amp; python manage.py runserver 0.0.0.0:8000&quot;</code></pre>
<p>그런데 이렇게 입력하고 나니 RUN 명령어를 인식하지 못한다고 뜬다!
그래서 해당 줄을 제거하고 일단 docker-compose를 실행하기 전 해당 환경에서 직접 권한을 풀어주었다.</p>
<pre><code class="language-bash"># docker 실행할 환경
chmod +x ./ wait-for-it.sh</code></pre>
<p>그리고 나서 실행!
굿!</p>
<h4 id="permission-denied-2--해결">permission denied #2 : 해결!</h4>
<p>수정!!!!! 2021.05.19
코드를 수정해서 다시 진행해 보았다.</p>
<pre><code class="language-bash"># docker-compose.yml
command: &gt;
    bash -c ”chmod +x ./wait-for-it.sh
    &amp;&amp; ./wait-for-it.sh db:3306 -t 15
    &amp;&amp; python manage.py makemigrations
    &amp;&amp; python manage.py migrate
    &amp;&amp; python manage.py runserver 0.0.0.0:8000&quot;</code></pre>
<p><code>RUN</code>을 빼고 실행하니 docker가 실행되면서 다음과 같은 메세지가 출력되더라.</p>
<pre><code class="language-bash">forum_1  | wait-for-it.sh: waiting 15 seconds for db:3306
...
#생략
...
forum_1  | wait-for-it.sh: db:3306 is available after 7 seconds
forum_1  | No changes detected
forum_1  | Operations to perform:
forum_1  | Apply all migrations: admin, auth, contenttypes, questions, sessions, users
forum_1  | Running migrations:
forum_1  | No migrations to apply.
forum_1  | Watching for file changes with StatReloader</code></pre>
<p>실행 성공!</p>
<br>

<hr>
<h2 id="🛠-주의">🛠 주의!</h2>
<p>DB에 대한 정보를 settings.py 혹은 보안설정으로 my_settings.py 에 기록했던것을 기억하는가!</p>
<p>docker를 이용해 DB도 이미지로 빌드하게 되면 해당 프로젝트의 연결되는 DB는 내 로컬 / RDS 가 아닌 <strong>docker image실행으로 만들어진 이미지 속의 DB</strong>이다. 때문에 docker-compose파일에서 지정한 <strong>DB이름과 비밀번호로 settings.py도 수정한다.</strong> 또한 SECRET_KEY의 경우에도 실무에서는 회사 내에서만 공유하겠지만 일단 나는 <code>배포 / 과제제출</code> 용이기 때문에 공개하기로 한다! (임의의 string으로 지정했다.)</p>
<br>

<hr>
<br>

<h2 id="🙆♀️-실행-성공">🙆‍♀️ 실행 성공</h2>
<p><img src="https://images.velog.io/images/c_hyun403/post/ad54e041-4372-4b15-8ce3-20d19421f433/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-05-19%20%EC%98%A4%ED%9B%84%2010.43.27.png" alt="">
<code>docker-compose가 무사히 실행된 화면 &amp; httpie 확인 화면 &amp; docker app으로 보는 container 상태</code></p>
<br>

<p>이후 docker를 종료하고 다시 실행해 보았다. 이때는 터미널에서 실행하려 하니 해당 container가 존재하지 않는다고 해서... <em>읭?</em> docker app에서 실행해 주었다.</p>
<pre><code class="language-bash">#안됌
❯ docker start labs_forum
Error response from daemon: No such container: labs_forum
Error: failed to start containers: labs_forum</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/a0dbd02b-78b8-49ed-a1d4-8383d4e4ed66/%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-05-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.20.38.png" alt=""></p>
<p>통신 확인 완료!!</p>
<br>

<h2 id="🏷-image로-만든-db-확인">🏷 image로 만든 DB 확인</h2>
<p>docker container로 실행된 db image를 확인해 보자</p>
<pre><code class="language-bash">docker exec -t {db container이름} bash
mysql -u root -p
password &gt; {docker-compose 작성할때 입력한 password 입력}</code></pre>
<br>
<br>

<h1 id="-----------------------------1">----------------------------</h1>
<br>
<br>

<h1 id="끝">끝!</h1>
<p>꼬박 하루가 걸린 docker-compose 였다! 처음 코드를 열어봤을때 진짜 무슨..말이고.. 했는데 하나하나 뜻을 보니 이 코드는 어디서, 왜 필요한지 이해하면서 점점 재밌어 졌다!</p>
<p>이제 DB도 도커로 관리!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Caching] Redis]]></title>
            <link>https://velog.io/@c_hyun403/Caching-Redis</link>
            <guid>https://velog.io/@c_hyun403/Caching-Redis</guid>
            <pubDate>Sun, 16 May 2021 08:35:29 GMT</pubDate>
            <description><![CDATA[<p>챗봇관련 과제를 받았는데 가능하다면 부하 관련하여 성능을 향상시켜달라는 요청도 있었다. 이와 관련하여 원래 공부하고, 적용해보려고 했던 <strong>Redis</strong>를 사용하기로 했다.!
<em>짧은 시간 안에 얼마나 깊게 파악할수 있을진 모르겠지만 과제를 떠나서 일단 개념 정리하고, 적용해 보는것을 목표로!</em></p>
<br>

<h1 id="메모리-캐싱">메모리 캐싱</h1>
<p>&quot;메모리 캐싱&quot;을 하는 이유는 무엇?? 이라고 하면 다음과 같다.</p>
<blockquote>
</blockquote>
<ul>
<li>서비스 요청의 증가 (매우많이)</li>
<li>DB요청이 매우 많아진다 =&gt; DB server의 부하가 증가한다</li>
<li>🔥 메모리 캐시 적용 🔥</li>
<li>성능 및 처리 속도가 향상된다. <strong>(DB Read의 부하 감소 가능)</strong></li>
</ul>
<p>그렇다면 <strong>DB server의 부하</strong>란 무엇인가?</p>
<h2 id="db-server-부하">DB server 부하</h2>
<br>
<br>

<h1 id="---------------------------">---------------------------</h1>
<br>
<br>

<h1 id="성능-알아보기-">성능 알아보기 :</h1>
<p>측정 대상은 지난 1차 프로젝트때 작성한 &quot;Sweethome&quot; 의 posting 관련 로직이다.</p>
<h1 id="1-loadtest">1. loadtest</h1>
<p>먼저 <code>npm install</code>로 설치되는 loadtest를 통해 간단히 호출에 걸리는 시간을 알아보자.
설치방법은 공식문서를 참고하였다.
<code>npm install loadtest</code>로는 가동이 안되어서
<code>sudo npm install -g loadtest</code>로 설치해주었다.</p>
<h2 id="1-단일-쓰레드-환경">1) 단일 쓰레드 환경</h2>
<p>설치 후 <code>python manage.py runserver</code>로 서버를 켠 뒤 명령어를 통해 loadtest 결과 확인.</p>
<pre><code class="language-bash">loadtest -n 10 http://127.0.0.1:8000/posting</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/d51a6f86-ac84-434f-a1b8-a734988a8a45/image.png" alt=""></p>
<p>결과 : 24초</p>
<p>10명의 유저 말고 100명의 유저일때를 가정해 테스트해 보았다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/ec898683-2ea9-4da3-a5ac-9beb32556156/image.png" alt=""></p>
<p>결과 : 245초</p>
<br>

<h2 id="2-멀티-쓰레드-환경">2) 멀티 쓰레드 환경</h2>
<p>gunicorn 을 사용해 멀티쓰레드 환경일때도 확인해 보았다.</p>
<pre><code class="language-bash">loadtest -n 10 http://0.0.0.0:8000/posting</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/29207405-01a4-4956-8496-a1f2e83bb28c/image.png" alt=""></p>
<p>결과 : 5초</p>
<p>100명일때도 확인해 보았다.
<img src="https://images.velog.io/images/c_hyun403/post/66882422-902f-4fe8-98ab-99419b770927/image.png" alt=""></p>
<p>결과 : 58초</p>
<p>약 5배의 속도차이를 보였다.</p>
<h1 id="2-locust">2. locust</h1>
<p>다음은 locust를 통해 확인해 보았다.</p>
<p>당시 코드를 보면 다음과 같다.
<img src="https://images.velog.io/images/c_hyun403/post/bf483ac8-5316-4fb6-bc92-373f1e8c09f2/image.png" alt=""></p>
<p>이왕 성능체크하는거 <code>prefetch_related</code>와 <code>select_related</code>를 사용하기 전과도 비교해 보도록 하자. 그래서 코드를 다음과 같이 수정하였다.
<img src="https://images.velog.io/images/c_hyun403/post/a53c5809-81db-4a6d-b32f-3d2c6a502f00/image.png" alt=""></p>
<br>
<br>

<h2 id="1-file-작성">1) file 작성</h2>
<p>부하성능을 알아보기 위해 선택한 것은 <strong>locust</strong>를 사용하는 방법이다.
이는 python에서 제공하는 모듈로 간단히 <code>pip install locust</code>를 통해 설치할 수 있다.</p>
<p>이후 locust를 실행시킬 명령어들이 작성된 파일을 작성한다.</p>
<p>기본적인 틀은 <a href="https://docs.locust.io/en/stable/writing-a-locustfile.html#writing-a-locustfile">공식문서</a>를 참고했다.</p>
<pre><code class="language-python"># locust_test.py
from locust import User, task, between

class PostingReadTest(User):
    wait_time = between(1, 2.5)

    @task
    def my_task(self):
        self.client.get(&quot;/posting&quot;)</code></pre>
<br>

<h3 id="locust-attribute">locust attribute</h3>
<blockquote>
<h4 id="wait_time">wait_time</h4>
</blockquote>
<p>wait_time 이라는 메소드는 증가되는 user들 간의 딜레이 타임을 의미한다.즉, 새로운 요청이 어느 시간 간격으로 들어오는지를 나타낸다. 이 메소드는 3가지의 <code>built in</code> 속성이 있다.</p>
<ul>
<li><strong>constant</strong> : 고정된 시간</li>
<li><strong>between</strong> : min과 max를 지정해 그 시간 사이의 랜덤한 시간</li>
<li><strong>constant_pacing</strong> : 최소 지정된 시간에 한번씩은 실행되도록 지정한 시간</li>
</ul>
<p>이중에서 내가 사용한 것은 between이다. (실제로 user는 규칙적인 시간 텀을 두고 들어오지 않으니)</p>
<br>

<blockquote>
<h4 id="task">@task</h4>
</blockquote>
<p>@task는 task를 지정하는 가장 쉬운 방법인 task decorator를 의미한다.
이를 사용하면 선택적으로 로직의 구현 비중을 조절할 수 있다. (=weight를 지정할 수 있다.)
예를 들어 다음과 같은 코드가 있다고 가정하자.</p>
<pre><code class="language-python"># locusttest.py
from locust import User, task, between

class MyUser(User):
    wait_time = between(5, 15)

    @task(3)
    def task1(self):
        pass

    @task(6)
    def task2(self):
        pass</code></pre>
<p>이런식으로 작성된 locust 코드가 있다면 <code>@task</code>로 묶인 함수에 대해서 task1이 3번 호출될동안 task2는 6번 호출되도록 한다.</p>
<br>

<blockquote>
<h4 id="class--user">class : User</h4>
</blockquote>
<p>class에 사용된 parameter로 <code>User</code>라는 것이 사용되었다. 이는 locust의 클래스인데 이로 인해 내가 작성한 class는 User 클래스를 상속받게 된다. Locust는 테스트되는 동안 각 user에 대해 User class의 instance 하나를 생성한다.</p>
<blockquote>
<h4 id="class--httpuser">class : HttpUser</h4>
</blockquote>
<p>HttpUser class는 User class보다 더 흔하게 사용된다. User와 달리 HttpUser에는 <code>client</code> 속성이 추가되어 있는데 이를 통해서 <strong>HTTP request</strong>로 테스트가 가능하다.</p>
<p>여기서 client는 HttpSession의 instance이다.또한 HttpSession은 <code>requests.Session</code>의 하위 클래스이다. _<span style="color:red">추가적인 공부가 필요한 부분</span>_</p>
<p>client속성은 모든 HTTP methods를 포함한다! GET / POST / PUT 등... <strong>즉, 위에서 말했던 것 처럼 method에 맞는 HTTP request 테스트가 가능하다.</strong></p>
<br>

<h3 id="🔥-error-발생">🔥 Error 발생</h3>
<p>위에서 작성한 코드대로 locust를 실행했더니 다음과 같은 에러가 발생했다.</p>
<pre><code class="language-bash">AttributeError: &#39;NoneType&#39; object has no attribute &#39;get&#39;</code></pre>
<p>위 코드에서 <code>get</code>이 사용된 부분은 client이다. 즉, client에 아무것도 담기지 않은것 같다.</p>
<p>때문에 기존에 User class를 상속받도록 작성한 것 대신 위에서 작성한 것 처럼 client 속성을 갖고 있는 HttpUser 클래스를 상속받게 하였다.</p>
<pre><code class="language-python"># locusttest.py
from locust import HttpUser, task, between

class MyUser(HttpUser):
    wait_time = between(5, 15)

    @task(3)
    def task1(self):
        pass

    @task(6)
    def task2(self):
        pass</code></pre>
<br>

<h2 id="2-실행">2) 실행</h2>
<p>첫 시도는 단순히 locustfile이 작성된 위치에서 (참고로 해당 파일도 프로젝트의 root directory에 작성하였다.) 실행하였다.</p>
<pre><code>locust -f locust_test.py</code></pre><p>결과는 모두 fail이 떴다. <img src="https://images.velog.io/images/c_hyun403/post/47c31a24-43cd-46de-a03d-03a73a498bfb/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.33.png" alt=""></p>
<p>Failures 항목을 보니 어떤 유형의 에러인지 확인 할 수 있었다.
<img src="https://images.velog.io/images/c_hyun403/post/c20c41d5-ee13-450f-ae42-5c49d8e3f9f8/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.44.png" alt=""></p>
<p><code>ConnectionError</code>라고 떴다. 생각해보니 가동중인 server가 없는데 어떤 경로로 test를 할수 있을지 의문이 들었고, 테스트 하고자 하는 프로젝트의 서버를 가동하였다. <code>runserver</code>
이후 다시 test 하였다.</p>
<pre><code class="language-bash">python manage.py runserver
locust -f locust_test.py</code></pre>
<h3 id="1-all">#1 .all()</h3>
<p>테스트 환경은 다음과 같다.</p>
<blockquote>
</blockquote>
<ul>
<li>총 user : 100명</li>
<li>초당 증가하는 user : 5명</li>
<li>host : <a href="http://localhost:8000">http://localhost:8000</a></li>
</ul>
<p>locust test 차트양상은 다음과 같다.<img src="https://images.velog.io/images/c_hyun403/post/2b114be8-5dde-467f-ad43-a1889a7096b2/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.39.48.png" alt=""></p>
<p>test통계와 error상황은 다음과 같다.
<img src="https://images.velog.io/images/c_hyun403/post/5489e319-4742-4ced-9e2b-dfb935782de0/image.png" alt=""></p>
<p>100명의 user가 호출하는 상황에서 통신 실패율이 약 90%... <em>엉망진창</em>
통신 속도는 평균 4468ms를 기록하였다. <strong>내 로직이 부하를 견디지 못한걸로 보여진다.</strong></p>
<br>

<h3 id="2-_relatedall">#2 ~~_related.all()</h3>
<p>프로젝트 당시 DB 성능 개선을 위해 작성했던 대로 다시 test를 진행해 보았다. 당시에 초점을 둔 곳은 DB자체를 호출하는 횟수를 줄이기 위함이었다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/27a169ff-b20b-4102-b113-b9b647333768/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.49.22.png" alt="">
<em>아니 대체..;;</em></p>
<p>test결과통계와 error는 다음과 같다.
<img src="https://images.velog.io/images/c_hyun403/post/d8c055ee-0a12-4601-9592-c397d5c9c5b3/image.png" alt=""></p>
<br>

<h3 id="3-멀티스레드-환경에서-실행해보기-gunicorn">#3 멀티스레드 환경에서 실행해보기 (gunicorn)</h3>
<p>너무나도 높은 실패율로 인해 redis를 적용하기 전 좀더 개선할 수 있는 방법을 생각하다가.
<code>python runserver로 할 시 단일 스레드 환경에서 실행하므로, gunicorn을 사용해 개발환경에 적합한 멀티스레드 환경에서 실행한다.</code> 라는 말이 생각나 이 환경에서 다시 test해보았다.</p>
<p>그러기 위해선 먼저 해당 프로젝트를 gunicorn 환경에 맞게 setting 해주어야 한다.</p>
<h4 id="case1--user-100명">case1 : user 100명</h4>
<p><img src="https://images.velog.io/images/c_hyun403/post/27fd09d5-fe6b-4a34-af3b-48a93bd51d60/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.32.42.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/443db169-c372-4ec8-86ae-c3e4a2d0d750/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.32.49.png" alt=""></p>
<p>🥰 깔끔!</p>
<br>

<h4 id="case2--user-100000명">case2 : user 100000명</h4>
<p>중간에 fail이 심하게 뜨고, 노트북 발열이 너무 심해서 중단! 대략 user 4000명까지 fail 없이 호출되는 것 처럼 보인다. 이후 redis를 사용해 user가 5000명인 상황에서 비교해 보도록 하자!
<img src="https://images.velog.io/images/c_hyun403/post/67250039-8336-4d15-9d64-809b7db79b52/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.27.11.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/db2f1999-272c-49b2-8174-fd4ac08195f8/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.27.38.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/0bf74e7e-7014-4792-a401-241db092dc2f/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.29.02.png" alt=""></p>
<p>노트북 성능이라던가 여러가지 추가로 업그레이드가 필요한 부분인거 같다! <em>욕심이었나!</em></p>
<br>

<h2 id="3-error">3) Error</h2>
<p>EC2로 서버를 가동하고, DB는 내 로컬 DB로 사용하고자 했을때 다음과 같은 에러가 발생했다.</p>
<pre><code class="language-bash">pymysql.err.OperationalError: (2003, &quot;Can&#39;t connect to MySQL server on</code></pre>
<p>이말은 즉, 보안에 의해서 DB가 외부에서부터 접속이 안되기 때문이다. 따라서 VPC 보안그룹까지 모두 설정했던 RDS로 DB를 연결해 주어 테스트를 진행하였다.</p>
<br>

<h2 id="4-추가-사항">4) 추가 사항</h2>
<p>gunicorn을 사용해 locust test를 진행할때 환경은 다음과 같았다.</p>
<ul>
<li>EC2 로 runserver (gunicorn 사용 / Ubuntu 환경)</li>
<li>로컬환경 터미널에서 locust 실행</li>
</ul>
<p>처음에는 locust 실행도 ubuntu 로 하려 했는데 ip주소를 제대로 변경하지 않아서인지 페이지에 접속이안되었다. 그래서 Ip 주소를 수정하려다가 혹시나 하여 로컬환경 터미널에서 locust를 실행했더니 그래도 잘 연결이 되더라!</p>
<p>이후 runserver 하는 프로젝트 위치와, locustfile의 위치가 달라도 제대로 실행이 될까? 하여 확인해 보았다.</p>
<blockquote>
<p>예상 결과 :
어찌되었든 <code>runserver</code>하는 프로젝트로 연결이 될 것이다. </p>
</blockquote>
<p><img src="https://images.velog.io/images/c_hyun403/post/2f9e7562-d19a-4d5d-92a5-d0e45ee0e3cf/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.41.11.png" alt=""></p>
<p>결과 : 정답!</p>
<p>locustfile에 대한 것을 git으로 관리하지 않고자 할때 해당 file들만 따로 폴더에 관리하는 것도 방법일것 같다! (물론 gitignore에 올려도 되긴 하지만)</p>
<br>
<br>

<h1 id="----------------------------1">---------------------------</h1>
<br>
<br>

<h1 id="redis">Redis</h1>
<p>그렇다면 Redis는 무엇인가? 하면 다음과 같다.</p>
<blockquote>
</blockquote>
<ul>
<li><strong>Re</strong>mote <strong>di</strong>ctionary <strong>s</strong>erver의 약자로 <strong>대용량 처리 관련 기술</strong>이다.</li>
</ul>
<blockquote>
<p>Redis에 대한 설명은 이곳에 정말정말 잘 나와있다.! 나도 여기서 참고했다. 
<a href="https://goodgid.github.io/Redis/#redis%EB%9E%80">https://goodgid.github.io/Redis/#redis%EB%9E%80</a></p>
</blockquote>
<p>위의 글을 간단히 요약하자면</p>
<p><strong>Redis는 NoSQL임과 동시에 In-memory솔루션으로 분리되는 Cache솔루션이며, Key-Value 방식의 구현이 가능해 빠른 속도의 데이터 처리가 가능하다. 또한 만료일을 지정하지 않은 이상 영구적인 데이터 보존이 가능하다. (디스크에 데이터를 저장하거나, 다른 서버에 복사본을 남길 수 있기 때문)</strong> 따라서 서버가 종료되고 다시 가동이 되어도 그대로 데이터를 유지할 수 있다.</p>
<p>MySQL과 비교하면 약 <strong>10배 빠른</strong>처리 속도를 자랑한다! 따라서 기존에 MySQL로 작성된 DB성능을 업그레이드하는데 Redis를 사용하는 것이다. <em>(내 모든 프로젝트들은 모두 MySQL로 작성되었다.)</em></p>
<br>

<h2 id="django에서-cache-세팅하기">django에서 cache 세팅하기</h2>
<p>내용은 <a href="https://realpython.com/caching-in-django-with-redis/#getting-started">RealPython</a> 문서를 참고했다.
가상환경에서 <code>pip install redis</code>로 설치해준뒤 Django에서도 redis를 사용할 수 있도록 <code>pip install django-redis</code>도 설치해 준다.</p>
<p>그리고 나서 my_settings.py에 기존에 DB에 대한 정보만 있던것에 Cach에 대한 정보도 기입해준다.</p>
<pre><code class="language-python">#my_settings.py
CACHES = {
    &quot;default&quot;: {
        &quot;BACKEND&quot;: &quot;django_redis.cache.RedisCache&quot;,
        &quot;LOCATION&quot;: &quot;redis://127.0.0.1:6379/1&quot;,
        &quot;OPTIONS&quot;: {
            &quot;CLIENT_CLASS&quot;: &quot;django_redis.client.DefaultClient&quot;
        }
    }
}</code></pre>
<h2 id="loadtest로-호출-속도-확인하기">loadtest로 호출 속도 확인하기</h2>
<blockquote>
<p>🔥 기존의 로직은 불러오는 데이터가 너무 많아 빠르게 테스트 결과를 확인하기에 적합하지 않다고 판단하여 로직을 간단하게 수정하였다.</p>
</blockquote>
<p>수정한 로직은 다음과 같다.
<em>db에 저장된 posting들 중 게시글의 &#39;content&#39;만 가져오도록 한다. 기억으론 한 40여개 정도의 데이터가 있던걸로 기억한다.</em></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/5de477c0-de4d-45a1-a87d-7d026bd12de1/image.png" alt=""></p>
<p>이 상태에서 Loadtest를 통해 100명의 user가 있다는 전제 하에 호출 속도를 확인해 보았다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/ba83bf1a-af16-4727-b39e-ba51028b3ec7/image.png" alt=""></p>
<blockquote>
<p>결과 : 약 8.7초</p>
</blockquote>
<p>이후 cache를 적용해 수정한 뒤 loadtest로 호출 속도를 확인하였다. 이때 django에서 제공해주는 cache를 import 하면 된다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/9f14a890-6eeb-49d4-85ee-59d24d422f38/image.png" alt=""></p>
<p>똑같이 100명의 user가 있다는 조건에서 test해보았다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/b7016d2f-a751-4e94-98cc-dfd2425f2b6e/image.png" alt=""></p>
<blockquote>
<p>결과 : 약 0.5초</p>
</blockquote>
<br>

<h3 id="1-loadtest-결과">1) loadtest 결과</h3>
<p>django-redis를 통해 caching을 적용한 결과 호출 속도가 10배 이상 빨라진걸 확인할 수 있었다.!</p>
<br>

<h3 id="2-locust-결과">2) locust 결과</h3>
<p>locust를 이용해서도 결과를 확인해보고자 하였고 이때는 <strong>원래의 로직 그대</strong> (두개의 외부 class를 각각 inner join과 outer join하는 값 있음)로 하려고 했다. 하지만 제대로 된 test인지도 파악이 안되는 상태에서 다음과 같은 에러가 자꾸 발생하면서 실패율이 90프로가 나왔다... cache를 사용해도ㅠㅠ</p>
<pre><code>django.db.utils.OperationalError: (1040, &#39;Too many connections&#39;)</code></pre><p>이는 MySQL 자체에서 <code>처리 가능한 connection의 수를 넘겼다는 말</code> 이라던데 일단 그래서 User가 100명이 아닌 50명인 경우로두고 test 하였을땐 cache를 사용하지 않아도 모두 성공적인 호출을 불러왔고 rmp차이도 cache를 사용했을때와 사용하지 않았을때 변화가 없었다.</p>
<p>그래서 test용으로 작성한 간단한 로직으로 다시 test해 보았다.
100명일 경우 cache를 사용하지 않아도 fail이 뜨지 않았기에 user를 200명으로 가정하고 test를 진행하였다.</p>
<br>

<h4 id="redis-사용-전">redis 사용 전</h4>
<p><img src="https://images.velog.io/images/c_hyun403/post/ff6cdad3-f1fd-4341-91ab-160d352e85d2/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.56.52.png" alt=""></p>
<p><img src="https://images.velog.io/images/c_hyun403/post/4a970655-4ad9-4fcc-8176-3b8280013e50/image.png" alt=""></p>
<blockquote>
<p>호출 속도 : 1693ms</p>
</blockquote>
<br>

<h4 id="redis-사용-후">redis 사용 후</h4>
<p><img src="https://images.velog.io/images/c_hyun403/post/95ddfd41-2016-41ab-a00a-529ba62ad8ea/%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-05-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.57.00.png" alt="">
<img src="https://images.velog.io/images/c_hyun403/post/f6ace830-5552-4dd1-832d-79e6f8d9b361/image.png" alt=""></p>
<blockquote>
<p>호출 속도 : 5ms</p>
</blockquote>
<p>결과적으로 <strong>호출 실패도 없어졌고, 호출 속도도 향상했을음 확인할 수 있었다!!!</strong></p>
<br>
<br>

<h1 id="----------------------------2">---------------------------</h1>
<br>
<br>

<h1 id="끝-🔥">끝 🔥</h1>
<p>django에서 redis를 사용하기 위해선 django의 cache를 꼭 import 해준다는 개념이 아직은 이해가 잘 안된다. 또한 cache로 메모리에 저장된 데이터를 확인하는 방법을 알고자 하였는데 <code>redis-cli</code>에 접속이 잘 안되서 확인하지 못했다! 데이터는 만료기한이 있거나 임의로 삭제해 주지 않는 한 계속 저장된다고 해서 <code>cache.delete(&#39;posts&#39;)</code> 로직이 실행되도록 해주었는데 제대로 삭제가 되었는지는 모르겠다.</p>
<p>좀더 공부해서 저장된 cache를 확인해 보도록 하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Web] SPA, MPA, CSR, SSR]]></title>
            <link>https://velog.io/@c_hyun403/Web-SPA-MPA-CSR-SSR</link>
            <guid>https://velog.io/@c_hyun403/Web-SPA-MPA-CSR-SSR</guid>
            <pubDate>Fri, 14 May 2021 05:58:13 GMT</pubDate>
            <description><![CDATA[<p>먼저 이 개념을 이해하기 전 &quot;Web&quot;에대한 기본적인 지식이 필요하다.
1, 2, 3세대와 Modern Web까지의 흐름을 간단하게 살펴보자.</p>
<h1 id="web">Web</h1>
<h4 id="1세대--정적인-웹">1세대 : 정적인 웹</h4>
<ul>
<li>HTML과 CSS로만 구성된 웹.</li>
<li>사용자와의 interaction은 불가능 하고 단지 정보 전달의 용도로 사용되었다.</li>
</ul>
<h4 id="2세대--동적인-웹">2세대 : 동적인 웹</h4>
<ul>
<li>Javascript의 사용으로 Server로부터 필요한 데이터터를 주고받을수 있는 동적인 사용이 가능해 졌고, 사용자와의 interaction이 가능해졌다.</li>
<li>🔥 그러나 JS는 &quot;일부&quot;에서만 사용되었고, HTML과 JS에 대한 데이터를 하나의 서버에서 모두 전송해주는 형태였다. (구분 X)</li>
</ul>
<h4 id="3세대--spa-single-page-application">3세대 : SPA (Single Page Application)</h4>
<ul>
<li>SPA의 개념의 등장. 즉, 하나의 Page로 전체 사이트를 구현할 수 있게 되었다.</li>
<li>또한 HTML태그 자체를 JS가 동적으로 생성할수 있게 되었고 HTML/JS부분과 data 부분이 구조적으로 분리되면서 FrontEnd와 BackEnd가 구별되기 시작했다.</li>
<li>이후 FE에서는 좀더 효율적인 Framework 혹은 Library들이 많이 탄생했다.</li>
</ul>
<h4 id="modern-web">Modern Web</h4>
<ul>
<li><strong>규모가 커진 형태</strong> (사용자와의 Interaction 중요성 증가 + 웹 서비스의 복잡성 증가 + 사용자 수, data의 양 증가)
  =&gt; 때문에 시스템 부하(동시접속자, 방대한 데이터의 양)와관련된 문제들이 발생했고 이를 해결하기 위해 <code>System architecture</code>가 발전했다.</li>
<li>FE 서버와 BE 서버의 분리 발생</li>
</ul>
<br>
<br>

<h1 id="-------------------------------">-------------------------------</h1>
<br>
<br>

<h1 id="csr-vs-ssr">CSR vs SSR</h1>
<h2 id="1-csr">1. CSR</h2>
<p>CSR은 <strong>Client Side Render</strong>라는 뜻으로 <strong>Cleint측에서 RENDERING 되는 형식</strong>이다.</p>
<p>여기서 또 하나 필요한 개념으로 CRA가 있다.</p>
<blockquote>
<h2 id="cra-create-react-app">CRA (Create React App)</h2>
<p><em>완전 Front 영역. 나는 아무것도 알지 못한다. 그치만 개념은 이해할수 있지. 나는 지성인이니까</em>
<br>
<em>&quot;아무런 초기 설정이 없어도 CRA를 통해 React 기반의 SPA 사이트를 구현할 수 있다.&quot;</em>
<br>
즉, React를 통해 app을 만듬으로써 초기설정 없이 빠르게 결과물을 만들어낼수 있다는 개념이다.
<br>
<strong>그러나</strong> CRA 방식으로 build한 프로젝트는 <strong>검색에 잘 걸리지 않는다</strong></p>
</blockquote>
<p>그 이유는 CRA를 통해 만들어진 project는 <strong>오직 CSR(Client Side Render)로 실행되기 때문</strong>이다.</p>
<blockquote>
<p>** CSR방식에서 구글봇의 작동법 **
말그래도 Cleint 측에서 rendering이 됨을 듯한다.
웹 페이지에 접속하면 HTML, JS, CSS확장자의 file이 차례로 응답된다.</p>
</blockquote>
<ul>
<li>제일 먼저 응답된 HTML 파일에 의해서 내용이 비어있는 웹 페이지가 형성된다. (안에 data 없음)</li>
<li>🔥 구글로 예를들면 구글봇이 웹 페이지를 분석해 해당 페이지의 data정보를 알아낸다. <strong>BUT</strong> 내용없음! 정보 획득 X</li>
<li>이후 응답된 JS 파일의 다운로드가 완료되고 <strong>JS 파일이 dom을 비어있는 HTML에 그려진다 (RENDERING)</strong></li>
<li>이미 구글봇은 떠난 후..</li>
</ul>
<p>CSR을 사용함으로써 장점도 있다.</p>
<ol>
<li>BackEnd 호출을 최소화 할 수 있다<ul>
<li>최초 호출 시에만 HTML과 JS, CSS를 요청하고 이후엔 요구되는 데이터 만큼만 통신한다. (by JSON)</li>
</ul>
</li>
<li>routing (라우팅, 새로운 페이지로 이동하는 것) 을 하더라도 HTML의 변화는 X! JS가 새로운 페이지를 그려낸다.</li>
</ol>
<br>

<h3 id="자투리-정보--seo-search-engine-optimization">자투리 정보 : SEO (Search Engine Optimization)</h3>
<p>SEO란 웹 페이지 검색엔진이 <strong>자료를 수집하고 순위를 매기는 방식에 맞게 웹 페이지를 구성해서 검색 결과의 상위에 나올수 있도록 하는 작업</strong>을 말한다.</p>
<p>출처 : <a href="https://ko.wikipedia.org/wiki/%EA%B2%80%EC%83%89_%EC%97%94%EC%A7%84_%EC%B5%9C%EC%A0%81%ED%99%94">wikipedia</a></p>
<p>커머스 사이트와 같은 &quot;검색&quot;이 곧 매출과 직결되는 사업분야의 경우 SEO 성능이 매우매우 중요하다!</p>
<br>

<h2 id="2-ssr">2. SSR</h2>
<p>이와 달리 SSR은 <strong>Server Side Render</strong>라는 뜻으로 <strong>Server에서 RENDERING이 되어 응답을 보내는 형식</strong>이다.</p>
<blockquote>
<p>** SSR방식에서 구글봇의 작동법 **
이미 Rendering이 되어 FE에 보내지는것!</p>
</blockquote>
<ul>
<li>응답된 HTML파일에 Rendering된 JS의 내용이 채워진 채 웹 페이지가 형성된다.</li>
<li>🔥 구글봇이 해당 웹 페이지에 존재하는 data의 정보를 수집한다. =&gt; 검색에 걸리는 효과!</li>
</ul>
<p>SSR이 나오게 된 계기는 &quot;SPA의 형식을 지키면서도 검색에 자주 노출되게 하기 위한 방법&quot;을 찾았던 점이다. 즉, <strong>SPA도 챙기면서 SEO까지 챙기기 위한 방법으로 나온것이 SSR 방식이다.</strong></p>
<br>

<h4 id="ssr의-장점">SSR의 장점</h4>
<p>위에서 말했듯 CSR방식에 비해 <strong>SEO 측면에서 유리하다</strong>
    =&gt; 검색엔진에게 보여줄 data를 마련할 수 있다.</p>
<p>또한 <strong>전체적으로 사용자에게 보여주는 콘텐츠 구성이 <code>완료</code> 되는 시점이 더 빠르다.</strong> 비록 CSR방식에 비해 페이지를 <strong>구성하는 속도는 느려지지만 !</strong></p>
<br>

<h4 id="ssr의-주의점">SSR의 주의점</h4>
<p>Server 측에서 rendering되어 data가 전해지기 때문에 <strong>페이지를 잘못 구성할 경우 CSR에 비해 서버 부하가 더 크고, 때문에 첫 로딩이 매우 느려질 수 있다</strong></p>
<br>
<br>

<h1 id="--------------------------------1">-------------------------------</h1>
<br>
<br>

<h1 id="mpa-vs-spa">MPA vs SPA</h1>
<br>

<h2 id="1-mpa">1. MPA</h2>
<p>MPA란 <strong>Multi Page Application</strong>이라는 뜻으로 말그대로 &quot;여러장의 페이지&quot;로 구성된 application을 뜻한다.</p>
<p>현재 우리가 사용하고 있는 &quot;사이트&quot;하나를 생각해보면 해당 사이트가 &quot;한 페이지&quot;로 구성된건 아니라고 생각한다! (당장 구글만 봐도 검색창, 검색 후 창 등.. 페이지가 진짜 많아 보인다)</p>
<p>하지만 이러한 구성이 모두 MPA인것은 아니다. 이후에 나올 SPA에서 다루도록 한다.</p>
<p>다시 MPA로 돌아가서 말하면 MPA는 <strong>정적 웹사이트로 구현된 HTML 파일이 여러개 존재</strong>하고, 페이지간 이동하면서 <strong>화면이 깜빡이는걸 볼 수 있다. (서로 다른 HTML 파일로 이동됨)</strong></p>
<br>

<h2 id="2-spa">2. SPA</h2>
<p>SPA란 <strong>Single Page Application</strong>이라는 뜻으로 &quot;하나의 페이지&quot;로 구성된 application을 뜻한다.
즉, <strong>하나의 HTML 파일로 구성</strong>되어 있다는 뜻.</p>
<p>그렇다면 우리가 보는 &quot;페이지 간의 이동&quot;은 어떻게 SPA로 설명할까??</p>
<p>바로 JS에서 HTML 태그를 생성해 내기 때문에 SPA구조가 될수 있기 때문이다.
즉, <strong>페이지를 이동하게 되면 기존에 있던 HTML 파일에 JS의 특정함수로 생성된 새로운 HTML 태그들로 채워진다는 것</strong>이다.
때문에 페이지간 이동시 <strong>화면이 깜빡이지 않는다.</strong></p>
<br>
<br>

<h1 id="--------------------------------2">-------------------------------</h1>
<br>
<br>

<h1 id="mpa--spa--csr--ssr-">MPA &amp; SPA &amp; CSR &amp; SSR ?</h1>
<p>그렇다면 현재 웹 기준으로 네가지의 관계성은??</p>
<p>현재웹 기준으로 한다면 SSR은 SPA로 구성된 SSR방식을 뜻하며, 이에 따라 SPA형식의 결과물은 CSR과 SSR방식으로 나뉜다!</p>
<br>

<h1 id="csr--ssr">CSR + SSR</h1>
<p>가능하다! 효율이 더욱 높아지는 방법인데 이를 위한 모듈 하나 없을까. 바로 &quot;Next.js&quot;와 같은 훌륭한 framework들이 이미 존재한다.</p>
<p>더 자세한 개념은 지금상태보다 더 깊은 FE지식을 요하기 때문에 일단 &quot;가능하다&quot;라는 것!!</p>
<br>

<blockquote>
<h3 id="자투리-정보--hydration">자투리 정보 : hydration</h3>
<br>
hydration이란 **Server에서 전송한 `정적 문서`를 데이터 변경에 반응할 수있는 `동적 형태`로 변환하는 _클라이언트 측 프로세스_**를 뜻한다.
<br>
이는 render와 동일하지만, dom이 이미 그려져 있는 상태이기 때문에 event listener만 부착하는 식으로 작동한다.
</blockquote>
<br>
<br>

<h1 id="--------------------------------3">-------------------------------</h1>
<br>
<br>

<h1 id="끝">끝!</h1>
<p>BE로 공부하고 있는 지금 React나 dom에 대한 개념은 잘 모르지만 SSR과 CSR에 따른 SEO효율의 차이와 기존의 문제점을 극복하기 위해 현재 사용하는 방식 (SSR + CSR)에 대해서 알수 있었다.! 🥰</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래밍] 객체 지향 프로그래밍, 함수형 프로그래밍]]></title>
            <link>https://velog.io/@c_hyun403/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@c_hyun403/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Tue, 11 May 2021 12:38:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>** - 객체 지향 프로그래밍 : OOP (Object-Oriented Programming)**
** - 절차 지향 프로그래밍 : POP (Procedure-Oriented Programmin)**
** +) 함수형 프로그래밍 **</p>
<br>

<h1 id="객체-지향-프로그래밍-oop">객체 지향 프로그래밍 (OOP)</h1>
<p>객체 지향 프로그래밍이란 개념은 1960년대에 나타났다.
<em>&quot;모든 데이터를 객체(Object)로 취급하고 처리 요청을 받은 객체가 자기 안에 있는 기능을 가지고 처리한다.&quot;</em></p>
<p>이렇게 탄생한 OOP는 캡슐화 / 상속 / 다형성 의 특징을 갖고 있으며 모듈 재사용이 가능해 확장 및 유지보수에 상대정으로 용이하다. (자세한 특징은 아래에 추가)</p>
<br>

<h3 id="클래스-기반-객체-지향-프로그래밍">클래스 기반 객체 지향 프로그래밍</h3>
<p>객체지향 프로그래밍을 따르는 언어는 대표적으로 Java나 Python, C++ 이 있다. 특히나 python에서 이제껏 내가 사용했고 흔히들 사용되는 방법은 <strong>클래스 기반의 객체지향 프로그래밍 기법이다.</strong></p>
<p>객체와 관련된 코드를 <strong>분리</strong>할 수 있게 하는 것이 객체 지향 프로그래밍의 핵심이다. 이때 코드를 더욱 효율적으로 관리하기 위해 클래스 라는 구조가 탄생한 것이다. 즉, 클래스는 <code>객체를 조금 더 효율적으로 생성하기 위해 만들어진 구문</code>이다!</p>
<blockquote>
<p>** class, object, instance **
object와 instance는 지칭하는것의 상태에 따라 구분된다.</p>
</blockquote>
<ul>
<li>Class: 같은 종류의 집단에 속하는 attribute(속성)과 behavior(행위)를 정의한 것.</li>
<li>Object : 클래스의 정의대로 만들어진 실체.</li>
<li>instance : class에 의해서 만들어진 객체(object). 객체가 메모리에 할당된 것. &quot;인스턴스&lt;객체&quot;로 볼 수 있고, 각각의 인스턴스들은 독립적이다.<br>
></li>
<li><em>객체가 메모리에 할당되어 실제 사용될 때 인스턴스라고 한다.*</em> 즉 인스턴스는 객체에 포함된다고 볼 수 있다.</li>
</ul>
<br>
<br>

<h2 id="1-oop의-특징">1. OOP의 특징</h2>
<p>위에서 말했듯 객체지향 프로그래밍에는 캡슐화 / 상속 / 다형성 이라는 특징이 있는데 이 외에도 몇가지 특징이 더 있다.</p>
<blockquote>
<p>참고 자료 : <a href="https://ko.wikipedia.org/wiki/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D#%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%EC%96%B8%EC%96%B4"> wikipedia </a></p>
</blockquote>
<h4 id="1-자료-추상화">1) 자료 추상화</h4>
<p>자료추상화는 <em>&quot;불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단히 만드는 것&quot;_이다. 이런 방법을 통해 정의된 자료형을 <strong>추상 자료형</strong>이라고 하는데 _&quot;추상 자료형은 자료형의 자료 표현과 자료형의 연산을 <strong>캡슐화</strong>한 것으로 접근 제어를 통해서 자료형의 정보를 은닉할 수 있다&quot;</em></p>
<p>객체 지향 프로그래밍에서는 일반적으로 다음과 같이 정의한다.</p>
<ul>
<li>추상 자료형 : <strong>클래스</strong></li>
<li>추상 자료형의 instance : <strong>객체</strong></li>
<li>추상 자료형에서 정의된 연산 : <strong>메소드</strong>(함수)</li>
<li>메소드의 호출 : <strong>생성자</strong></li>
</ul>
<br>

<h4 id="2-상속다중상속">2) 상속&amp;다중상속</h4>
<p><strong>상속</strong>은 <em>&quot;새로운 class가 기존 class의 자료와 연산을 이용할수 있게 하는 것&quot;</em> 으로
<strong>다중상속</strong>은 <em>&quot;class가 2개 이상의 class로부터 상속받게 하는 것&quot;</em> 을 의미한다.</p>
<blockquote>
<p>** 다중상속 지원 언어 ? **
python 의 경우 다중상속이 가능하지만 JAVA는 지원하지 않는다.</p>
</blockquote>
<br>

<h4 id="3-다형성">3) 다형성</h4>
<p><strong>다형성</strong> 이란 <em>&quot;어떤 한 요소에 여러 개념을 넣어 놓는 것으로 오버라이딩 혹은 오버로딩을 의미한다.&quot;</em></p>
<blockquote>
<p>** 오버라이딩 / 오버로딩 ? **</p>
</blockquote>
<p><strong>오버라이딩</strong>이란  상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것.
<strong>오버로딩</strong>이란 서로 다른 시그니처를 갖는 여러 메소드를 하나의 이름으로 정의하는 것.</p>
<blockquote>
</blockquote>
<p><strong>시그니쳐 ?</strong>
메소드의 정의에서 <strong>메소드 이름과 매개변수 리스트의 조합</strong>을 의미한다.</p>
<p>다형 개념을 통해서 프로그램 안의 객체 간의 관계를 <strong>조직적</strong>으로 나타낼 수 있다.</p>
<br>

<h4 id="4-동적-바인딩">4) 동적 바인딩</h4>
<p><strong>동적 바인딩</strong>이란 <strong>실행 시간 중에 일어나거나 실행 과정에서 변경될수 있는 바인딩</strong>으로 프로그램의 한 개체나 기호를 실행 과정에 여러 속성이나 연산에 바인딩함으로써 다형 개념을 실현하는 것이다.</p>
<p>컴파일 시간에 완료되어 변화하지 않는 정적 바인딩과 대비된다.</p>
<blockquote>
<p>** 바인딩 ? **
바인딩이란 <strong>프로그램의 어떤 기본 단위가 가질 수 있는 구성요소의 구체적인 값, 성격을 확정하는 것</strong>을 의미한다.</p>
</blockquote>
<p>동적바인딩을 나타내는 대표적인 예시가 인터프리터 언어인 <strong>Python</strong>이다. </p>
<pre><code class="language-python"># python 예시
a = 123</code></pre>
<p>python의 경우 변수의 자료형을 미리 선언하지 않고 <strong>실행시간에 변수의 자료형을 결정한다.</strong>
그 외에 a는 <code>변수명</code>, 123은 <code>변수의 자료값</code> 으로 할당, 즉 &quot;바인딩&quot; 된다. </p>
<p>함수의 동적 바인딩은 실행파일을 만들 때 호출할 함수의 메모리 주소가 확정되지 않고 보류 상태로 있다가 <strong>이후 실제 실행 시간에 호출할 함수의 주소가 결정</strong> 된다. 때문에 <strong>주소를 저장할 공간을 미리 확보</strong>해야 한다.</p>
<p>보통의 가상 함수들이 &quot;동적 바인딩&quot;을 구현하는데 <strong>실행 될지 안될지 확정되지 않은 함수를 위해 저장공간을 미리 마련해야한다는 점</strong> 때문에 메모리 관리에 있어선 <strong>비효율적</strong>일 수 있다.</p>
<blockquote>
<p><strong>가상 함수 ?</strong>
객체 지향 프로그래밍에서 가상 함수는 <strong>상속하는 클래스 내에서 같은 시그니처의 함수로 오버라이딩 될 수 있는 함수 또는 메소드</strong>이다. OOP의 다형성에서 중요한 부분이다</p>
</blockquote>
<p>출처 : <a href="https://ko.wikipedia.org/wiki/%EA%B0%80%EC%83%81_%ED%95%A8%EC%88%98">wikipedia</a></p>
<br>

<h2 id="2-oop를-쓰는-이유">2. OOP를 쓰는 이유?</h2>
<p>wikipedia의 말이다. 읽으면서 아! 그렇구나! 싶었다.</p>
<p>소프트웨어 공학의 관점에서 바라볼때 SW의 질을 향상시키기 위해선 <strong>강한 응집력</strong>과 <strong>약간 결합력</strong>을 지향해야 한다. OOP의 경우 class를 이용해 모아둔 data를 사용함으로써 <strong>응집력을 강화하고, class간 독자적인 존재로 결합력을 약하게 한다.</strong>
때문에 OOP를 지향하는 것이라고 생각한다!</p>
<br>
<br>

<h1 id="------------------------">------------------------</h1>
<br>
<br>

<h1 id="함수형-프로그래밍">함수형 프로그래밍</h1>
<p>함수형 프로그래밍이란 <strong>자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나</strong>이다. 즉, <strong>프로그램은 함수의 계산</strong>이라고 프로그래밍을 정의하며 명령형 프로그래밍에서는 <strong>상태를 바꾸는 것을 강조</strong>하는 것과 달리 함수형 프로그래밍에서는 <strong>함수의 응용을 강조</strong>한다.</p>
<p>이와 관련하여 언어의 종류도 함수형인지 명령형인지 나눌 수 있다.</p>
<blockquote>
<p>** 명령형 언어 ? **
명령형 언어는 식의 값이 부분식의 계산 순서에 의존적이게 되어 이로 인해서 부작용이 발생할 수 있다. 또한 <strong>함수의 호출 순서에 종속적인 특징을 갖는다.</strong></p>
</blockquote>
<p>** 함수형 언어 ? **
함수형 언어는 독립적인 변수를 사용한다. 따라서 <strong>입력값이 같으면 항상 같은 결과값을 반환</strong>한다.이러한 점을 들어 함수형 언어는 <strong>참조투명성이 있다</strong>고 할수 있다. 참조투명성의 보장으로 인해서 <strong>함수형 언어는 간결하고 이해하기 쉬운 특징을 가지며 프로그램의 형식적인 분석과 조작이 용이하다.</strong></p>
<p><strong>함수형 언어만 함수형 프로그래밍이 가능한 것은 아니다.</strong> 명령형 언어에는 대표적으로 python과 C++이 있다. 그치만 나도 사용했고, 많은 이들도 python으로 함수형 프로그래밍을 구현하고 있다.</p>
<p>이처럼 <strong>함수형 언어로 별도로 설계되지 않은 언어도 함수형 프로그래밍이 가능하다</strong></p>
<br>

<h2 id="1-함수형-프로그래밍의-장점">1. 함수형 프로그래밍의 장점</h2>
<p>명확한 장점이 두가지 있다.</p>
<blockquote>
</blockquote>
<h4 id="1-코드의-용도를-구분할-수-있다">1) 코드의 용도를 구분할 수 있다.</h4>
<h4 id="2-코드를-재사용-할-수-있다">2) 코드를 재사용 할 수 있다.</h4>
<h1 id="함수형-프로그래밍에-관한-내용-추가중">함수형 프로그래밍에 관한 내용 추가중</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] 세팅부터 배포까지]]></title>
            <link>https://velog.io/@c_hyun403/Docker-%EC%84%B8%ED%8C%85%EB%B6%80%ED%84%B0-%EB%B0%B0%ED%8F%AC%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@c_hyun403/Docker-%EC%84%B8%ED%8C%85%EB%B6%80%ED%84%B0-%EB%B0%B0%ED%8F%AC%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Tue, 11 May 2021 10:03:43 GMT</pubDate>
            <description><![CDATA[<p>EC2와 RDS, nohup으로 gunicorn을 사용해 background에서 서버를 가동시켜 배포하는것까지 해보았다. 이번에는 가상환경 세팅까지 필요없도록 Docker를 이용해 배포해보도록 하자!</p>
<p>프로젝트 명 : jellogram</p>
<br>

<h1 id="docker">Docker</h1>
<p>Docker에 대한 전반적인 이해를 쉽게 해주었던 글이 있다!! 모두 읽어보시는걸 추천드립니다.
<a href="https://khj93.tistory.com/entry/Docker-Docker-%EA%B0%9C%EB%85%90">https://khj93.tistory.com/entry/Docker-Docker-%EA%B0%9C%EB%85%90</a></p>
<p>아래 개념적인 내용은 해당 게시글을 읽고 정리한 글이다.</p>
<h2 id="1-docker-란">1. Docker 란?</h2>
<p>Docker는 <strong>가상화 기술</strong>이다. 이 기술은 Docker라는 회사가 개발한 container virtualization으로 해당 기술품은 기존에도 많이 존재했지만 Docker가 많이 사용되면서 사람들이 container virtualization 기술을 아예 Docker라고 부르게 되었고 <code>Docker = container virtualization</code> 로 고착된 것이다. <em>원래는 그런 의미가 아님! 상처에 붙이는 밴드를 흔히들 &quot;데일밴드&quot;라고 부르는 거랑 같은 격이다</em></p>
<p>Docker를 이용하면 container 가상화 실행 환경 위에 application 배포 엔진을 추가하여 언제 어디서든 코드가 실행될수 있도록 제공한다. 이점을 이용해 요즘엔 MSA나  CI/CD 와 함께 사용되는데 때문에 하나의 container당 하나의 application이나 프로세스를 실행하는 것이 권장된다.</p>
<br>

<h4 id="vm-hypervisor-vs-container-가상화-docker">VM (Hypervisor) VS Container 가상화 (Docker)</h4>
<p><img src="https://images.velog.io/images/c_hyun403/post/8171f7ba-d9a1-4ad1-9bfa-9e640167774c/%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-05-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.42.39.png" alt=""></p>
<p>_사진 출처 : <a href="https://khj93.tistory.com/entry/Docker-Docker-%EA%B0%9C%EB%85%90">https://khj93.tistory.com/entry/Docker-Docker-%EA%B0%9C%EB%85%90</a> _</p>
<p>좌측이 VM 모식도 이고 우측이 container 가상화 모식도이다.</p>
<blockquote>
<p>** VM ? **
하나의 물리적 시스템에서 각각 자체 운영 체제(OS)와 application을 운영할수 있다. 단, VM은 서로에 대해서 알지 못하고, host의 OS도 알지 못한다. (완전 독립적!)
때문에 실제 하드웨어간의 조정을 위해 &quot;hypervisor&quot;라고 불리는 경량 소프트웨어 계층이 필요하다. hypersivor 계층이 VM을 각각 분리하여 서로간에 간섭을 막는다.
<br>
<strong>&lt;대표 장점&gt;</strong></p>
</blockquote>
<ul>
<li><code>물리적 서버의 리소스를 더 효율적으로 운용할 수 있다.</code> : 한 서버에 한 OS가동시 낭비되는 리소스(CPU, 메모리, 스토리지 등..) 가 존재하지만 한 서버에 여러개의 OS를 가동하면 idle상태로 낭비시키지 않고 필요한 OS에 리소스(예를 들면 CPU)가 할당되어 효율적인 운용이 가능하다. / 중저가의 하드웨어 여러개를 운용하는 것보다 고가의 하드웨어로 한 서버에 여러개의 OS를 운용하는것이 더욱 효율적이다.</li>
</ul>
<p>정리하자면 Host OS엔진 위해 가상화를 시키기 위한 Hypervisor 엔진, 그리고 그 위에 Guest OS를 가동시키는 것과 달리 컨테이너 기반 가상화는 <strong>Docker 엔진 위에 Application 실행에 필요한 바이너리</strong>만 올라가게 된다. 때문에 OS위의 OS동작으로 인해 속도가 느린 VM에 비해 성능의 효율이 더 뛰어나다는 장점이 있다.</p>
<blockquote>
<p>container를 사용한다는 것은 VM을 생성하는 것이 아니라 <strong>Host OS가 사용하는 리소스를 분리하여 여러 환경을 만들 수 있도록 하는 것</strong>이다.</p>
</blockquote>
<br>

<h2 id="2-docker의-구조">2. Docker의 구조</h2>
<p>4가지 부분으로 구성되어 있다.</p>
<blockquote>
</blockquote>
<ul>
<li>client와 server(=docker engine)</li>
<li>image</li>
<li>registries</li>
<li>containers</li>
</ul>
<h4 id="1-cilent와-server">1) cilent와 server</h4>
<p>여기서 server는 docker의 engine이라고도 불린다.
둘은 같은 Host이거나, 혹은 다른 Host일때도 함께 운용될 수 있다.</p>
<h4 id="2-image">2) image</h4>
<p>Dockr의 이미지를 그림으로 표현하면 다음과 같다.
<img src="https://images.velog.io/images/c_hyun403/post/358583e8-624d-47e5-9259-240ba7efe467/%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-05-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.56.34.png" alt=""></p>
<p>해당 이미지에 저장된 값을 토대로 환경이 셋팅되는것인데 해당 image에는 container를 실행할 수 있는 실행파일, 설정값 등을 갖고 있다.</p>
<p>이 이미지를 활용해 배포한다면 프로젝트에 필요한 환경까지 모두 공유가 가능해지는 것이다. 단순히 서버와 DB를 공유하는것에서 끝나는것이 아닌! 세팅까지!</p>
<h4 id="3-registries">3) registries</h4>
<p>docker의 image를 저장하는 repository이다. <code>(=Githup의 repo)</code></p>
<h4 id="4-contaiers">4) contaiers</h4>
<p>docker의 Image를 실행시키는 가상화 공간이다. 원래 docker container는 두개 이상의 프로세스를 실행 할 수 있지만 권장사항에서는 하나의 container당 하나의 process만 실행하는것을 권장한다.</p>
<br>
<br>

<h1 id="------------------------">------------------------</h1>
<br>
<br>

<h1 id="docker-사용하기-연습-ver">Docker 사용하기 연습 ver.</h1>
<br>

<h2 id="step-1-docker-설치">Step 1. Docker 설치</h2>
<p>나의 경우 MacOS이기 때문에 Docker 사이트에서 프로그램을 다운로드 하였다. <a href="https://docker.com">https://docker.com</a></p>
<br>

<h4 id="ec2-에서-docker-설치하기">EC2 에서 Docker 설치하기</h4>
<p>EC2는 Ubuntu 운영체제이다! 따라서 프로그램 설치가 아니라 terminal에서 명령어를 입력해 설치를 진행해 주어야 한다. <strong>이후 진행할 예정</strong></p>
<h3 id="step-1-2-docker-목록-확인">Step 1-2. Docker 목록 확인</h3>
<p>먼저 지난번에 실습하면서 Docker image를 만든적이 있다 해당 Docker는 이용중지상태인데 가동시키지 않은 상태에서 <strong>현재 실행중인 컨테이너와 실행이 종료된 상태의 컨테이너 목록을 살펴보자</strong></p>
<pre><code class="language-bash"># 실행중인 container 목록
docker ps

# 실행종료된것 포함 모든 container 목록
docker ps -a

# 생성&amp;다운로드 된 image 목록
docker images

# 모든 image 목록
docker images -a</code></pre>
<p>명령문을 살펴보면 <code>-a</code> 라는 의미가 all 같다!</p>
<p>내 터미널에 명령어를 입력하면 다음과 같다.
<img src="https://images.velog.io/images/c_hyun403/post/ec623d13-88b5-48fb-8c16-dabd446967d5/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.18.54.png" alt=""></p>
<p><em>그때 docker_train 이라는 이름으로 생성했었다</em></p>
<br>

<h2 id="step-2-docker-file">Step 2. Docker file</h2>
<p>Docker file에 대한 개념을 잡아야 한다.
이는 image 생성의 <strong>출발점</strong>으로 image를 구성하기 위한 명령어들을 작성하여 image를 구성할 수 있다. 즉, <strong>Docker File을 읽을 수 있다면 해당 Image가 어떻게 구성되어 있는지 알 수 있다.</strong></p>
<p>따라서 Docker File을 작성해서 image를 생성하도록 한다</p>
<pre><code class="language-bash">#./Dockerfile
FROM python:3
#기반이 될 이미지

# 작업디렉토리(default)설정
WORKDIR /usr/src/app

## Install packages
#현재 패키지 설치 정보를 도커 이미지에 복사
COPY requirements.txt ./
#설치정보를 읽어 들여서 패키지를 설치
RUN pip install -r requirements.txt

## Copy all src files
#현재경로에 존재하는 모든 소스파일을 이미지에 복사
COPY . .


## Run the application on the port 8080
#8000번 포트를 외부에 개방하도록 설정
EXPOSE 8000


#CMD [&quot;python&quot;, &quot;./setup.py&quot;, &quot;runserver&quot;, &quot;--host=0.0.0.0&quot;, &quot;-p 8080&quot;]
#gunicorn을 사용해서 서버를 실행
CMD [&quot;gunicorn&quot;, &quot;--bind&quot;, &quot;0.0.0.0:8000&quot;, &quot;docker_train.wsgi:application&quot;]</code></pre>
<h2 id="step-2-2-dockerignore">Step 2-2. dockerignore</h2>
<p>gitignore와 같이 dockeringore도 작성해서 이미지 작성시 이미지에 들어가지 않을 내용을 선별할 수 있다.</p>
<pre><code class="language-bash"># vi dockerignore

npn-debug.log
Dockerfile*
docker-compose*
.dockerignore
.git
.gitignore
.vscode

# 그 외 추가할 사항등 입력 후 :wq</code></pre>
<br>

<h2 id="step-3-이미지-빌드하기">Step 3. 이미지 빌드하기</h2>
<p>위에서 Docker File을 작성하였다면 해당 file이 있는 위치에서 다음의 명령어를 입력한다</p>
<pre><code class="language-bash">docker build -t [만들려는 이미지 이름]

# 권장 방식
docker build -t [도커허브에 가입한 계정명/이미지명(프로젝트명을 권장한다)] : [버전(0.0.0 이런형태 권장)]
ex) docker build -t chyun403/docker_train:0.1.0</code></pre>
<h2 id="step-4-빌드된-이미지-실행시키기">Step 4. 빌드된 이미지 실행시키기</h2>
<pre><code class="language-bash">docker run --name &#39;컨테이너 명&#39; -d&#39;데몬으로 실행하기 위한 옵션&#39; -p &#39;호스트 포트&#39;:&#39;컨테이너 포트&#39; &#39;이미지명&#39;
ex) docker run --name docker_train -d -p 8000:8000 chyun403/docker_train:0.1.0</code></pre>
<br>

<h2 id="docker-flow">Docker Flow</h2>
<p>명령어를 통해 Docker의 pull부터 실행까지 방법에 대한 것이다. 참고한 사이트에 더 많은 명령어들이 있다!
출처 : <a href="https://khj93.tistory.com/entry/Docker-Docker-%EC%84%A4%EC%B9%98%EB%B6%80%ED%84%B0-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%8B%A4%ED%96%89%EA%B9%8C%EC%A7%80-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95?category=797143">khj93.tistory</a></p>
<h4 id="1docker-image-pull">1)Docker image pull</h4>
<pre><code class="language-bash">docker pull [이미지 이름]</code></pre>
<h4 id="2docker-image-list-확인">2)Docker image list 확인</h4>
<pre><code class="language-bash">docker images</code></pre>
<p>참고로 이미 빌드된 이미지로 컨테이너에서 실행이 되고있다면 해당 컨테이너를 먼저 삭제한 후 이미지 삭제가 가능하다.</p>
<h4 id="3-docker-image-삭제하기">3) Docker image 삭제하기</h4>
<pre><code class="language-bash">docker rmi [이미지 이름]</code></pre>
<h4 id="4-docker-image-push">4) Docker image push</h4>
<pre><code class="language-bash">docker push [레지스트리url/이미지:버전]</code></pre>
<h4 id="5-docker-image로-컨테이너-실행시키기">5) Docker image로 컨테이너 실행시키기</h4>
<pre><code class="language-bash">docker run &lt;옵션&gt; &lt;이미지 이름, ID&gt; &lt;명령&gt; &lt;매개변수&gt;

docker run --name [컨테이너이름] -d [데몬으로 실행하기 위한 옵션] -p [호스트 포트]:[컨테이너 포트] [이미지이름]

# 예시 
docker run --name [컨테이너이름] -v [호스트경로]:[컨테이너경로] -p [호스트포트]:[컨테이너포트] -d 이미지:버전</code></pre>
<br>
<br>

<h1 id="-------------------------1">------------------------</h1>
<br>
<br>

<h1 id="docker-실행하기-실습-ver">Docker 실행하기 실습 ver.</h1>
<p>진행할 프로젝트 : jellogram
server : nohup으로 gunicorn을 이용해서 가동 (Ubuntu)</p>
<h2 id="1-dockerfile-작성">1) Dockerfile 작성</h2>
<pre><code class="language-bash">#./Dockerfile
FROM python:3
#기반이 될 이미지

# 작업디렉토리(default)설정
WORKDIR /usr/src/app

## Install packages
#현재 패키지 설치 정보를 도커 이미지에 복사
COPY requirements.txt ./
#설치정보를 읽어 들여서 패키지를 설치
RUN pip install -r requirements.txt

## Copy all src files
#현재경로에 존재하는 모든 소스파일을 이미지에 복사
COPY . .


## Run the application on the port 8080
#8000번 포트를 외부에 개방하도록 설정
EXPOSE 8000

#CMD [&quot;python&quot;, &quot;./setup.py&quot;, &quot;runserver&quot;, &quot;--host=0.0.0.0&quot;, &quot;-p 8080&quot;]
#gunicorn을 사용해서 서버를 실행
CMD [&quot;gunicorn&quot;, &quot;--bind&quot;, &quot;0.0.0.0:8000&quot;, &quot;jellogram.wsgi:application&quot;]</code></pre>
<br>

<h2 id="2-dockerfile을-기반으로-이미지-빌드하기">2) Dockerfile을 기반으로 이미지 빌드하기</h2>
<pre><code class="language-bash">docker build -t chyun403/jellogram_g:0.1.0 .</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/1382ae6e-5813-4f2d-a2c7-6c9f2dc2fcf0/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%202.53.59.png" alt=""></p>
<p>Dockerfile에 입력한 대로 [n/5]만큼 설치되는걸 확인할 수 있다.</p>
<br>

<h3 id="2-1-이미지-목록-확인">2-1) 이미지 목록 확인</h3>
<pre><code class="language-bash">❯ docker images -a
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
chyun403/jellogram_g    0.1.0     2ed9fab3474a   9 minutes ago   1GB
chyun403/docker_train   0.1       0a40a7b73545   2 months ago    932MB</code></pre>
<br>

<h2 id="3-빌드된-이미지-실행하기--container">3) 빌드된 이미지 실행하기 : Container</h2>
<pre><code class="language-bash">docker run -d -p 8000:8000 --name kch chyun403/jellogram_g:0.1.0</code></pre>
<p>** -d **</p>
<blockquote>
<p>gunicorn으로 서버를 가동시켰을때 background에서도 가동하기 위해 <strong>nohup</strong>을 사용했었다! 여기서 입력한 명령어 중 <code>-d</code>는 데몬화 한다는 의미로 백그라운드에서 서버를 가동하도록 한다는 의미이다.
즉, docker안에 있는 gunicorn 설정이 백그라운드에서도 가동되도록 하겠다는 의미이다. (docker 프로세스 자체가 백그라운드에서 실행되는 의미가 아님!)</p>
</blockquote>
<p>** -p **</p>
<blockquote>
<p>port forwarding이라는 의미이다.
Dockerfile에서 <code>EXPOSE 8000</code>으로 입력해주었는데 이는 내가 띄울 Docker 서버에 8000 포트를 연다는 의미이다.
<code>-p @@@@:####</code> 에서 앞 <code>@</code>는 내 <strong>컴퓨터에서 호출하는 포트번호</strong> 이고 뒤 <code>#</code>은 <strong>가상환경이 받아들이는 포트번호</strong>이다.
따라서 Dockerfile에 설정한 대로 8000은 뒤 <code>#</code>에 입력되어야 하고, 만약 내 컴퓨터에서 이미 포트번호 8000번을 사용한다면 다른 번호를 지정해서 앞 <code>@</code>에 입력해 주면 된다.</p>
</blockquote>
<p>만약 <code>-p 9000:8000</code> 이라면 내 컴퓨터에서 포트번호 9000으로 호출하면 가상환경의 포트번호 8000으로 들어가게 된다는 의미이다.</p>
<p>** --name [컨테이너이름] **</p>
<blockquote>
</blockquote>
<p>원래 docker에서 <strong>랜덤하게 의미없는 이름으로 &#39;_&#39;로 구분하면서 자동으로 지정</strong>해준다. (왜냐면 나중되면 docker OS 를 진짜 많이 만들어야 하는데 그때마다 naming하기가 버겁기 때문)</p>
<p>&lt; random 하게 이름 생성 됐을 경우 &gt;</p>
<pre><code class="language-bash">docker run -d -p 8000:8000 chyun403/jellogram_g:0.1.0</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/aab75593-1703-44ad-a710-321298e42d67/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.07.58.png" alt=""></p>
<p>&lt;내가 이름을 지정해주었을 경우&gt;</p>
<pre><code class="language-bash">docker run -d -p 8000:8000 --name jello0512 chyun403/jellogram_g:0.1.0</code></pre>
<p><img src="https://images.velog.io/images/c_hyun403/post/8a26760c-2ac6-458a-8247-9bebc9b18915/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.09.14.png" alt=""></p>
<p>참고로 <code>--name [컨테이너 명]</code>의 위치는 무관하다 따라서 다음의 명령어도 똑같이 기능한다.</p>
<pre><code class="language-bash">docker run --name jello0512 -d -p 8000:8000 chyun403/jellogram_g:0.1.0</code></pre>
<h3 id="3-2-실행되고-있는-container-목록-확인">3-2) 실행되고 있는 container 목록 확인</h3>
<pre><code class="language-bash">❯ docker ps -a
CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS                    PORTS                    NAMES
08fdf94dae7c   chyun403/jellogram_g:0.1.0   &quot;gunicorn --bind 0.0…&quot;   2 minutes ago   Created                   0.0.0.0:8000-&gt;8000/tcp   jello0512
3958fbd0aff0   chyun403/jellogram_g:0.1.0   &quot;gunicorn --bind 0.0…&quot;   4 minutes ago   Created                   0.0.0.0:8000-&gt;8000/tcp   angry_heyrovsky
d65e82315f3f   chyun403/docker_train:0.1    &quot;gunicorn --bind 0.0…&quot;   2 months ago    Exited (0) 2 months ago                            docker_train</code></pre>
<h2 id="4-통신으로-확인하기">4) 통신으로 확인하기</h2>
<p>Docker를 통해 background에서 gunicorn으로 서버가 가동되는것 까지 세팅되도록 했다! 바로 통신으로 확인해 본다.</p>
<pre><code class="language-bash"># 실행 명령어
❯ docker run -d -p 8000:8000 --name jellogram chyun403/jellogram:0.1.0</code></pre>
<p>컨테이너 생성 확인<img src="https://images.velog.io/images/c_hyun403/post/d0fb6c3c-67dd-4835-9138-d2d1f5f2cb12/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.37.34.png" alt=""></p>
<p>postman으로 확인<img src="https://images.velog.io/images/c_hyun403/post/4a4c2d82-4b42-4eb6-86b3-88eff70690a8/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.38.05.png" alt=""></p>
<br>

<h2 id="🐳-docker-실행--통신확인--docker-종료">🐳 docker 실행 ~ 통신확인 ~ docker 종료</h2>
<p><img src="https://images.velog.io/images/c_hyun403/post/741b15fa-543c-421a-a487-6c8d00d8d6e1/%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-05-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.31.43.png" alt=""></p>
<h1 id="🔥-에러-발생">🔥 에러 발생</h1>
<p>이미지를 빌드하고 빌드한 이미지를 실행시키고자 하면 다음과 같은 에러가 발생했다.</p>
<pre><code>docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: &quot;gunicorn&quot;: executable file not found in $PATH: unknown.</code></pre><p>말그대로 <strong>gunicorn</strong>이 없다고 하는것 같은데 구글링도 하면서 혹시몰라 <code>requirements.txt</code>를 확인해보았다.</p>
<p>gunicorn이 없었다. ....</p>
<p>처음엔 container만 삭제하고 gunicorn을 requirements.txt에 추가한 뒤다시 실행하면 될거라 생각했지만 같은 에러가 발생했다.</p>
<p>다시 생각해보니 <strong>이미지를 만든 그 시점에 대한 환경으로 계속해서 같은 실행이 반복되었던 것</strong> 때문에 환경이 변한다면 <strong>변화할때마다 이미지를 새로 생성해 주어야 한다</strong></p>
<p>기존의 컨테이너와 이미지를 모두 삭제한 후</p>
<ul>
<li>requirements.txt에 gunicorn 추가</li>
<li>새로 이미지 빌드</li>
<li>빌드된 이미지 실행</li>
</ul>
<p>을 진행하였고 postman으로 통신을 확인할 수 있었다.</p>
<p>통신까지 확인한 후에는 컨테이너 실행을 종료 시켜주었다 (삭제 아님!)
<img src="https://images.velog.io/images/c_hyun403/post/ebd8408b-d52f-4442-a92d-c5e909fda30c/%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-05-12%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%203.51.11.png" alt="">
<br>
<br></p>
<h1 id="-------------------------2">------------------------</h1>
<br>
<br>

<h1 id="ec2에서-docker-실행하기">EC2에서 Docker 실행하기</h1>
<p>EC2는 우분투! 우분투 명령어를 사용해 내 환경에 docker를 설치해준다.
<em>가상환경 켜.. 제발..</em></p>
<pre><code class="language-bash">sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable&quot;
sudo apt update
apt-cache policy docker-ce

sudo apt install docker-ce</code></pre>
<p>참고로.. 마지막 명령어 직전까지 또 base 에서 해버렸다. 인간은 똑같은 실수를 반복한다지...</p>
<p>이후 혹시나 싶어서 MacOS에서 만들었던 container를 start해보려니 그런 container는 없다고 한다.</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/c33f45d0-55b0-4267-9379-a67628ed1750/image.png" alt=""></p>
<p>따라서 imgae build부터 다시 실행해 준다.
<img src="https://images.velog.io/images/c_hyun403/post/6ae0ec5a-e140-44e9-ade9-55fb45ced90d/image.png" alt=""></p>
<p>이후 <code>docker run</code>으로 실행시켜 새로운 container를 생성후 통신으로 확인한다!</p>
<p><img src="https://images.velog.io/images/c_hyun403/post/6844dac3-d3b4-426e-813c-d202496b7d8c/image.png" alt=""></p>
<p>🔥 참고로 <code>http://localhost:8000</code> 으로 통신해도 값은 나오더라!</p>
<br>
<br>

<h1 id="-------------------------3">------------------------</h1>
<br>
<br>

<h1 id="끝">끝!</h1>
<p>확실히 혼자서 진행해보니 Docker를 어떻게 사용하는지, 왜 사용하는지, 사용했을때의 효과로 무엇이 있는지 좀더 느끼기 쉬웠다!</p>
<p>가상환경을 셋팅해줄 필요가 없다는것. pip install -r이 필요가 없으니!
또한 그때그때 OS에 맞춰 번역까지 해주니 OS에 제한되는것 없이 업데이트가 가능하다는 점. 등 Docker에 대한 기본적인 개념을 알 수 있었다.</p>
<p>이후 프로젝트를 진행하면서 새로운 모듈을 더 설치하게 될텐데 주기적으로 docker로 배포를 진행하면서 관리해보도록 하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] S3 이미지 여러개 업로드하기]]></title>
            <link>https://velog.io/@c_hyun403/AWS-S3-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@c_hyun403/AWS-S3-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 10 May 2021 16:58:25 GMT</pubDate>
            <description><![CDATA[<p>지난번에는 S3를 이용해 이미지 한개를 업로드 하는걸 성공했다.
소셜활동을 하면서 이미지를 두개 이상올리는 기능을 참 많이 봤는데 나도 구현해보자!!</p>
<p>먼저 처음 시도한 것은 postman으로 계속 test하면서 form-data의 key에 또다른 &#39;filename&#39;이라는 키값을 지정한 것이었다.
<img src="https://images.velog.io/images/c_hyun403/post/d0cfe473-fa61-4acc-8e9a-a960dc7ce194/%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-05-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.46.25.png" alt=""></p>
<p>결과는 실패.
두개의 사진 중 두번째로 들어간 <code>7clock.jpeg</code>만 업로드 되었다.</p>
<br>

<h1 id="list로-받기">list로 받기</h1>
<p>위 문제를 해결하기 위해 가장 먼저 떠오른 것은 해당 값을 list로 받는것. list속 객체 형태로 받아야 할것 같다.
먼저 처음 들어온 이미지의 key이름이 filename이었는데 이를 filename1로 변경하고 이후 하나씩 늘어날때마다 숫자도 늘려주도록 해보자.</p>
<p>=&gt; 이렇게 하는게 프론트에서도 기능이 될지 모르겠다! 이미지를 업로드할때마다 숫자가 하나씩 증가되게 키값을 설정하는게 되지..않을까...?</p>
<p>일단 백엔드 로직에서 이를 해결하기 위해선</p>
<pre><code class="language-python">[f&#39;filename{i} for i in range(1, len(request.FILES)+1)&#39;]</code></pre>
<p>대충~~ 이런식으로 진행해야 될것 같다.</p>
<h2 id="1차-시도">1차 시도</h2>
<pre><code class="language-python">for i in range (1, len(request.FILES)+1):
  file = request.FILES[f&#39;filename{i}&#39;]
  url_generator = str(uuid.uuid4())
  s3_client.upload_fileobj(
    file,
    # 이하 생략</code></pre>
<p>먼저 업로드된 이미지가 2개일때 <code>request.FILES</code>로 들어온 값을 확인해 보았다.
<img src="https://images.velog.io/images/c_hyun403/post/0bb743d2-f5ba-4279-b1f0-7f65ff670234/%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-05-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.55.35.png" alt=""></p>
<p>성공...😳</p>
<p>RDS와 S3를 모두 확인해 보았다.
<img src="https://images.velog.io/images/c_hyun403/post/b00326b4-2cf1-4356-8d35-d90e74456d6b/%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-05-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.57.15.png" alt=""></p>
<p>굿!</p>
<p>반복문을 이용해 하나씩 순차적으로 저장해 주는 방법을 택했는데 다행히 잘 작동하였다.</p>
<p>혹시 이후에 반복문 말고 같은 키값으로 저장하는 방법을 알게된다면 내용을 추가하겠다!</p>
]]></description>
        </item>
    </channel>
</rss>