<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JJ BLOG</title>
    <description>Ruby on Rails로 웹개발을 하고있는 웹개발자입니다.</description>
    <link>https://jongjineee.github.io/</link>
    <atom:link href="https://jongjineee.github.io/sitemap.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 18 Jun 2020 08:15:38 +0000</pubDate>
    <lastBuildDate>Thu, 18 Jun 2020 08:15:38 +0000</lastBuildDate>
    <generator>Jekyll v3.8.7</generator>
    
      <item>
        <title>Git Alias 설정하기</title>
        <description>&lt;h3 id=&quot;git-alias란&quot;&gt;Git Alias란?&lt;/h3&gt;
&lt;p&gt;Git을 사용하다보면 수많은 명령어를 가지고 있다는 것을 깨닫습니다. 하지만 Git을 쓰다보면 그 많은 명령어와 옵션을 익히게 되고 익숙해지게 됩니다. 이 단계에서 Alias가 필요해지게 됩니다. 자주쓰는 명령어를 간단한 단축키로 설정하여 사용할 수 있게 해주는 것이 바로 Alias입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;alias-추가하기&quot;&gt;Alias 추가하기&lt;/h3&gt;
&lt;p&gt;Git 관련된 설정을 하기 위해 .gitconfig 파일을 수정합니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vim ~/.gitconfig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
[alias] 섹션을 추가하고 섹션의 아래에 축약하고자 하는 명령어를 추가해주면 됩니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[alias]
    s = status -s
    ci = commit
    br = branch
    co = checkout
    l = log --reflog --graph --decorate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
간단히 몇가지 명령어를 넣었습니다. 사용하는 명령어를 잘 넣어주면 편리하게 사용할 수 있습니다. checkout과 같은 명령어를 넣어주면 확실히 편리함을 느낄 수 있습니다.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://johngrib.github.io/wiki/git-alias/&quot;&gt;John Grib&lt;/a&gt;님의 블로그를 참고하면 더 다양한 명령어에 대한 이야기를 보실 수 있습니다!&lt;/p&gt;
</description>
        <pubDate>Sat, 13 Jun 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/06/13/git-alias.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/06/13/git-alias.html</guid>
        
        <category>git</category>
        
        <category>github</category>
        
        <category>alias</category>
        
        <category>git setting</category>
        
        <category>깃</category>
        
        <category>깃헙</category>
        
        <category>깃 얼라이어스</category>
        
        
      </item>
    
      <item>
        <title>컴파일러(Compiler)와 인터프리터(Interpreter)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=12WOTdHsI0mUMPmGjeLpJmw9lzVSDtBVX&quot; alt=&quot;intro&quot; class=&quot;center&quot; width=&quot;100%&quot; /&gt;
&lt;br /&gt;
개발을 시작할 때 가장 먼저 배우는 것은 프로그래밍 언어입니다.
우리는 컴퓨터가 이해할 수 있는 언어인 프로그래밍 언어를 컴퓨터에 입력해 만들고자 하는 어떤 제품이나 서비스를 만들게 됩니다.
하지만 우리는 컴퓨터가 이진수(0과 1로 이루어진 수)만 이해할 수 있다고 알고 있습니다. 
이는 컴퓨터의 CPU가 인식해서 기능을 이해하고 실행할 수 있는 기계어 명령어가 이진수로 이루어져있다는 것을 의미합니다.
이렇게 CPU가 이해할 수 있는 기계어 명령어를 모아놓은 것을 기계어 &lt;span class=&quot;emphasis&quot;&gt;명령어 셋(Instruction Set)&lt;/span&gt;이라고 하며, 여러 기계어 명령어 셋으로 구성한 구조를 &lt;span class=&quot;emphasis&quot;&gt;ISA(Instruction Set Architecture)&lt;/span&gt;라고 합니다. 이런 명령어 셋은 각 CPU 회사 (인텔, AMD 등)에서 만들어내고 때문에 각 회사의 CPU 별로 명령어 셋이 다를 수 있습니다.
&lt;br /&gt;
&lt;br /&gt;
그런데 우리는 프로그래밍을 하면서 0과 1로 개발을 한 적이 없습니다. 하지만 컴퓨터는 0과 1만 이해합니다. 우리가 영문으로 작성한 코드를 컴퓨터가 실행하기 위해 우리의 코드를 번역해주는 번역기가 도와주기 때문입니다. 이러한 번역기를 번역 시기에 따라 두가지로 구분하는데 이것이 &lt;span class=&quot;emphasis&quot;&gt;컴파일러(Compiler)&lt;/span&gt;와 &lt;span class=&quot;emphasis&quot;&gt;인터프리터(Interpreter)&lt;/span&gt;입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;컴파일러compiler&quot;&gt;컴파일러(Compiler)&lt;/h3&gt;
&lt;p&gt;컴파일러는 앞서 말했듯 어떤 언어에서 다른 언어로 번역해주는 프로그램을 뜻합니다. 컴파일러는 우리가 작성한 코드를 &lt;strong&gt;한번에 모두 기계어로 번역&lt;/strong&gt;합니다. 컴파일러를 실행하면 해당 코드를 모두 번역해서 하나의 바이너리 파일로 저장하고 이후 사용자는 이 바이너리 파일을 실행시킵니다. 즉, 컴파일러 언어를 실행할 때, 코드를 작성하고 컴파일을 한 후 이 컴파일된 파일을 실행해야합니다. 컴파일러는 모든 코드를 번역하기 때문에 번역하는 데에 시간이 오래 걸리고 그 과정이 복잡합니다. 하지만 한번 번역을 하면 바이너리 파일이 생성되어 &lt;strong&gt;메모리에 저장&lt;/strong&gt;되며 다음 &lt;strong&gt;실행에 소요되는 시간이 짧아집니다.&lt;/strong&gt; 또한 CPU에 따라 명령어 셋이 다를 수 있기 때문에 어떤 컴퓨터에서 컴파일된 코드를 여러 종류의 컴퓨터로 자유롭게 옮겨다니며 사용할 수 없습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;인터프리터interpreter&quot;&gt;인터프리터(Interpreter)&lt;/h3&gt;
&lt;p&gt;인터프리터는 컴파일러와 다르게 번역해야할 파일을 받아 &lt;strong&gt;한 줄씩 실행&lt;/strong&gt;합니다. 때문에 번역시간은 빠르지만 &lt;strong&gt;실행시간은 느리고&lt;/strong&gt;, 바이너리 파일을 만들지 않아 메모리를 사용하지 않습니다. 한번에 모든 코드를 번역하는 컴파일러와 다르게 한 줄씩 번역하여 실행하기 때문에 개발자의 입장에서는 디버깅을 할 때 좀 더 편리하게 사용할 수 있습니다. 인터프리터의 경우 웹 개발에 유용하게 사용할 수 있습니다. 컴파일러 언오로 웹을 개발한다면 오류 코드가 있을 시 전체 페이지가 모두 먹통이 될 수 있습니다. 하지만 인터프리터 언어로 개발한다면 해당 코드를 실행하지 않는 한 에러는 발생하지 않을 수 있습니다. 반면에 효율을 중시하는 분야에서는 컴파일 언어를 사용하여 실행 속도를 최대한 빠르게 하는 것이 중요할 것입니다.&lt;/p&gt;
</description>
        <pubDate>Sat, 06 Jun 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/06/06/compiler-and-interpreter.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/06/06/compiler-and-interpreter.html</guid>
        
        <category>compiler</category>
        
        <category>interpreter</category>
        
        <category>cs</category>
        
        <category>computer science</category>
        
        <category>programming</category>
        
        <category>컴파일러</category>
        
        <category>인터프리터</category>
        
        <category>프로그래밍 언어</category>
        
        
      </item>
    
      <item>
        <title>Ansible이란?</title>
        <description>&lt;p&gt;서비스에서 사용하는 툴 중 하나인 Ansible에 대해서 알아보고자 합니다. 
AWS의 여러 서버를 관리하는데 사용하고 있으며 YAML 포맷을 기반으로 사용하기 때문에 어렵지 않게 사용할 수 있었습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;infrastructure-as-a-codeiac&quot;&gt;Infrastructure as a Code(IaC)&lt;/h3&gt;
&lt;p&gt;Ansible은 여러 IaC를 기반으로 하는 툴 중 하나입니다. IaC는 기존 인프라의 쉘 스크립트, 수동의 CLI기반 프로비저닝 방식에서 탈피하여, 시스템을 코드 기반으로 자동 설치 및 구축, 관리, 프로비저닝을 구현하는 IT 인프라 구성 프로세스를 말합니다. IaC를 기반으로 하는 툴들은 매우 다양하며 그중 하나로 Ansible이 있습니다. 쉽게 말해 서버의 인프라 구성을 개발을 하듯이 코드로 작성하는 것을 의미합니다. 이는 서버의 환경을 규격화하고 배포되는 모든 서버가 동일한 환경을 유지할 수 있도록 해줍니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;ansible-이란&quot;&gt;Ansible 이란?&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;“Ansible is a radically simple IT automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ansible 이란 머신들을 관리하고 모니터링 하는 하나의 도구입니다. Python으로 구현된 &lt;a href=&quot;https://github.com/ansible/ansible&quot;&gt;오픈소스&lt;/a&gt;로 서버의 프로비저닝, 배포 등의 자동화를 관리해줍니다. 우리가 통상적으로 서버의 프로비저닝이나 배포를 자동화한다고 할 때 가장 먼저 떠오르는 것이 쉘 스크립트입니다. 하지만 쉘 스크립트에 익숙하지 않은 분들에게는 (저를 포함한..) 꽤 어려운 작업입니다. 이러한 작업들을 보다 편리하고 생산성 있게 도와주는 툴이라고 할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;ansible의-특징&quot;&gt;Ansible의 특징&lt;/h3&gt;

&lt;p&gt;여러가지 특징들이 있습니다. 그 특징들 중 아마 아래 두가지 특징으로 인해 Ansible이 지지층을 빠르게 확보할 수 있었을 것입니다.&lt;/p&gt;

&lt;h4 id=&quot;agentless&quot;&gt;Agentless&lt;/h4&gt;
&lt;p&gt;일반적으로 대부분의 IaC 툴들은 중앙에 놓여진 서버에서 관리 할 서버 및 클라이언트에 Agent를 설치하고 데이터를 수집해 오는 Pull 방식 사용합니다. 이 때문에 관리포인트가 늘어나게 됩니다.&lt;/p&gt;

&lt;p&gt;이에 반해 Ansible은 중앙 서버로써 관리 할 각종 서버에 SSH로 통신하여 원격으로 실행하거나 파일을 연결된 서버로 밀어넣어 관리하는 Push 방식을 사용합니다. 때문에 관리포인트는 중앙의 Ansible이 설치된 서버 하나 입니다.&lt;/p&gt;

&lt;p&gt;쉽게 말해 작업 대상이 되는 노드에 별도의 무언가를 설치할 필요가 없습니다. 작업을 명령할 내 로컬에만 Ansible을 설치하면 준비는 끝납니다.&lt;/p&gt;

&lt;h4 id=&quot;멱등성&quot;&gt;멱등성&lt;/h4&gt;
&lt;p&gt;어떤 연산이 여러번 수행되더라도 결과가 달라지지 않는 성질을 의미합니다. 즉, 한번 만들어 둔 설정에 따라 실행을 반복하더라도 결과값이 달라지지 않습니다. 또한 변경 된 부분이 있다면 그 부분만 업데이트 되어 반영됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;ansible의-구성-요소&quot;&gt;Ansible의 구성 요소&lt;/h3&gt;
&lt;h4 id=&quot;본체&quot;&gt;본체&lt;/h4&gt;
&lt;p&gt;Ansible 소프트웨어 자체로 서버/클라이언트 구성과 같은 형태가 아니며 한 번 설치하면 필요할 때 명령을 실해하는 것으로 충분합니다. 
&lt;br /&gt;&lt;/p&gt;
&lt;h4 id=&quot;inventory&quot;&gt;Inventory&lt;/h4&gt;
&lt;p&gt;Ansible이 작업할 대상 머신. “어디에서” Ansible을 실행하는 가?
Inventory는 Ansible에서 조작 대상이 되는 서버 접속 정보를 나타냅니다. Inventory는 여러 개의 서버를 그룹화해 정의하거나 각각의 서버와 그룹에 대해 변수를 사용한 파라미터를 설정할 수 있습니다.&lt;/p&gt;

&lt;p&gt;쉽게 말해서 Host IP들을 정리해 놓은 파일이고 별명을 붙이거나 그룹으로 묶거나 혹은 SSH 접근 방식을 기록해 놓을 수 있습니다.
&lt;br /&gt;&lt;/p&gt;
&lt;h4 id=&quot;module&quot;&gt;Module&lt;/h4&gt;
&lt;p&gt;Ansible에서 실행되는 개별 작업의 정의. “무엇을” Ansible에서 실행하는가?
대상 호스트에서 실행하는 라이브러리들입니다. 2,000개 이상의 Module을 제공하며 버전이 지속적으로 패치되고, 넓은 사용자 층을 확보하면서 많은 모듈을 지원하고 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/modules_by_category.html&quot;&gt;모듈 참조&lt;/a&gt;
&lt;br /&gt;&lt;/p&gt;
&lt;h4 id=&quot;playbook&quot;&gt;Playbook&lt;/h4&gt;
&lt;p&gt;모듈 호출의 중심에 있는 Ansible 코드. “어떻게” Ansible을 실행하는가?
Ansible에서 스크립트이며 Ansible을 사용할 때 필요한 작업은 Playbook의 구현과 실행이라고 할 수 있습니다. YAML로 작성합니다.&lt;/p&gt;
</description>
        <pubDate>Sun, 10 May 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/05/10/ansible.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/05/10/ansible.html</guid>
        
        <category>ansible</category>
        
        <category>IaC</category>
        
        <category>yaml</category>
        
        <category>앤서블</category>
        
        <category>playbook</category>
        
        
      </item>
    
      <item>
        <title>[Rails] Polymorphic Association(다중 연관성)</title>
        <description>&lt;p&gt;얼마전 모임 서비스에 공석 알림 기능을 개발하게 되었습니다. 모임 운영을 자동화하기 위해서 고안해낸 기능인데 이 공석 알림 기능은 모임과 비디오챗에 모두 사용되었습니다. 이 과정에서 사용하게된 Rails의 Polymorphic에 대해서 이야기 하고자합니다.&lt;/p&gt;

&lt;h3 id=&quot;polymorphic-association다중-연관성&quot;&gt;Polymorphic Association(다중 연관성)&lt;/h3&gt;

&lt;p&gt;Polymorphic Association을 번역하면 ‘다중 연관성’입니다. 말 그대로 하나의 모델이 다른 여러 모델과 관계를 맺는 것입니다. 예를들어 위에서 이야기한 ‘공석 알림’ 기능을 ‘모임’과 ‘비디오챗’에 연결하여 사용할 수 있습니다.
만약 Polymorphic을 사용하지 않는다면 아래와 같이 비슷한 유형의 정보를 담은 모델을 각자 따로 만들어야합니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=17hqC-OTpSJwuh7tl0n7rl-O-Ijmp7goL&quot; alt=&quot;pre_waiting_seat&quot; class=&quot;center&quot; width=&quot;60%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이를 하나의 ‘공석 알림’으로 묶기 위해서 Polymorphic Association을 사용할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=102M_n0BJzkuHyiNj26Q_27SvBbomKW3R&quot; alt=&quot;after_waiting_seat&quot; class=&quot;center&quot; width=&quot;60%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;여기서 우리는 foreign_id만을 이용해서 공석 알림이 모임을 위한 것인지 비디오챗을 위한 것인지 알 수 없습니다. 이를 위해 하나의 정보를 더 필요로 하는데 Rails는 사용자가 Type을 저장하지 않아도 자체적으로 연결된 모델의 Type 값을 잘 넣어줍니다.
위와 같은 구조로 model을 설계하기 위해 우리는 아래와 같이 ‘모임’ 또는 ‘비디오챗’이 들어갈 수 있는 ‘Record’라는 네이밍을 통해 이들을 연결했습니다. 공석 알림 모델을 생성하기 위해서 migration 파일을 아래와 같이 설정해줄 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateWaitingSeats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:waiting_seats&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bigint&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record_id&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record_type&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 references를 사용하면 더 단순하게 만들 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateWaitingSeats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:waiting_seats&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;references&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;polymorphic: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:waiting_seats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:record_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;이를 기반으로 각 모델을 설정하면 다음과 같이 작성할 수 있습니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WaitingSeat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;polymorphic: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:waiting_seats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :record&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VideoChat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:waiting_seats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :record&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이제 모임과 비디오챗에서 ‘record’를 통해 각 ‘공석 알림’을 검색할 수 있습니다.
&lt;br /&gt;
ex)&lt;br /&gt;
@video_chat.waiting_seats → 비디오챗 공석 알림,&lt;br /&gt;
@program.waiting_seats → 모임 공석 알림,&lt;br /&gt;
@waiting_seat.record → 모임 또는 비디오챗&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Apr 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/04/26/rails-polymorphic-association.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/04/26/rails-polymorphic-association.html</guid>
        
        <category>rails</category>
        
        <category>polymorphic</category>
        
        <category>다중 연관성</category>
        
        <category>modeling</category>
        
        
      </item>
    
      <item>
        <title>[Python] 쿠팡 파트너스 자동 포스팅 개발기 - 2</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1gPylgzGIIEutY299CsVHF4lfl3PurlwX&quot; alt=&quot;python&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;네이버-블로그에-자동-포스팅하기-&quot;&gt;네이버 블로그에 자동 포스팅하기 &lt;a href=&quot;https://github.com/Jongjineee/auto_coupang_partners&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fab fa-github&quot;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;이전 포스팅 [Python] 쿠팡 파트너스 자동 포스팅 개발기 - 1 에서 쿠팡 파트너스의 API를 이용해 상품의 데이터를 가져오는 방법에 대해서 알아 보았습니다.&lt;br /&gt;
이번 포스팅에서는 네이버의 API를 이용하여 이전에 받아온 쿠팡 파트너스의 상품을 네이버 블로그에 포스팅 해보겠습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;네이버-로그인&quot;&gt;네이버 로그인&lt;/h4&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_logintoken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;webdriver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChromeOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'headless'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;webdriver_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getcwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;r&quot;/chromedriver&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;webdriver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Chrome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webdriver_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;implicitly_wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'https://nid.naver.com/nidlogin.login'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;document.getElementsByName('id')[0].value=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;document.getElementsByName('pw')[0].value=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_element_by_xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'//*[@id=&quot;label_login_chk&quot;]'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_element_by_xpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'//*[@id=&quot;frmNIDLogin&quot;]/fieldset/input'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;implicitly_wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;네이버 블로그에 포스팅 하기 위해서는 로그인이 필요합니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_logintoken()&lt;/code&gt;함수는 id, pw 등 로그인 인증을 위한 인자를 받아 로그인 하는 역할을 합니다. 자동으로 로그인을 하도록 이번에도 webdriver를 이용했습니다. 네이버 로그인 페이지에서 ID와 PW를 입력하고 로그인 버튼을 누르도록 합니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implicitly_wait()&lt;/code&gt;를 이용하여 페이지가 열리는 시간동안 잠시 기다렸다가 코드가 실행되도록 해줍니다.&lt;/p&gt;

&lt;hr /&gt;
&lt;h4 id=&quot;네이버-로그인-api-토큰&quot;&gt;네이버 로그인 API 토큰&lt;/h4&gt;
&lt;p&gt;네이버 블로그 API를 이용하기 위해서는 &lt;a href=&quot;https://developers.naver.com/docs/login/api/&quot;&gt;네이버 아이디 로그인 API&lt;/a&gt;를 이용해 접근 토큰(access token)을 받아야 합니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'https://nid.naver.com/oauth2.0/token?'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'grant_type=authorization_code'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;amp;client_id='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;amp;client_secret='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_secret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;amp;redirect_uri='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;amp;code='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&amp;amp;state='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urllib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'X-Naver-Client-Id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'X-Naver-Client-Secret'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urllib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rescode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getcode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rescode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;js&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'utf 8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'access_token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error Code:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rescode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;토큰을 받기위한 Request를 URL은 위 링크에 자세히 나와있습니다. 간단히 설명하자면 client_id와 client_secret, callback_url 등의 data를 포함하여 Request를 보내고 받은 response를 확인합니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Response Example
{'access_token': '', 'refresh_token': '', 'token_type': 'bearer', 'expires_in': ''}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 response를 받게 되면 access_token을 사용할 수 있게 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;네이버-블로그-api&quot;&gt;네이버 블로그 API&lt;/h4&gt;
&lt;p&gt;위에서 네이버 로그인 API로 access_token을 받았다면 네이버 블로그 API를 사용할 수 있게 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write_product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;category_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;blog_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bearer &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access_token&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Bearer 다음에 공백 추가
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;blog_write_api&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://openapi.naver.com/blog/writePost.json&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;blog_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'title'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'categoryNo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;category_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'contents'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;blog_headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Authorization'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blog_header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_write_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blog_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;rescode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rescode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;Bearer Authentication란?&lt;br /&gt;
API에 접속하기 위해서는 access token을 API 서버에 제출해서 인증을 해야 합니다. 이 때 사용하는 인증 방법이 Bearer Authentication 입니다. 이 방법은 OAuth를 위해서 고안된 방법이고, &lt;a href=&quot;https://tools.ietf.org/html/rfc6750&quot;&gt;RFC 6750&lt;/a&gt;에 표준명세서가 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;access_token을 전송할 때 토큰을 인증하기 위한 방법을 포함한 요청 변수들을 넣어줍니다. 사용할 수 있는 기능들은 &lt;a href=&quot;https://developers.naver.com/docs/blog/post/&quot;&gt;네이버 블로그 API&lt;/a&gt;에서 확인하실 수 있습니다. 이전 포스팅에서 image에 대해서 특이한 점이 있다고 말했는데&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;img = [('image', (fname, open(fnamefull, 'rb'), 'image/png', {'Expires': '0'}))]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 img 리스트에 여러 이미지를 multipart request로 전송해야 된다고 합니다. 덕분에 contents를 작성할 때 img 태그에 src=”#숫자”와 같은 형태로 쉽게 불러와 사용할 수 있습니다.
이제 write_product로 Request API를 보내면 네이버 블로그에 자동으로 포스팅이 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;실적&quot;&gt;실적&lt;/h4&gt;
&lt;p&gt;코드가 까다롭거나 어렵지 않아서 쉽게 이해할 수 있을 것입니다. 코드는 Github에 올려 놓았으니 누구나 사용하실 수 있습니다. 포스팅하는 콘텐츠를 좀 더 성의있게 꾸며야 되는데 미루다가 결국 못꾸미고 그대로 사용해버렸습니다. 그래서 그런지 실적이 너무 저조하네요..😭 그래도 코드를 실행한 3월 9일부터 29일까지 약 20일간 자동화 코드를 실행하고 발생한 실적을 공개합니다.🙂
&lt;img src=&quot;https://drive.google.com/uc?id=15Sg3byr_EAEzGnNo4rv1SLKZHFezfdGO&quot; alt=&quot;쿠팡실적&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 29 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/03/29/auto_naver.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/03/29/auto_naver.html</guid>
        
        <category>블로그</category>
        
        <category>쿠팡</category>
        
        <category>쿠팡 파트너스</category>
        
        <category>API</category>
        
        <category>python</category>
        
        <category>크롤링</category>
        
        <category>토이 프로젝트</category>
        
        <category>selenium</category>
        
        
      </item>
    
      <item>
        <title>[Python] 쿠팡 파트너스 자동 포스팅 개발기 - 1</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1gPylgzGIIEutY299CsVHF4lfl3PurlwX&quot; alt=&quot;python&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;쿠팡-파트너스-상품-가져오기-&quot;&gt;쿠팡 파트너스 상품 가져오기 &lt;a href=&quot;https://github.com/Jongjineee/auto_coupang_partners&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fab fa-github&quot;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;‘쿠팡 파트너스’는 쿠팡에서 운영하는 온라인 제휴마케팅 서비스입니다. 개인의 홈페이지, 블로그, SNS 등을 사용하여 쿠팡 상품을 노출하여 구매가 발생하면 광고비를 지급합니다. 즉, 쿠팡의 제품을 쿠팡 파트너스를 통해 링크를 발급받아 공유하고, 그 링크를 통해 구매가 발생하면 광고비를 얻을 수 있는 것입니다. 쿠팡 파트너스에서는 사용자가 적용할 수 있는 쿠팡의 상품 정보를 포함한 파트너스의 기능과 실적 정보 등을 API로 제공합니다. 이 쿠팡 파트너스와 네이버 블로그 API를 이용하여 쿠팡 상품을 네이버 블로그에 자동 포스팅하는 
Python 코드를 만들어 보았습니다.
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://partners.coupang.com/#help/open-api&quot;&gt;∙ 쿠팡 API&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;https://developers.naver.com/products/blog/&quot;&gt;∙ 네이버 블로그 API&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
코드는 &lt;span class=&quot;emphasis&quot;&gt;main.py&lt;/span&gt;를 통해서 실행할 수 있으며 &lt;span class=&quot;emphasis&quot;&gt;auto_coupang.py&lt;/span&gt;를 통해 쿠팡 파트너스의 상품 데이터를 가져오고 
&lt;span class=&quot;emphasis&quot;&gt;auto_naver.py&lt;/span&gt;를 통해 상품을 블로그에 포스팅합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;hmac-인증&quot;&gt;HMAC 인증&lt;/h4&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TZ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;GMT+0&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'%y%m%d'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'T'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'%H%M%S'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Z'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;hashlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sha256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hexdigest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;f&quot;CEA algorithm=HmacSHA256, access-key=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;access_key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, signed-date=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, signature=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;쿠팡 API를 통해서 상품 정보를 요청할 때 쿠팡은 &lt;span class=&quot;emphasis&quot;&gt;HMAC 인증 방식&lt;/span&gt;을 통해 클라이언트를 인증합니다. HMAC인증은 REST API 요청을 받을 때, 요청의 중간 과정에서 위변조가 있었는지 알아내는 방법입니다. 해싱 기법을 적용하여 메시지의 위변조를 방지하는 기법을 &lt;span class=&quot;emphasis&quot;&gt;HMAC(Hash-based Message Authentication)&lt;/span&gt;이라고 합니다.&lt;br /&gt;
과정을 차례대로 살펴보면&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;사전에 서버와 클라이언트는 별도의 채널에서 해시에 사용할 &lt;strong&gt;Key&lt;/strong&gt;를 공유합니다.&lt;/li&gt;
  &lt;li&gt;클라이언트는 &lt;strong&gt;Key&lt;/strong&gt;와 &lt;strong&gt;Message&lt;/strong&gt;를 HMAC 알고리즘으로 
&lt;strong&gt;Hash&lt;/strong&gt; 값으로 만듭니다.&lt;/li&gt;
  &lt;li&gt;생성된 &lt;strong&gt;Hash&lt;/strong&gt; 값과 &lt;strong&gt;Message&lt;/strong&gt;를 &lt;strong&gt;HTTP 요청&lt;/strong&gt;으로 서버에 보냅니다.&lt;/li&gt;
  &lt;li&gt;서버는 클라이언트에게 받은 요청의 &lt;strong&gt;Message&lt;/strong&gt;와 본인이 가진 &lt;strong&gt;Key&lt;/strong&gt;를 조합하여 HMAC 알고리즘으로 클라이언트와 동일하게 &lt;strong&gt;Hash&lt;/strong&gt; 값을 만듭니다.&lt;/li&gt;
  &lt;li&gt;클라이언트에서 받은 &lt;strong&gt;Hash&lt;/strong&gt; 값과 서버에서 생성한 &lt;strong&gt;Hash&lt;/strong&gt; 값을 비교합니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;클라이언트에서 정상적으로 생성된 Hash 값을 재사용하는 것을 막기위해 Hash를 생성할 때 &lt;strong&gt;TimeStamp&lt;/strong&gt;를 포함하는 것이 좋습니다. 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;generate_hmac&lt;/code&gt;으로 Hash 값을 생성했다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_product&lt;/code&gt;로 상품 정보를 가져옵니다. 
&lt;strong&gt;Requests 모듈&lt;/strong&gt;을 이용해서 HTTP 요청을 보냅니다. 요청의 헤더에는 앞서 인증을 받기위해 생성한 Hash 값을 넣어줍니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;퍼센트-인코딩&quot;&gt;퍼센트 인코딩&lt;/h4&gt;

&lt;p&gt;요청을 보낼 때 URL은 쿠팡 파트너스의 검색 기능을 이용합니다. 검색에는 &lt;span class=&quot;emphasis&quot;&gt;keyword&lt;/span&gt;와 &lt;span class=&quot;emphasis&quot;&gt;limit&lt;/span&gt; 파라미터가 사용됩니다. Limit는 integer 타입의 데이터를 받기 때문에 쉽게 넣을 수 있지만 keyowrd에는 &lt;span class=&quot;emphasis&quot;&gt;한글&lt;/span&gt;을 입력하게 됩니다. URL에 사용할 수 있는 문자는 &lt;span class=&quot;emphasis&quot;&gt;ASCII 코드&lt;/span&gt;로 표현할 수 있는 영문, 숫자, 몇몇 기호 뿐입니다. 때문에 한글은 사용할 수 없습니다.
예를들어 쿠팡에 ‘가구’를 검색하여 나온 URL을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urllib.request.urlopen()&lt;/code&gt;으로 요청하면 ‘가구’라는 한글 때문에 에러가 발생합니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; urllib.request.urlopen('https://www.coupang.com/np/search?component=&amp;amp;q=%EA%B0%80%EA%B5%AC&amp;amp;channel=user')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 28029: ordinal not in range(128)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이를 피하기 위해 URL에서 ‘가구’라는 검색어를 퍼센트 인코딩(percent encoding)으로 인코딩해주어야 합니다. 우리가 사용하는 웹 브라우저에서는 퍼센트 인코딩을 자동으로 수행해주지만 파이썬에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urllib.parse.quote()&lt;/code&gt;로 한글을 ASCII 코드로 변환해 줍니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;rCode&quot;: &quot;0&quot;,
  &quot;rMessage&quot;: &quot;&quot;,
  &quot;data&quot;: {
	&quot;landingUrl&quot;: &quot;https://link.coupang.com/re/AFFSRP?lptag=AF1234567&amp;amp;pageKey=fashion&amp;amp;traceid=V0-153-5243b8cd44933da5&quot;,
	&quot;productData&quot;: [
  	{
    	&quot;keyword&quot;: &quot;Water&quot;,
    	&quot;rank&quot;: 12,
    	&quot;isRocket&quot;: false,
    	&quot;productId&quot;: 27664441,
        &quot;productImage&quot;: &quot;http://static.coupangcdn.com/image/product/image/vendoritem/2018/03/12/3205353792/48d92887-82e7-46f5-8704-89df99a23bde.jpg&quot;,
    	&quot;productName&quot;: &quot;탐사 소프트 100% 천연펄프 3겹 롤화장지 30m, 30롤, 1팩&quot;,
	    &quot;productPrice&quot;: 15600,
    	&quot;productUrl&quot;: &quot;https://link.coupang.com/re/AFFSDP?lptag=AF1234567&amp;amp;pageKey=319834306&amp;amp;itemId=1023216541&amp;amp;vendorItemId=70064597513&amp;amp;traceid=V0-163-5fddb21eaffbb2ef&quot;
  	}
	]
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;요청을 통해 위와 같은 형식의 데이터를 받게 되고 &lt;span class=&quot;emphasis&quot;&gt;‘productData’&lt;/span&gt;에서 사용하고자 하는 데이터를 사용합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;selenium&quot;&gt;Selenium&lt;/h4&gt;
&lt;p&gt;위에서 가져온 상품의 데이터만으로도 블로그에 상품을 포스팅 하기에는 충분합니다. 하지만 더 많은 정보를 가져오고 싶어서 상품 상세페이지를 &lt;span class=&quot;emphasis&quot;&gt;크롤링&lt;/span&gt;하도록 했습니다. 크롤링을 위해 &lt;span class=&quot;emphasis&quot;&gt;Selenium&lt;/span&gt;을 사용했습니다. Selenium은 &lt;span class=&quot;emphasis&quot;&gt;webdriver&lt;/span&gt;를 통해 브라우저를 제어할 수 있습니다. 자신이 쓰고자 하는 브라우저의 driver를 설치합니다. 저는 Chrome driver를 설치했습니다.
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://sites.google.com/a/chromium.org/chromedriver/downloads&quot;&gt;∙ Chrome driver&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
코드에서 &lt;span class=&quot;emphasis&quot;&gt;ChromeOptions&lt;/span&gt; 객체를 생성하여 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_argument()&lt;/code&gt;를 통해 여러가지 옵션을 설정할 수 있습니다. 이중 &lt;span class=&quot;emphasis&quot;&gt;‘headless’&lt;/span&gt;는 브라우저를 렌더링하지 않고 메모리 상에서만 작업이 이루어지도록 합니다. 
크롤링을 진행할 브라우저인 User-agent 정보를 입력하고 이전에 설치한 chromedriver의 경로를 &lt;span class=&quot;emphasis&quot;&gt;webdriverpath&lt;/span&gt;에 넣어줍니다. 저는 코드와 같은 위치에 넣어주었습니다.
이후 설정한 옵션과 함께 chromedriver를 실행해줍니다. 이때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.sleep()&lt;/code&gt;을 이용해 페이지 로딩이 완전히 이루어질 때까지 잠깐 멈춰 기다려줍니다.
&lt;br /&gt;
&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Driver.execute_script()&lt;/code&gt;를 통해 실행하는 script 코드들은 &lt;span class=&quot;emphasis&quot;&gt;크롤러 탐지를 우회&lt;/span&gt;하는 코드입니다. 플러그인 속성을 덮어써 임의의 배열을 반환하도록 하고, 언어 설정이 되어 있지 않은 Headless모드에 언어를 설정해줍니다. 또 그래픽카드가 돌아가는 척하도록 해줍니다. 사람인 척하는 것입니다.&lt;br /&gt;
이렇게 봇이 아닌 척을 열심히 하고 페이지의 요소들을 가져옵니다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Find_element_by_class_name&lt;/code&gt;을 이용하면 쉽게 요소들을 가져올 수 있습니다. 이미지의 경우 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os.makedirs()&lt;/code&gt;를 이용해 폴더를 생성하고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urllib.request.urlretrieve()&lt;/code&gt;를 이용해 이미지를 바로 파일로 저장할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Return 값에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img&lt;/code&gt;의 값이 조금 특이한데 이는 이후 &lt;span class=&quot;emphasis&quot;&gt;네이버 블로그 API&lt;/span&gt;를 다루는 편에서 자세히 알려드리겠습니다.
이렇게 상품을 가져오는 &lt;span class=&quot;emphasis&quot;&gt;auto_coupang.py&lt;/span&gt; 코드를 만들었고 다음편에 &lt;span class=&quot;emphasis&quot;&gt;auto_naver.py&lt;/span&gt;를 리뷰하겠습니다.&lt;/p&gt;
</description>
        <pubDate>Sun, 15 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/03/15/auto_coupang.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/03/15/auto_coupang.html</guid>
        
        <category>블로그</category>
        
        <category>쿠팡</category>
        
        <category>쿠팡 파트너스</category>
        
        <category>API</category>
        
        <category>python</category>
        
        <category>크롤링</category>
        
        <category>토이 프로젝트</category>
        
        <category>selenium</category>
        
        
      </item>
    
      <item>
        <title>글또 4기 시작!</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1dJxg_LBqAyduQHm9tx7zq84Zrzi_lT7P&quot; alt=&quot;글또_인트로&quot; class=&quot;center&quot; width=&quot;30%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
2020년이 시작되고 글을 하나도 못썼다..😭😭😭 &lt;br /&gt;
핑계 아닌 핑계를 대자면 새로운 서비스를 런칭하느라 눈코 뜰 새 없이 바쁘기는 했다.
그래도 놀건 다 놀았으니 블로그를 잊고 있었던 것은 맞다.
글또 3기를 시작하고 꽤 열심히 글 소재를 찾았고 글을 열심히 올렸는데
3기가 마무리되자마자 블로그를 돌보지 않았다.
그런 내 모습을 잘 알고 있기 때문에 4기를 이어서 다시 들어가게 되었고 운영진으로 함께 하기로 하였다.&lt;/p&gt;

&lt;h3 id=&quot;-글또-4기-자동화팀&quot;&gt;&lt;i class=&quot;fas fa-map-pin&quot; style=&quot;color:#f50e57&quot;&gt;&lt;/i&gt; 글또 4기 자동화팀&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;글또 4기에는 3기보다 더 많은 분이 함께 하게 되었다.
3기에는 &lt;span class=&quot;emphasis-org&quot;&gt;44명&lt;/span&gt;이 함께 하였고 4기에는 &lt;span class=&quot;emphasis-org&quot;&gt;69명&lt;/span&gt;이 함께 한다.
이전 기수에서는 활발한 오프라인 모임을 하지 않았기 때문에 많은 개발자분을 글로만 접할 수 있었다.
올린 포스팅을 읽고 어떤 일을 하고 어떤 공부를 하는지 엿볼 수 있었다. &lt;br /&gt;
&lt;br /&gt;
하지만 이번 4기에서는 더 많은 운영진이 생겼고 이들을 운영팀과 자동화 팀으로 나누었다.
운영 팀은 오프라인 모임을 포함하여 더 활발한 모임이 이루어질 수 있도록 도와주는 역할 등을 하고 
자동화 팀에서는 글또의 핵심 규칙에 맞게 글또분들이 활동을 잘하는지 자동으로 확인할 수 있도록 하는 역할을 한다.&lt;br /&gt;
&lt;br /&gt;
나는 글또 &lt;span class=&quot;emphasis-org&quot;&gt;자동화 팀&lt;/span&gt;에 들어가 글또를 돕기로 하였다. 
회사에서 자동화 작업을 많이 하고 있지만, 다른 개발자분들과 협업하여 좋은 코드를 만들어내고 많은 사람이 만족할 만한 좋은 모임을 만들어 가는데 기여할 수 있다는 것에 보람을 느끼고 열심히 해볼 생각이다.&lt;/p&gt;

&lt;h3 id=&quot;-글-작성-계획&quot;&gt;&lt;i class=&quot;fas fa-map-pin&quot; style=&quot;color:#f50e57&quot;&gt;&lt;/i&gt; 글 작성 계획&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;최대한 많은 글을 작성하는 것이 일단 목표이다.&lt;br /&gt;
글또에서 정해진 기간에 한정하여 글을 작성하기보다 더 많은 글을 작성해보고 싶다. 블로그에 글을 쌓아 빨리 디자인을 새롭게 다시 해보고 싶다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;많은 &lt;span class=&quot;emphasis-org&quot;&gt;토이 프로젝트&lt;/span&gt;도 해보고 싶고, 그런 프로젝트들의 개발기를 블로그에 잘 정리해서 올릴 것이다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;업무 중 새롭게 알게 된 사실이나 공부한 내용을 글로 적어서 올리고 블로그에 정성을 꽤 들여볼 생각이다. &lt;span class=&quot;emphasis-org&quot;&gt;레일즈&lt;/span&gt;를 사용하기 때문에 아마 레일즈에 관련된 내용을 올릴 것 같다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;글또 자동화 프로젝트&lt;/span&gt;에 대한 이야기를 잘 정리하고 싶다. 아직 본격적으로 진행이 되지 않아 어떻게 개발을 할지는 잘 모르겠으나 다른 사람들과 함께하는 프로젝트이다 보니 최대한 노력할 생각이다. 남에게 피해를 주고 싶지 않다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;알고리즘, 자료구조와 같은 &lt;span class=&quot;emphasis-org&quot;&gt;CS 전공&lt;/span&gt;을 공부할 생각이다. 살면서 독서랑은 담을 쌓고 지냈지만, 최근에 책을 읽기 시작했다. 조금씩 책과 친해지고 CS 전공 책을 읽어보고 싶다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-마무리&quot;&gt;&lt;i class=&quot;fas fa-map-pin&quot; style=&quot;color:#f50e57&quot;&gt;&lt;/i&gt; 마무리&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;매번 시작에는 의욕이 앞서고 이것저것 벌려 놓는 것을 좋아하는데 이번에는 꼭 모두 마무리하여 보람찬 활동이 되었으면 좋겠다. 
바쁜 업무가 아직 많이 남아있어서 모두 할 수 있을지는 모르겠으나 그래도 쉬는 시간과 노는 시간이 있는 것을 보면 할 수 있는데 안 하는 것 같다. 
다양한 개발자분들과 좋은 모임에서 각자의 고민과 이야기를 나누고 좋은 글을 공유할 수 있게 해준 글또에게 감사하고 열심히 활동해보려고 한다.&lt;/p&gt;
</description>
        <pubDate>Sat, 29 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2020/02/29/fourth_geultto_start.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2020/02/29/fourth_geultto_start.html</guid>
        
        <category>블로그</category>
        
        <category>개발자</category>
        
        <category>신입 개발자</category>
        
        <category>개발 블로그</category>
        
        <category>글또</category>
        
        <category>글또 4기</category>
        
        
      </item>
    
      <item>
        <title>2019년 회고(글또, 여행, Google Analytics, 서비스 개발)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1T0W_P0Z2akAKWt0jMqPa1aKNUSfl6J5P&quot; alt=&quot;미국_인트로&quot; /&gt;&lt;/p&gt;

&lt;p&gt;벌써 2019년이 끝나간다. 2018년 막바지에 취업이 되어 서비스 개발을 하게되었고 지금까지 열심히 해오고 있다.
여행도 많이 다녀오고 나름 큰 문제없이 잘 지내온 것 같다.&lt;/p&gt;

&lt;h3 id=&quot;-글또&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 글또&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;2019년에 가장 잘한 일 중 하나는 글또 활동이다. 혼자 블로그를 했다면 이렇게까지 하지도 못했을 것이다. 
그렇다고 글을 엄청 많이 쓰거나 내용이 좋은 글을 쓴 것은 아니다. 하지만 내가 잘했다고 생각하는 이유는 꾸준한 포스팅 때문이다. 
글또를 시작하면서 매달 글을 써왔고, 다른 사람들이 포스팅한 글도 많이 읽게 되었다. 
다른 개발자들이 어떤 분야에 관심이 있는지, 어떤 공부를 하고 있는지 엿볼 수 있었다. 
나름대로 자극도 받았고 더 열심히 공부해야겠다는 다짐도 했지만 다짐한 만큼 이루어내지는 못한 것 같아서 조금 아쉽다. 
기회가 된다면 다음 기수에도 함께하여 계속해서 이 활동을 이어가고 싶다.&lt;/p&gt;

&lt;h3 id=&quot;-여행&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 여행&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;미국, 태국, 일본 세 나라를 다녀왔다. 원래 해외여행은 별로 좋아하는 편이 아니었는데 미국을 한번 다녀오고 생각이 바뀌었다. 
다른 나라의 문화를 경험한다는 것은 호기심이 많고 새로운 경험을 좋아하는 내가 휴가를 쓰고 할 수 있는 최고의 활동이다. 
미국은 두 번째 방문이었는데 2018년에는 그랜드캐니언을 방문했고 이번에는 요세미티를 다녀왔다. 
그랜드캐니언과 또 다른 느낌의 대자연이었고 캠핑을 할 수 있어서 좋았다. 
항상 느끼지만, 대자연이 주는 충격은 다른 어떤 충격보다 대단한 것 같다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1xrTQV_TTDA_WthO8s03q8JSfAEsX2zgG&quot; alt=&quot;요세미티&quot; width=&quot;49%&quot; /&gt;
&lt;img src=&quot;https://drive.google.com/uc?id=1tUBazvfgS-67T_G09uHhW2Dw7X3XmfAv&quot; alt=&quot;후버댐&quot; width=&quot;49%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;동남아를 한 번도 가보지 못해서 태국을 가보기로 했다. 주변 사람들은 마사지를 받는 것을 좋아하여 1일 1 마사지를 받는다고 했지만 
내게는 마사지가 맞지 않는 것 같다. 아파서 싫어하기보다 누군가 신체를 만지는 것이 별로 기분 좋은 일은 아닌 것 같다. 
태국은 큰 사원이 많아 구경하기 좋았고 거리도 굉장히 깨끗했다. 처음 음식이 입맛에 맞지 않아서 태국 음식은 내 입맛에 맞지 않다고 생각했지만 
이후 먹어본 음식점에서는 너무 맛있게 먹어서 태국 음식도 내 입맛에 맞는 걸로… 특히 야시장이 제일 재미있었고 행복했다. 맛있는 음식을 많이 먹을 수 있었다!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1ex5FRi8A2tPuJYWKp7alXkkQHQj8zOvB&quot; alt=&quot;태국&quot; width=&quot;70%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;일본은 오사카를 다녀왔다. 지금은 나라 간 관계가 좋지 않아서 가고 싶지 않지만, 이전에 갔다 왔던 도쿄를 경험하고 기억이 좋아서 오사카를 다녀오게 되었다. 
일본은 우리나라와 크게 다른 점이 없는 나라지만 그래도 외국인들 사이에 섞여서 여행한다는 것이 기분을 좋게 만드는 것 같다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1wrpq1yE9DRTXdIViWYdK2AeW0GiaHMyi&quot; alt=&quot;일본&quot; width=&quot;60%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;-google-analytics&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; Google Analytics&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;새로운 CTO님이 들어오면서 서비스를 개발하는데 체계를 잡아가고 있다. 
데이터 기반 의사결정을 하기 위해 GA를 적극적으로 활용하고 있다. 이전에는 GA에 대한 이해가 부족하고 자유롭게 다루지 못해서 
데이터를 확인하는 데 어려움을 겪었다. 때문에 새로 온 CTO님이 와서 가장 먼저 한 일은 팀원들에게 GA 교육을 해준 것이다. 
GA로 데이터를 보내기 위해 Tracking 코드를 심었고 세그먼트를 만들어 보고 싶은 데이터를 마음껏 볼 수 있도록 해주었다. 
개발 전에는 항상 이루고자 하는 목표가 있으며 데이터를 확인하고 개선하고자 하는 부분을 개선한다. 
개선하여 배포한 이후 데이터를 보고 우리가 목표한 바를 이루었는지 확인한다. 
당연한 프로세스 같지만, GA를 충분히 활용하지 못한다면 이 프로세스를 실행하기 어렵다. 
이러한 방식으로 개발을 하니 성과를 명확히 확인할 수 있고, 개선하고자 하는 방향이 명확해지는 것 같다. 
앞으로의 서비스 개발이 기대되며 더 많은 데이터에 욕심이 생긴다.&lt;/p&gt;

&lt;h3 id=&quot;-서비스-개발&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 서비스 개발&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;1년간 여러 가지 기능을 개발하였고 개선해왔지만 가장 큰 아이템은 오프라인 모임 기능이다. 
개발자가 나 혼자였고 때문에 혼자 힘으로 개발을 했다. 혼자서 개발하기 때문에 나름 고심하여 만들었다. 
기능은 의도했던 대로 구현되었고 배포하였을 때 사용자의 반응도 좋았다. 하나의 성과가 생겼구나 하고 좋아하였는데 문제는 
이후에 발생했다. 기능의 사용자를 고려할 때 Admin에 대해서 충분히 생각하지 못하여 해당 기능을 담당하는 담당자들이 많이 힘들어했다. 
자동화 할 수 있는 부분은 최대한 자동화하여 기능을 개발해야했는데 새로운 서비스를 만들 때 뭔가 불안한 마음이 들어 수동으로 처리하도록 한 
기능들이 많았다. 이후 업무 담당자들은 해당 기능에 맞추어 업무 프로세스를 만들었고 이에 적응해왔다. 나중에 와서 이 부분들은 모두 자동화하려고 하니 
이미 익숙해진 프로세스를 업무 담당자들은 포기하지 못했고, 나 또한 지금의 프로세스보다 업무 효율을 높일 수 있다는 확신을 하기 어려웠다. 
다행히 함께하게 된 CTO님께 해당 이슈를 공유하였고 같이 개선해 나가기로 하였다. 
서비스 개발은 고려해야 할 부분도 많고 하나라도 놓치면 이후 눈덩이처럼 문제가 불어나기 때문에 생각을 많이 해야한다. 
이번 일로 많은 부분을 반성할 수 있었고 한 단계 성장할 수 있는 계기가 된 것 같다.&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/12/22/review2019.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/12/22/review2019.html</guid>
        
        <category>블로그</category>
        
        <category>회고</category>
        
        <category>개발자</category>
        
        <category>신입 개발자</category>
        
        <category>개발 블로그</category>
        
        
      </item>
    
      <item>
        <title>DEVIEW DAY2 2019 후기</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1QkGwwXevEYffDOzfGk9I2O_HoDUPzUr3&quot; alt=&quot;intro&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;DEVIEW?&lt;/span&gt;&lt;br /&gt;
지식을 나누고 탁월함을 추구하며, 함께 성장하는 대한민국 대표 개발자 컨퍼런스
내일을 향한 개발자들의 시선이라는 의미를 담은 DEVIEW (Developer’s View). 
개발자들이 축적한 지식과 경험, 노하우를 공유하여 대한민국 개발자들의 성장을 이끌어가기 위해, 매년 가을 개최되는 대한민국 대표 개발자 컨퍼런스입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;2019 DEVIEW가 코엑스 그랜드볼룸에서 열렸습니다. 
DAY 1에는 주로 AI에 대한 세션이 많았습니다. 
저는 웹과 클라우드에 관심이 있었기 때문에 DAY2에 참가하게 되었습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1id8w1BEGdtCl9HGOexssAIS7fp8Fk0o1&quot; alt=&quot;TimeTable&quot; /&gt;
&lt;span class=&quot;caption&quot;&gt;참가자 스케줄&lt;/span&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;1-multi-tenancy-kubernetes-on-bare-metal-servers-네이버-컨테이너-클러스터&quot;&gt;1. Multi-Tenancy Kubernetes on Bare-Metal Servers (네이버 컨테이너 클러스터)&lt;/h4&gt;

&lt;p&gt;첫번째 세션으로 이종현님의 Multi-Tenancy Kubernetes을 들었습니다.
아직 쿠버네티스를 제대로 사용해보지 못했고 궁금해서 몇번 찾아보고 관심을 갖고 있었던 중이라 세션을 듣게 되었습니다. 
세션의 내용은 네이버의 컨테이너 플랫폼 팀에서 쿠버네티스를 어떤 식으로 구현했고 운영하는 중이며 어떤 방향으로 나아갈 것인가를 다루었습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;emphasis&quot;&gt;“네이버 컨테이너 클러스터”&lt;/span&gt;는 네이버의 서비스/플랫폼을 서빙할 수 있는 컨테이너 인프라스트럭처를 제공하는 프로젝트입니다.
네이버와 같은 대규모 서비스의 경우 많은 수의 컨테이너를 관리하기 위해서 &lt;span class=&quot;emphasis&quot;&gt;Multi-Tenancy Kubernetes&lt;/span&gt;를 도입합니다. 
이는 단점과 장점이 있는데 제가 이해한 바로는 대규모의 쿠버네티스를 하나의 팀에서 관리하고, 이를 이용하는 나머지 팀과 다양한 리소스들을 공유합니다.
때문에 나머지 팀은 이 쿠버네티스를 이용하는 서버를 보유할 필요가 없으며, 이를 위한 여유 리소스를 확보하지 않아도 됩니다. 
하지만 이러한 공유가 잘 이루어지기위해서는 모두가 쿠버네티스에 대한 지식이 있어야하며 공유하는 컨테이너에 신뢰성이 확보되어야합니다. 물론, 운영하는 팀은 큰 쿠버네티스를 운영하기에 힘들것입니다.&lt;br /&gt;
&lt;br /&gt;
대규모 서비스에서 모두가 고민하는 예상치못한 실시간 트레픽을 &lt;span class=&quot;emphasis&quot;&gt;오토 스케일링&lt;/span&gt;을 통해 대응하는 방법을 설명해주었습니다. 
또한 여유 리소스를 보유하는 서버를 각 컨테이너들이 공유하여 리소스를 최소화하고 효율적으로 이를 활용하는 방법을 볼 수 있었습니다. 
이후 클러스터가 커지면서 발생한 여러가지 이슈들에 대해 공유하였고 이를 해결하기위해 시도한 방법, 해결 방안 등을 공유해주었습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;2-pinpoint는-어떻게-observability를-강화했는가&quot;&gt;2. Pinpoint는 어떻게 observability를 강화했는가&lt;/h4&gt;

&lt;p&gt;두번째 세션으로 구태진님의 ‘Pinpoint는 어떻게 observability를 강화했는가’를 듣게 되었습니다. 
&lt;strong&gt;&lt;a href=&quot;https://github.com/naver/pinpoint&quot; target=&quot;_blank&quot;&gt;PINPOINT&lt;/a&gt;&lt;/strong&gt;란 대규모 분산 시스템의 성능을 분석하고 문제를 진단, 처리하는 &lt;span class=&quot;emphasis&quot;&gt;java 플랫폼&lt;/span&gt;입니다.
네이버에서 개발한 오픈소스로 STAR 수가 굉장했습니다. 실제로 세계적으로 큰 기업에서도 사용하고 있습니다.
MSA에 대해 공부한지 얼마 지나지 않아 MSA에서의 &lt;span class=&quot;emphasis&quot;&gt;모니터링&lt;/span&gt; 방법에 대한 설명을 듣게되어 반가웠습니다.
세션은 Pinpoint팀이 해왔던 고민들과 이 고민들을 해결하기 위해서 해왔던 몇몇 작업들을 보여주었습니다.&lt;br /&gt;
&lt;br /&gt;
Pinpoint는 대규모 분산환경을 이해하기 쉽도록 보여줍니다. 때문에 이를 이용하면 거대한 서비스 구조를 한눈에 파악하기 쉽고 문제의 원인을 빠르게 찾도록 도와줍니다.
Pinpoint의 코어에 대한 내용은 ‘DEVIEW 2015’에 자세히 설명이 되어있다고합니다.
구태진님은 리얼타임기능을 구축하는 과정을 발표했는데
흥미로운 점은 pinpoint 내에서 데이터를 주고 받을 때 &lt;span class=&quot;emphasis&quot;&gt;Handshake&lt;/span&gt; 방식을 사용한다는 것이었습니다.
이후 데이터 분산 모듈화를 위해 &lt;span class=&quot;emphasis&quot;&gt;주키퍼&lt;/span&gt;를 사용하는 방식을 설명해주고 성능개선, 병렬처리등 많은 이야기를 해주셨습니다.
Pinpoint를 사용해보지 못해서 내용을 이해하는데 조금 어려움이 있었지만 문제를 인지하고 해결하기까지의 과정이 흥미로웠습니다.
Pinpoint는 더욱 많은 언어들을 지원하기위해 노력하고 있다고하니 기대가됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;3--fail-fast-learn-faster-sre-실패에서-배워나가는-sre&quot;&gt;3.  Fail Fast, Learn Faster SRE (실패에서 배워나가는 SRE)&lt;/h4&gt;

&lt;p&gt;세번째 세션은 &lt;span class=&quot;emphasis&quot;&gt;SRE(Site Reliability Engineering)&lt;/span&gt;에 대한 세션이었습니다. 
SRE는 어떻게 하면 대규모 시스템에서 신뢰성을 보장할지 고민하는 기술 분야이자 방법론입니다.
네이버 검색의 SRE는 대한민국에서 발생하는 온갖 종류의 이벤트들에 대응하면서 성장하고 있었습니다.&lt;br /&gt;
&lt;br /&gt;
세션 전 밖에서 여러 스타트업과 네이버 서비스팀의 부스를 돌아다니다가 그만 앞부분을 놓쳤습니다…
나중에라도 들어갔는데 세션이 너무 재미있어서 정말정말 아쉬웠습니다.&lt;/p&gt;

&lt;p&gt;24시간 이상없이 운영이 되어야하는 서비스라면 규모가 커질수록 SRE를 도입해야할 것입니다.
SRE라고 하지 않아도 이미 이러한 대응을 하는 곳도 많을 것 같습니다.
네이버에서 SRE의 필요성을 느낀 사건은 이전에도 많았겠지만 ‘2016년 경주 지진’이라고합니다.
지진이 발생하자 많은 트래픽이 특정 시간대에 몰리면서 네이버 검색 시스템은 약 10분간 마비되었습니다.
이러한 비상상황에 대응할 수 있는 체계가 없었기 때문에 본격적으로 대응 체계를 만들기 시작했습니다. 
여러 예상되는 상황을 설정하고 대응 체계를 마련하며, 사후 비슷한 상황에 대한 대처를 할 수 있도록 기록합니다.&lt;br /&gt;
&lt;br /&gt;
SRE의 목적은 서비스 장애에 대응하고 이를 방지하는 것입니다. 
이러한 과정에서 소요되는 시간을 줄이기 위한 방법들에 대해서 발표해주었습니다. 
또하 그 과정에서 발생한 경보의 빈도, 거짓 경보 등에 대해서 이야기해주었고 이런 이슈들을 어떤 방식으로 해결했는지 알 수 있었습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;4-속도의-속도에-의한-속도를-위한-몽고db-네이버-컨텐츠검색과-몽고db&quot;&gt;4. 속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB)&lt;/h4&gt;

&lt;p&gt;다음은 &lt;span class=&quot;emphasis&quot;&gt;몽고DB&lt;/span&gt;에 대한 세션이었습니다. 주로 몽고DB의 성능개선에 대한 이야기들이었습니다.
저는 평소에 RDB를 많이 사용하며 개발중인 서비스 또한 RDB를 사용합니다. 
NoSQL은 사용해본적이 없고 이에대한 글과 이야기를 많이 접해서 궁금하여 한번 들어보았습니다. 
몽고DB는 문서(document)형 데이터베이스이며, &lt;span class=&quot;emphasis&quot;&gt;hBase&lt;/span&gt;에 대한 이야기도 많이 해주셨는데
hBase 같은 경우는 빅데이터 처리를 한다고 하면 누구나 한번쯤은 들어봤을 법한 DB입니다.&lt;/p&gt;

&lt;p&gt;세션에서는 &lt;span class=&quot;emphasis&quot;&gt;Index&lt;/span&gt;의 용도와 목적에 맞는 사용법을 설명해줍니다.
또 너무 큰 Document에 대해서는 Thread를 이용해 작성하여 Upsert 한다고합니다.&lt;br /&gt;
저 또한 콘텐츠를 다루는 서비스다보니 주의깊게 들을 수 있었습니다.&lt;br /&gt;
&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;* Upsert: 업데이트를 진행할 때, 만족하는 로우가 있다면 업데이트를 하고 없다면 INSERT 하는 것을 의미&lt;/span&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;emphasis&quot;&gt;Query Planner&lt;/span&gt;로 인해 발생한 이슈에 대해서 이야기해주었는데 재미있게 들었고, 
Query Planner라는 것에 대해 새롭게 알 수 있었고 한번쯤은 사용하는 DB의 Query Planner를 찾아보는 것도 재미있을 것 같습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;5-쿠팡-추천-시스템-2년간의-변천사-상품추천에서-실시간-개인화로&quot;&gt;5. 쿠팡 추천 시스템 2년간의 변천사 (상품추천에서 실시간 개인화로)&lt;/h4&gt;

&lt;p&gt;5번째 세션은 가장 재미있게 들었던 세션입니다. 쿠팡의 &lt;span class=&quot;emphasis&quot;&gt;추천 시스템&lt;/span&gt;에 대한 이야기로 
어떤 방향으로 추천 시스템을 개발해 왔는지에 대한 이야기를 해줍니다.
서비스가 조금씩 성장함에 따라 더 많은 유저를 유치하고 활동하게 하기 위해 추천 시스템은 필수라고 생각합니다. 
물론 쿠팡과 같이 e-commerce는 아니지만 쿠팡에서 다루는 상품(=아이템)에 어떤 것들을 대입해도 좋은 추천 시스템이 될 수 있을 것이라는 생각을 할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;쿠팡에서는 이러한 추천 시스템을 구축하기 위해 추천 모델과 서비스를 &lt;span class=&quot;emphasis&quot;&gt;분리&lt;/span&gt;합니다. 
이렇게 분리된 추천 모델은 서비스와 분리되어 있기 때문에 빠르게 개발 할 수 있으며, 다른 서비스에 적용하기도 수월합니다. 
쿠팡의 추천 모델이 아이템을 추천할 때는 &lt;span class=&quot;emphasis&quot;&gt;자연어 검색&lt;/span&gt;과 유사한 과정을 거칩니다.
쿼리를 통해 후보를 찾고 랭킹을 통해 아이템을 정렬합니다.
이후 실시간 로그를 관리하는 방식, 개인화 추천을 위해 사용되는 방식 등을 비교하여 잘 설명해주었습니다. 
쿠팡의 세션을 통해 추천 시스템에 대해 더욱 관심이 생겼습니다. 
특히 Item과 Item의 관계에서 Input과 Output을 통해 비유하여 설명해 주어 많은 영감을 얻을 수 있었던 것 같습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;6-스케일아웃없이-순간-급증하는-주문-처리하기-microservice-with-kafka&quot;&gt;6. 스케일아웃없이 순간 급증하는 주문 처리하기 (Microservice with Kafka)&lt;/h4&gt;

&lt;p&gt;마지막 세션으로 11번가에서 발표한 MSA로의 전환기 &amp;amp; Kafka 도입기에 대한 세션을 들었습니다. 
세션 초반에 11번가가 MSA로 전환되기 전 서비스의 상태에 대해서 이야기해 줄 때 모두들 재미있게 들었습니다. 
상품을 주문하기위한 대기열이 34913명이라고 표시되던 창을 없애고 싶었던 개발자 분들은 
&lt;span class=&quot;emphasis&quot;&gt;Kafka&lt;/span&gt; 기반의 &lt;span class=&quot;emphasis&quot;&gt;MSA&lt;/span&gt;를 모색합니다.
MSA의 게이트웨이를 이용하면서 만나게된 이슈들에 대해서 설명해 주었고 Kafka를 도입하게 된 계기, 도입하면서 생긴 이슈와 해결법들에 대해 공유해주었습니다. 
물론 서비스 별로 어울리는 방식이 다르겠지만 다른 서비스의 구조를 볼 수 있고 설명을 들을 수 있다는 것이 정말 뜻깊었습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;이번 DEVIEW 2019는 제게 좋은 동기부여가 되었습니다. 
항상 사용하는 기술만 사용하고, 매번 같은 서비스를 바라보니 생각이 이전보다 많이 닫혀있다는 느낌을 받았습니다. 
같은 방식으로 개발하기를 고수하고 해오던 방식과 사용하던 기술만 사용하려고했습니다. 
클라우드와 같은 많은 개발자들이 관심을 가질만한 기술들을 기웃기웃 해보기는 했지만 실제로 튜토리얼을 진행해보거나 
깊게 알아보려는 노력을 하지 않았던 것 같습니다.&lt;br /&gt;
&lt;br /&gt;
앞으로 많은 컨퍼런스를 다녀볼 예정이지만 컨퍼런스에 가기 전에 해당 기술에 대한 지식이 없다면 들어도 이해하기 힘들 것이라는 생각이 들었습니다. 
이번에는 운이 좋게도 컨퍼런스 전에 공부했던 내용들이 많이 나와서 이해하기 어려운 부분이 많지는 않았지만 
다른 컨퍼런스에서는 준비를 조금 해야겠다는 생각을 했습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;컨퍼런스에 대한 자세한 내용은 해당 홈페이지를 참고하세요!&lt;br /&gt;
&lt;strong&gt;&lt;a href=&quot;https://deview.kr/2019&quot; target=&quot;_blank&quot;&gt;DEVIEW 2019&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 31 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/31/deview2019.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/31/deview2019.html</guid>
        
        <category>네이버</category>
        
        <category>DEVIEW</category>
        
        <category>데뷰</category>
        
        <category>2019</category>
        
        <category>컨퍼런스</category>
        
        <category>후기</category>
        
        
      </item>
    
      <item>
        <title>디자인패턴_Singleton Pattern(싱글톤 패턴)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1gPylgzGIIEutY299CsVHF4lfl3PurlwX&quot; alt=&quot;python&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;디자인패턴&quot;&gt;디자인패턴&lt;/h4&gt;
&lt;p&gt;디자인 패턴이란 자주 나타나는 시스템 구조를 조금 더 쉽고 빠르게 설계하기위해
재이용하기 좋은 형태로 구조화한 것을 말합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;singleton-pattern싱글톤-패턴&quot;&gt;Singleton Pattern(싱글톤 패턴)&lt;/h4&gt;
&lt;p&gt;특정 클래스의 인스턴스가 하나만 만들어지고, 
어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.
객체 중에는 Manager나 사용자 설정같이 하나만 있어도 되거나 오직 하나만 있어야되는 것들이 있습니다.
이런 객체의 경우 인스턴스를 2개 이상 만들경우 에러나 자원의 낭비를 야기하기때문에&lt;br /&gt;
싱글톤 패턴을 사용합니다.
&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;일반적으로 클래스의 인스턴스는 여러가지가 생성될 수 있습니다.&lt;/span&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;싱글톤 패턴은 특정 클래스에 대해 객체 인스턴스가 하나만 만들어질 수 있도록 해 주는 패턴입니다. 
즉, 싱글톤 패턴의 핵심은 어떠한 상황에서든 해당 객체의 인스턴스는 하나만 존재해야 한다는 것입니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;# type을 상속받음
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;__instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;# class의 instance를 저장할 속성
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# class로 instance를 만들 때 호출
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# class로 instance를 생성하지 않았느지 확인
&lt;/span&gt;                                                &lt;span class=&quot;c1&quot;&gt;# 생성하지 않았으면 instance를 생성하여 속성에 저장
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;               &lt;span class=&quot;c1&quot;&gt;# class로 instance를 생성했으면 instance 반환
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metaclass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;      
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;                                    &lt;span class=&quot;c1&quot;&gt;# True : 인스턴스 a와 b는 같음
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;싱글톤 패턴을 만드는 여러가지 방법 중 Metaclass를 이용하는 방법이 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;Metaclass?&lt;/span&gt;&lt;br /&gt;
메타클래스(Metaclass)는 클래스를 만드는 클래스입니다.
파이썬에서는 클래스도 객체이며 클래스를 만드는 또 다른 클래스가 메타클래스입니다.&lt;br /&gt;
여기서는 간단히 설명하고 메타클래스에 대해서는 추후에 자세히 다루겠습니다. 
일반적으로는 자주 사용할 일이 없지만 Django 에서는 ORM을 다루는데 메타클래스가
꽤 중요한 역할을 해냅니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;먼저 type을 상속받은 메타클래스 Singleton을 만들고, 클래스 Hello를 만들 때 metaclass에 Singleton을 
지정합니다. 이렇게 하면 메타클래스 Singleton 이 클래스 Hello의 동작을 제어할 수 있습니다.&lt;/p&gt;

&lt;p&gt;보통 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt; 메서드는 인스턴스를 ()로 호출할 때 호출됩니다. 
하지만 메타클래스에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt; 메서드를 구현하면
메타클래스를 사용하는 클래스로 인스턴스를 만들 때 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt; 메서드가 호출됩니다.&lt;/p&gt;

&lt;p&gt;여기서는 Hello()로 인스턴스를 만들 때 Singleton의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt; 메서드가 호출됩니다. 
따라서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt; 안에서 중복을 확인하고, 중복이 없으면 인스턴스를 생성,
속성에 저장한 뒤 반환합니다. 만약 인스턴스가 생성되어 있다면 인스턴스를 더이상 생성하지 않고
바로 반환합니다.&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/28/singleton.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/28/singleton.html</guid>
        
        <category>디자인패턴</category>
        
        <category>싱글톤패턴</category>
        
        <category>SingletonPattern</category>
        
        <category>python</category>
        
        
      </item>
    
      <item>
        <title>디자인패턴_Facade(퍼사드)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1gPylgzGIIEutY299CsVHF4lfl3PurlwX&quot; alt=&quot;python&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;facade퍼사드&quot;&gt;Facade(퍼사드)&lt;/h4&gt;
&lt;p&gt;퍼사드 패턴은 클래스와 클래스 간의 관계가 복잡하여 동작 방식에 대해 이해하기 어려울 때
이를 단순하게 만들어주는 디자인 패턴입니다.
단순화된 인터페이스를 통해 서브 시스템을 더 쉽게 사용하기 위해서 사용합니다.
퍼사드 패턴의 장점은 클라이언트 구현과 서브시스템을 분리하여 사용한다는 점입니다.
예를들어 서브시스템 A, B, C 가 있을 때 A에 변경점이 생기더라도 퍼사드 패턴에서는 B,C를
변경해주지 않아도 됩니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1xAX6DNLx2D1ZhtIpSn9fpi9scXH-KmiD&quot; alt=&quot;facade&quot; /&gt;&lt;/p&gt;

&lt;p&gt;위의 구조처럼 사용자는 파사드 클래스를 통해 서브 시스템을 사용하며, 서브 시스템의 내부 구조에 대한
이해가 필요하지 않습니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Subsystem1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Subsystem2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Subsystem3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Facade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Subsystem1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Subsystem2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;three&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Subsystem3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Facade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Facade 클래스를 통해 Subsystem1,2,3을 사용합니다. 
패턴이라고 말하기 힘들정도로 간단하여 이해하기 쉬웠습니다! :)&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/28/facade.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/28/facade.html</guid>
        
        <category>퍼사드패턴</category>
        
        <category>Facade</category>
        
        <category>python</category>
        
        <category>디자인패턴</category>
        
        
      </item>
    
      <item>
        <title>도메인을 통해 웹사이트에 접속하는 과정</title>
        <description>&lt;p&gt;웹 서버에는 IP 주소가 있습니다. 
우리가 어떤 웹사이트에 접속한다는 것은 해당 웹사이트의 IP 주소로 접속하여
그 웹 서버가 제공하는 데이터를 보는 것입니다.
때문에 도메인을 이용하지 않고 IP 주소를 입력하는 것으로도 웹사이트에 접속이 가능합니다.
하지만 우리가 모든 웹사이트의 IP 주소를 외우거나 연필로 공책에 하나하나 적어놓고 주소록에서 전화번호를 
찾듯 웹사이트에 접속할 수 없기 때문에 &lt;span class=&quot;emphasis-org&quot;&gt;도메인&lt;/span&gt;을 이용합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;도메인이란&quot;&gt;도메인이란?&lt;/h4&gt;
&lt;p&gt;도메인은 네트워크상에서 컴퓨터를 식별하는 호스트명입니다.
지정된 IP조소를 알기쉬운 문자로 변환한 인터넷 상의 주소라고 할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;브라우저가-도메인에-해당하는-ip를-찾는-순서&quot;&gt;브라우저가 도메인에 해당하는 IP를 찾는 순서&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;Local Cache를 검색한다.&lt;/li&gt;
  &lt;li&gt;Hosts 파일을 검색한다.&lt;/li&gt;
  &lt;li&gt;도메인 네임서버를 검색한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Local Cache
우리가 한번이라도 어떤 사이트에 접속한다면 브라우저는 해당 도메인의 IP를 저장합니다.
도메인을 입력할 때마다 매번 IP를 검색한다면 비효율적이기때문에 브라우저에서 저장해놓고 
다음에 접속할 때 cache에서 바로 찾아서 접속하게 됩니다.
자동완성 기능이 작동한다면 cache에 저장이 되어있는 것이겠죠?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Hosts 파일
Local Cache에 도메인에 해당하는 IP가 없다면 Hosts 파일에서 찾습니다.
각 운영체제는 Hosts 파일을 갖고 있는데 이는 도메인과 해당 IP를 매핑합니다.
때문에 관리자권한으로 해당 파일을 수정한다면 도메인과 다른 IP주소로 접속이 가능합니다.
물론 자신의 컴퓨터에서만!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;도메인 네임서버를 검색
마지막 단계로 hosts 파일에도 도메인에 해당하는 IP 주소가 없을 때 도메인 네임서버(DNS)를 검색합니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1CUAPOGF1_9fleXNgUso93fUJrabFl_dn&quot; alt=&quot;DNS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;위의 그림을 보면 쉽게 이해를 할 수 있습니다. DNS 서버는 IP 주소와 도메인의 매핑 정보를 관리합니다.
때문에 우리가 DNS 서버에 도메인에 해당하는 IP 주소를 묻는 요청에 응답해줍니다.
게다가 DNS에도 캐시가 있어 자주 요청 받는 정보는 캐시로 관리합니다.&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/28/domain_process.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/28/domain_process.html</guid>
        
        <category>네트워크</category>
        
        <category>도메인</category>
        
        <category>domain</category>
        
        <category>웹사이트</category>
        
        <category>DNS</category>
        
        
      </item>
    
      <item>
        <title>덱(Deque)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1-331-WItPcbNuWKl34MzUECc9edYGgIu&quot; alt=&quot;data_structure&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;덱deque&quot;&gt;덱(Deque)&lt;/h4&gt;

&lt;p&gt;덱은 Double-Ended Queue의 줄임말입니다. 
큐의 앞과 뒤에서 입력과 삭제가 모두 가능한 큐를 말합니다.&lt;br /&gt;
큐의 확장형이라는 느낌을 받을 수 있지만 스택의 성격도 갖고 있습니다. 
스택과 큐는 구조의 한계를 갖고 있기 때문에 그에 맞는 용도가 있었습니다.
하지만 덱의 경우 여러가지 용도에 모두 사용이 가능합니다.&lt;/p&gt;

&lt;p&gt;덱은 보통 이중 연결 리스트로 구현됩니다.&lt;br /&gt;
그 이유는 큐의 앞과 뒤에서 데이터의 입력과 삭제가 모두 이루어질 수 있어야하기 때문입니다.&lt;br /&gt;
덱의 앞을 가르키는 주소로 &lt;span class=&quot;emphasis&quot;&gt;head&lt;/span&gt;와 뒤를 가르키는 주소로 &lt;span class=&quot;emphasis&quot;&gt;tail&lt;/span&gt;을 사용합니다.&lt;/p&gt;

&lt;p&gt;덱은 보통 &lt;span class=&quot;emphasis&quot;&gt;스케줄링&lt;/span&gt;에 많이 사용되는데 우선순위를 조절하게 될 때 유용합니다. 
예를들어 먼저 있던 데이터에 우선순위를 두고 싶다면 앞에서 데이터를 빼내야하는데 이는 스택에서
불가능하고 최근에 들어온 데이터에 우선순위를 두고 싶다면 큐에서 불가능합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;add_front, delete_front&lt;/strong&gt; 연산은 스택의 &lt;strong&gt;push, pop&lt;/strong&gt;과 같은 연산이고
&lt;strong&gt;add_rear, delete_front&lt;/strong&gt; 연산은 큐의 &lt;strong&gt;enqueue, dequeue&lt;/strong&gt; 연산과 같습니다.
추가로 덱은 &lt;strong&gt;get_front, get_rear, delete_rear&lt;/strong&gt;를 가지고 있습니다.&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/28/deque.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/28/deque.html</guid>
        
        <category>자료구조</category>
        
        <category>덱</category>
        
        <category>deque</category>
        
        <category>stack</category>
        
        <category>스택</category>
        
        <category>que</category>
        
        <category>큐</category>
        
        
      </item>
    
      <item>
        <title>쓰레드(Thread)</title>
        <description>&lt;h4 id=&quot;쓰레드thread란&quot;&gt;쓰레드(Thread)란?&lt;/h4&gt;

&lt;p&gt;쓰레드는 CPU를 구성하는 기본단위입니다.
Thread ID, Program counter, register set 그리고 stack으로 구성되어 있습니다. 작업이 겹치는 프로세스를 여러 개 생성하는 것은 메모리의 낭비입니다. 
그래서 프로세스는 하나만 생성하고 PC(Program Counter)만 여러개 생성합니다. 즉 CPU 수행 단위를 여러 개 두는 것입니다. 이를 쓰레드라고 합니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1N4g-xpBs4d9Y_PzG1ZvX460QJqYKflSY&quot; alt=&quot;process_and_thread&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;각각 다른 작업을 수행하려면 앞의 포스팅에서 말씀드린 것처럼 PC 값이 꼭 필요합니다.
또 메모리에 어떤 레지스터 값들을 세팅해야합니다. 때문에 쓰레드마다 PC 값과 레지스터 값들이
들어가는 것입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;쓰레드를-이용해-프로세스를-수행했을-때의-장점&quot;&gt;쓰레드를 이용해 프로세스를 수행했을 때의 장점&lt;/h4&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h5 id=&quot;1-빠른-반응&quot;&gt;1. 빠른 반응&lt;/h5&gt;
&lt;p&gt;쓰레드를 이용하면 하나의 서버쓰레드가 blocked 상태인 동안에도 동일한 프로세스의 다른 쓰레드가
실행되어 보다 빠르게 처리가 가능합니다. 예를들어 웹 브라우저가 웹 페이지를 읽어올 때 작업이 오래
걸리는 경우 웹 브라우저 상태는 blocked 상태가 됩니다. 하지만 다른 쓰레드들은 읽어온 데이터를 
화면에 뿌려주는 역할을 할 수 있어 보다 사용자에게 빠르게 웹 페이지가 보여지게 됩니다.&lt;/p&gt;

&lt;h5 id=&quot;2-자원의-공유&quot;&gt;2. 자원의 공유&lt;/h5&gt;
&lt;p&gt;쓰레드들은 서로 자원을 공유합니다. 쓰레드의 자원 공유에 대해 이야기 하기에 앞서 
앞에서 설명을 못드린 프로세스의 자원 공유 방법에 대해 이야기해보겠습니다.
프로세스 간에도 자원을 공유합니다.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;Shared Memory&lt;/span&gt;&lt;/small&gt;&lt;br /&gt;
&lt;small&gt;상호 작용하는 프로세스 사이에 하나의 저장 공간을 만들어 정보를 공유합니다. 
이는 프로세스끼리 동시에 메모리에 접근을 하게 되면 충돌(동시에 값을 수정) 하여 데이터의 
불일치를 초래합니다.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;Message Passing&lt;/span&gt;&lt;/small&gt;&lt;br /&gt;
&lt;small&gt;Message Passing은 상호 작용하는 프로세스 간 작업이 필요한 경우 운영체제에게 각자의 Message를
전달함으로써 통신합니다. 이 방식은 운영체제를 거쳐야 하기 때문에 Shared Memory에 비해
속도가 느립니다.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;프로세스의 자원 공유 방법에 대해서 알아봤습니다. 이제 쓰레드에 대해 이야기해보겠습니다. 
쓰레드는 앞에서 말했듯이 자원을 공유합니다. 때문에 프로세스와 같이 메모리 공간을 따로 만들 필요도,
운영체제를 거칠 필요도 없습니다. 
이러한 특징은 Multi-Tread가 Multi-Process 보다 좋은 점이라고 할 수 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;자원의 공유로 인해 문맥 교환의 시간과 Process에 메모리와 자원을 할당하는 것이
    더욱 효율적으로 이루어집니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Multi-CPU에서 Multi-Thread 기법을 통해 확장성을 가질 수 있습니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;multi-thread-models&quot;&gt;Multi-Thread Models&lt;/h4&gt;

&lt;p&gt;Multi-Thread의 Model을 살펴보기 전에 &lt;span class=&quot;emphasis-org&quot;&gt;User Thread&lt;/span&gt;와 &lt;span class=&quot;emphasis-org&quot;&gt;Kernel Thread&lt;/span&gt;에 관해 이야기해보겠습니다.
&lt;span class=&quot;emphasis-org&quot;&gt;User Thread&lt;/span&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#include&amp;lt;thread&amp;gt;&lt;/code&gt; 혹은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt;를 통해 쓰레드를 이용합니다. 커널의 지원 없이
사용자 공간에서 실행되므고 빠른 쓰레드 연산이 가능합니다.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;Kernel Thread&lt;/span&gt;는 커널 내에 있는 쓰레드를 의미합니다. 운영체제가 직접 제공하고 관리하는 쓰레드입니다.
커널이 쓰레드를 인식하고 프로세스와 유사하게 스케줄링 합니다. 때문에 커널의 오버헤드가 더해져
쓰레드 생성과 스케줄링이 느릴 수 있습니다.&lt;/p&gt;

&lt;p&gt;Multi-Thread Models에는 Many-to-One model, One-to-One model, Many-to-Many model, Two-Level model이 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1bM3pq3CzoCIOc1uwqrQuA1QKLbd9dtr4&quot; alt=&quot;many_to_one_model&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;먼저 &lt;span class=&quot;emphasis-org&quot;&gt;Many-to-One model&lt;/span&gt;로 하나의 Kernel Thread가 다수의 User Thread를 처리하는 구조입니다.
이러한 모델은 User Thread 모델이며 모든 쓰레드 제어가 사용자 수준의 라이브러리에서 지원됩니다.
이러한 구조는 User Thread를 처리하던 중 System Call에 의해 blocking이 된다면 전체 프로세스가 막히는
병목현상이 일어나는 문제점을 갖습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1m833gUEgaHaQO1e8BiN5LS7IW04dc0Rj&quot; alt=&quot;one_to_one_model&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;다음으로 &lt;span class=&quot;emphasis-org&quot;&gt;One-to-One model&lt;/span&gt; 입니다. 처리해야 할 User Thread 한 개당 Kernel Thread를 
대응시켜 작업을 진행하는 구조입니다. 이러한 일대일 대응 구조는 Kernel Thread 생성에
과도한 생성의 문제를 가져오게 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1g8Y4NlrfwEzDwobSFwAjU2PuZqVl9yMM&quot; alt=&quot;many_to_many_model&quot; width=&quot;50%&quot; height=&quot;50%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;앞의 두 모델을 보완하여 합친 모델이 &lt;span class=&quot;emphasis-org&quot;&gt;Many-to-Many model&lt;/span&gt;입니다. 
다수의 User Thread를 다수의 Kernel Thread가 처리하는 구조입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1WHmBOhsZlAcSBFKdmMf6iijr1Xn5xUqV&quot; alt=&quot;two_level_model&quot; width=&quot;50%&quot; height=&quot;50%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;마지막으로 Many-to-Many model을 더욱 보완한 &lt;span class=&quot;emphasis-org&quot;&gt;Two-level model&lt;/span&gt; 입니다. 
Many-to-Many model과 One-to-One model을 합친 구조로 중요한 작업은 One-to-One 구조를
통해 처리하고 나머지는 Many-to-Many 구조를 통해 처리합니다.
이렇게 하면 중요한 작업에서의 기다림 현상을 줄일 수 있습니다.&lt;/p&gt;
</description>
        <pubDate>Fri, 25 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/25/thread.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/25/thread.html</guid>
        
        <category>운영체제</category>
        
        <category>os</category>
        
        <category>operating system</category>
        
        <category>리눅스</category>
        
        <category>linux</category>
        
        <category>process</category>
        
        <category>프로세스</category>
        
        <category>thread</category>
        
        <category>스레드</category>
        
        
      </item>
    
      <item>
        <title>마이크로 서비스 아키텍처(MicroService Architecture; MSA)</title>
        <description>&lt;p&gt;주변에서 마이크로서비스 아키텍처(Microservices architecture; MSA)에 대한 이야기가 많이 들려옵니다. 마이크로서비스 역시 장단점이 있는 하나의 기술입니다. 마이크로서비스를 사용하면 서비스를 분리하여 관리하게되며 애자일한 개발 환경과 점점 더 복잡해지는 애플리케이션에서 분명한 이점을 갖습니다. 동시에, 서비스를 분리하면서 생기는 단점도 존재합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;모노리틱-아키텍처monolithic-architecture&quot;&gt;모노리틱 아키텍처(Monolithic Architecture)&lt;/h4&gt;

&lt;p&gt;MSA를 잘 이해하기 위해서는 먼저, 반대되는 개념인 모노리틱(Monolithic) 아키텍처에 대해서 살펴볼 필요가 있습니다.
모노리틱 아키텍처는 단일 시스템으로 기존의 전통적인 웹 시스템 개발 스타일 입니다. 쉽게 말해 하나의 애플리케이션 내에 모든 로직들이 모두 들어 가 있는 구조입니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1ghtNr5qS7rDUrpmFKNeHSPASZY7mzDIL&quot; alt=&quot;monolithic_architecture&quot; width=&quot;50%&quot; height=&quot;50%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이러한 애플리케이션 구조는 실제로 많은 서비스에서 사용하고 있습니다. 단순한 구조의 애플리케이션은 로컬 환경에서 개발하기에도 편리하고 전체 애플리케이션을 하나로 처리하기 때문에 배포 역시 간편하며 테스트도 하나의 애플리케이션만 수행하면 되기 때문에 편리합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;그러나-이러한-모노리틱-아키텍처-시스템은-대형-시스템-개발시-몇가지-문제점을-갖습니다&quot;&gt;그러나 이러한 모노리틱 아키텍처 시스템은 대형 시스템 개발시 몇가지 문제점을 갖습니다.&lt;/h4&gt;

&lt;p&gt;서비스의 복잡도가 증가되면 아주 간단한 기능을 하나 추가하기 위해서도 매우 많은 줄의 코드를 수정해야합니다. 또한 크기가 크기 때문에, &lt;span class=&quot;emphasis&quot;&gt;빌드 및 배포 시간&lt;/span&gt;, &lt;span class=&quot;emphasis&quot;&gt;서버의 기동 시간&lt;/span&gt;이 오래 걸리며 프로젝트를 진행하는 과정에서 한사람의 실수는 전체 시스템의 &lt;span class=&quot;emphasis&quot;&gt;빌드 실패를 유발&lt;/span&gt;할 수 있습니다. 전체 시스템의 구조를 제대로 파악하지 않고 개발을 진행하게 되면, 특정 컴포넌트나 모듈에서의 성능 문제나 장애가 &lt;span class=&quot;emphasis&quot;&gt;다른 컴포넌트에까지 영향&lt;/span&gt;을 주게 됩니다. 또한, 컴포넌트별로 다른 기술을 도입하고자할 때 &lt;span class=&quot;emphasis&quot;&gt;유연하게 대응&lt;/span&gt;하지 못한다는 단점 등 여러가지 단점이 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;마이크로서비스-아키텍처microservices-architecture-msa&quot;&gt;마이크로서비스 아키텍처(Microservices architecture; MSA)&lt;/h4&gt;

&lt;p&gt;모노리틱 아키텍처의 대부분의 단점은 애플리케이션의 규모가 커지면서 발생합니다. 애플리케이션의 &lt;span class=&quot;emphasis&quot;&gt;규모가 커지고 구조가 복잡&lt;/span&gt;해질 때 고려해볼 수 있는 아키텍처가 있습니다. 
마이크로서비스 아키텍처(Microservices architecture; MSA)는 하나의 큰 애플리케이션을 여러개의 작은 애플리케이션으로 쪼개어 변경과 조합이 가능하도록 만든 아키텍처입니다.
기존의 통합 관점의 단일 시스템(Monolitic)을 서비스 단위로 나누고 분리하는 개념의 아키텍처라고 할 수 있습니다.
마이크로서비스 아키텍처는 기존에 없다가 갑자기 등장한 개념은 아닙니다. 기존의 CBD, SOA로부터 이어온 개념이로 모든 기술들이 그렇듯이 개념에 새로운 기술요소들이 융합되어 현재 우리가 가지고 있는 문제점들을 해결하기 위한 방법으로 등장하고 있습니다.
각 서비스 단위로 나눈다는 의미는 ‘사용자 관리’, ‘주문 관리’ 등 기능 단위로 서비스를 나누며 각 서비스는 모노리틱 아키텍처와 유사한 구조를 가집니다.
다만 하나의 서비스에서 처리하는 규모가 작기때문에 이를 마이크로서비스라고 부릅니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;구조&quot;&gt;구조&lt;/h4&gt;

&lt;p&gt;마이크로 서비스 아키텍처의 구조는 다음과 같은 모양을 따릅니다.&lt;br /&gt;
각 컴포넌트는 서비스 형태로 구현되고 API를 이용하여 타 서비스와 통신합니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1TILobJ3cA2Oh2FOl5G56L4lI8kGxmwdv&quot; alt=&quot;msa구조&quot; width=&quot;70%&quot; height=&quot;70%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;마이크로 서비스 아키텍처에서는 &lt;span class=&quot;emphasis&quot;&gt;데이터 베이스&lt;/span&gt; 또한 각 서비스 별로 나뉩니다. 이는 데이터의 트랜잭션 관리나 정규화 등의 관점에서 매우 비효율적으로 보일 수 있습니다. 
데이터 베이스도 각자 만들어진 목적이 있으며 그 목적에 맞게 사용하는 것이 가장 효율적인 방법입니다. 물론, 하나의 데이터베이스를 각각의 개별 서비스가 공유해서 사용하는 방식도 가능하지만 마이크로서비스 아키텍처가 가지는 근본적인 장점을 최대한 활용하기 위해서는 이렇게 서비스별로 별도의 데이터베이스를 사용하는 것이 필요합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;api-gateway&quot;&gt;API Gateway&lt;/h4&gt;

&lt;p&gt;모노리틱 아키텍처의 경우 실제 요청이 도착하여 결과를 반환하기까지 서버는 다양한 종류의 쿼리를 데이터베이스에 보내야합니다. 
이러한 방식은 애플리케이션 구조가 간단하다는 장점이 있지만 특정 기능에 변경이 있을 경우 해당 기능을 포함하고 있는 모든 코드를 수정해야한다는 단점이 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1OD9B_oeqsDa8O_IyD_hMbisvAR3DomqU&quot; alt=&quot;api_gateway&quot; width=&quot;70%&quot; height=&quot;70%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;API Gateway는 그 이름에서도 유추할 수 있듯이 서비스로 전달되는 모든 &lt;span class=&quot;emphasis-org&quot;&gt;API 요청의 관문(Gateway)&lt;/span&gt; 역할을 하는 서버입니다.
API Gateway는 클라이언트의 요청을 일괄적으로 처리하는 역할 뿐만 아니라, 전체 시스템의 부하를 분산시키는 &lt;span class=&quot;emphasis&quot;&gt;로드 밸런서&lt;/span&gt;의 역할, 동일한 요청에 대한 불필요한 반복작업을 줄일 수 있는 &lt;span class=&quot;emphasis&quot;&gt;캐싱&lt;/span&gt;, 시스템상을 오고가는 요청과 응답에 대한 &lt;span class=&quot;emphasis&quot;&gt;모니터링&lt;/span&gt; 역할도 수행할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;로드 벨런싱(Load Balancing)&lt;/span&gt;&lt;/small&gt;&lt;br /&gt;
&lt;small&gt;하나의 인터넷 서비스가 발생하는 트래픽이 많을 때 여러 대의 서버가 분산처리하여 서버의 로드율 증가, 부하량, 속도저하 등을 고려하여 적절히 분산처리하여 해결해주는 서비스&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;span class=&quot;emphasis-org&quot;&gt;로드 벨런서(Load Balancer)&lt;/span&gt;&lt;/small&gt;&lt;br /&gt;
&lt;small&gt;스위치라는 용어도 많이 쓴다. 허브가 한 포트로 신호가 들어오면 같은 신호를 다른 모든 포트로 전달하는 것에 비해, 스위치는 신호를 필요로하는 포트로만 신호를 전달. 따라서 스위치에서는 불필요한 트래픽이 감소, 이는 곧 네트워크에서의 데이터 전송 속도의 향상으로 이어진다. 동의어는 아니지만 동의어처럼 많이들 쓰고 있다.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;이렇게 API Gateway를 이용하여 서비스 요청에 대한 처리를 하게되면 특정 서비스의 변경사항이 생기거나 서비스가 통합/분리 되더라도 클라이언트는 그 사실을 인지할 필요가 없으며 API Gateway 내부의 변경사항만으로 처리가 가능하게 됩니다.
이렇게 시스템 내부의 아키텍처를 숨길 수 있는(encapsulate) 특성이 API Gateway가 갖는 가장 큰 장점이라고 할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;orchestration&quot;&gt;Orchestration&lt;/h4&gt;

&lt;p&gt;기존 open api의 mash up과 같은 개념으로, &lt;span class=&quot;emphasis&quot;&gt;여러개의 서비스를 묶어서 하나의 새로운 서비스를 만드는 개념이다.&lt;/span&gt;
예를 들어, 포인트 적립과, 물품 구매라는 서비스가 있을때, 이 두개의 서비스를 묶어서 “물품 구입시 포인트 적립”이라는 새로운 서비스를 만들어 낼 수 있다. 이러한 orchestration 기능은, api gateway를 통해서 구현될 수 있다.
하지만 &lt;span class=&quot;emphasis&quot;&gt;과도한 orchestration 로직은 전체적인 성능 문제&lt;/span&gt;를 유발합니다. 때문에 orchestration 서비스의 활용은 MSA에 대한 높은 이해와 api gateway 자체에 대한 높은 수준의 기술적인 이해를 필요로 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;공통-기능-처리-cross-cutting-function-handling&quot;&gt;공통 기능 처리 (Cross cutting function handling)&lt;/h4&gt;

&lt;p&gt;또한 API에 대한 인증 (Authentication)이나, Logging과 같은 &lt;span class=&quot;emphasis&quot;&gt;공통 기능&lt;/span&gt;에 대해서 서비스 컴포넌트 별로 중복 개발해야 하는 비효율성을 유발할 수 있다. 
api gateway에서 이러한 공통 기능을 처리하기 되면, api 자체는 비지니스 로직에만 집중을 하여 개발에 있어서의 &lt;span class=&quot;emphasis&quot;&gt;중복등을 방지&lt;/span&gt; 할 수 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;conways-law-컨웨이의-법칙&quot;&gt;Conway’s Law (컨웨이의 법칙)&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;“소프트웨어의 구조는 그 소프트웨어를 만드는 조직의 구조와 일치한다”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;마이크로서비스 아키텍처는 이 컨웨이 법칙에 근간을 두고 있습니다. 이는 각 컴포넌트를 팀에 배치해서 책임지고 개발하는 것을 근간으로 하며, 팀간의 의존성을 제거해서 각 팀이 컴포넌트 개발을 독립적으로할 수 있는 구조로 잡혀있습니다.&lt;/p&gt;
</description>
        <pubDate>Fri, 25 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/25/msa.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/25/msa.html</guid>
        
        <category>msa</category>
        
        <category>architecture</category>
        
        <category>마이크로서비스</category>
        
        
      </item>
    
      <item>
        <title>프로세스(Process)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=14deQUE0ykyNyS2jnnmzTbvdZaldeZomb&quot; alt=&quot;운영체제&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;프로세스process란&quot;&gt;프로세스(Process)란?&lt;/h4&gt;
&lt;p&gt;&lt;span class=&quot;emphasis&quot;&gt;프로세스는 실행 중인 프로그램을 말합니다.&lt;/span&gt;&lt;br /&gt;
프로그램과 프로세스를 구분할 줄 알아야합니다. 이 둘의 차이는 정말 명확합니다.&lt;br /&gt;
프로그램은 보조 기억장치&lt;small&gt;(하드디스크, SSD)&lt;/small&gt;에 존재하며 실행되기를 기다리는 명령어와 정적인 데이터의 묶음입니다.&lt;br /&gt;
이 프로그램의 명령어와 정적 데이터가 메인 메모리에 적재되면 프로세스가 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;프로세스-상태&quot;&gt;프로세스 상태&lt;/h4&gt;
&lt;p&gt;프로세스는 다섯 가지의 상태 중 하나의 상태로 존재합니다.&lt;br /&gt;
프로세스는 이러한 상태가 변경되면서 수행되는 것입니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1-Ofqhg-ITSfyI2vRbMmwz6qHTbnEMEBJ&quot; alt=&quot;process&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;프로세스-문맥&quot;&gt;프로세스 문맥&lt;/h4&gt;
&lt;p&gt;현재 컴퓨터는 하나의 CPU가 여러 프로세스를 동시에 수행합니다.&lt;br /&gt;
빠른 속도로 여러 개의 프로세스를 번갈아가며 수행하는 CPU의 입장에서 이전에 수행하고 있던 프로세스가 어디까지 진행되었는지 알아야 합니다.
이러한 부분 외에도 여러 데이터를 프로세스 문맥에서 저장하고 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;CPU의 수행상태&lt;br /&gt;
PC(Program Counter)는 다음에 수행할 명령어의 위치에 대한 주소를 담고 있습니다.
CPU가 할당되었을 때 PC가 가리키는 부분부터 수행해나갑니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;프로세스의 주소 공간&lt;br /&gt;
code, data, stack 각각의 공간에 어떤 값이 들어있는가를 나타내는 주소값을 포함합니다.
현재 변수의 값, 메모리에 담긴 내용, 스택에 어떤 내용이 쌓여있는가 등에 대한 정보입니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;프로세스 관련 커널 자료구조&lt;br /&gt;
프로세스가 시작될 때, 운영체제는 그 프로세스를 관리하기 위해 PCB를 생성합니다.&lt;br /&gt;
Kernel stack은 주소 공간에서 data에 해당하는 부분에 PCB를 저장합니다.&lt;br /&gt;
시스템 콜이 발생했을 때, 운영체제는 어떤 프로세스가 시스템 콜을 했는지 알기 위해 주소공간의 stack에 시스템 콜을 한 프로세스의 Kernel stack 값을 저장합니다.&lt;br /&gt;
&lt;span class=&quot;emphasis-org&quot;&gt;PCB(Process Control Block)?&lt;/span&gt;&lt;/p&gt;
    &lt;h6 id=&quot;프로세스의-정보를-저장하는-블록&quot;&gt;프로세스의 정보를 저장하는 블록&lt;/h6&gt;
    &lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;Kernel stack?&lt;/span&gt;&lt;/p&gt;
    &lt;h6 id=&quot;pcb가-저정되는-공간&quot;&gt;PCB가 저정되는 공간&lt;/h6&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;문맥-교환-context-switch&quot;&gt;문맥 교환 (Context Switch)&lt;/h4&gt;
&lt;p&gt;문맥 교환이란 프로세스 간 CPU 점유가 오가가는 것을 말합니다.
이때, CPU는 이전 프로세스의 상태&lt;span class=&quot;emphasis&quot;&gt;(PCB)&lt;/span&gt;를 저장하고, 다음 프로세스의 상태&lt;span class=&quot;emphasis&quot;&gt;(PCB)&lt;/span&gt;를 읽어와 동작합니다.
시스템 콜이나 인터럽트가 발생했다고 반드시 문맥 교환이 일어나는 것은 아닙니다. 
다른 프로세스로 넘어가는 과정에 cache memory에 진행하던 프로세스에 대한 cache를
모두 비워줘야하기 때문에 상당한 오버헤드를 발생시킵니다.
때문에 문맥 교환이 일어나는 상황은 &lt;span class=&quot;emphasis&quot;&gt;timer Interrupt, I/O 요청 시스템 콜&lt;/span&gt; 요청이 왔을 때 입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;프로세스를-스케줄링하기-위한-큐queue&quot;&gt;프로세스를 스케줄링하기 위한 큐(Queue)&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Job queue&lt;br /&gt;
현재 시스템 내에 있는 모든 프로세스가 담겨있는 공간&lt;/li&gt;
  &lt;li&gt;Ready queue&lt;br /&gt;
현재 메모리 내에 있으면서 CPU를 점유하기위해 기다리는 프로세스가 담겨있는 공간&lt;/li&gt;
  &lt;li&gt;Device queue&lt;br /&gt;
I/O device의 처리를 기다리는 프로세스가 담겨있는 공간&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;스케줄러scheduler&quot;&gt;스케줄러(Scheduler)&lt;/h4&gt;
&lt;p&gt;스케줄은 각각의 자원(CPU, Memory 등)을 어떻게 우선순위를 정할지 나타낼때 사용합니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;장기스케줄러(Long-term scheduler/job scheduler)&lt;br /&gt;
프로세스에 &lt;span class=&quot;emphasis&quot;&gt;메모리&lt;/span&gt;를 주는 문제를 스케줄링합니다.
시작 프로세스 중 어떤 것들을 Ready queue로 보낼지 결정합니다.
현재에는 곧바로 메모리에 올라가 Ready 상태가 되기때문에 사용되지 않습니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;단기스케줄러(Short-term scheduler/CPU scheduler)&lt;br /&gt;
프로세스에 &lt;span class=&quot;emphasis&quot;&gt;CPU&lt;/span&gt;를 점유시키는 문제를 스케줄링 합니다.
어떤 프로세스(Ready)를 다음번에 Running 시킬지 결정합니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;중기스케줄러(Medium-term scheduler/Swapper)&lt;br /&gt;
여유공간 마련을 위해 프로세스에게서 &lt;span class=&quot;emphasis&quot;&gt;Memory를 뺏는&lt;/span&gt; 문제를 스케줄링 합니다.
메모리에 너무 많은 프로세스가 올라와 있을 때, 사용자가 프로그램을 일시 정지시킨 경우
&lt;span class=&quot;emphasis&quot;&gt;Suspended(Stoped)&lt;/span&gt;상태가 됩니다. 외부적인 이유로 프로세스의 수행이 &lt;span class=&quot;emphasis&quot;&gt;정지&lt;/span&gt;된 상태를 말합니다.
Blocked 상태는 스스로 Ready로 돌아갈 수 있지만 Suspended 상태는 스스로 돌아갈 수 없습니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 08 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/10/08/process.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/10/08/process.html</guid>
        
        <category>운영체제</category>
        
        <category>os</category>
        
        <category>operating system</category>
        
        <category>리눅스</category>
        
        <category>linux</category>
        
        <category>process</category>
        
        <category>프로세스</category>
        
        <category>thread</category>
        
        <category>스레드</category>
        
        
      </item>
    
      <item>
        <title>OSI 7 계층</title>
        <description>&lt;h3 id=&quot;osi-7계층&quot;&gt;OSI 7계층&lt;/h3&gt;
&lt;p&gt;OSI 7계층의 뜻은 Open System Interconnection입니다. 
문자 그대로 해석해보면 ‘시스템의 상호간 연결을 위한 열린(표준) 시스템 모델’ 입니다. 즉, 다수의 시스템간의 연결을 위한 표준 모델입니다. 
서로 연결 하기 위해 각자가 보내고싶은대로 데이터를 보내는 것이 아니라 서로간의 약속된 모델로 주고 받는 것입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;구성&quot;&gt;구성&lt;/h3&gt;
&lt;p&gt;OSI 7계층의 구성은 다음과 같습니다.
&lt;br /&gt;
&lt;img src=&quot;https://drive.google.com/uc?id=1Z7Ih6-zXP7eoCwv3yy8PABnU1bexRBrZ&quot; alt=&quot;osi7_structure&quot; /&gt;
&lt;br /&gt;
통상적으로 응용계층을 상위, 물리 계층을 하위 계층으로 말합니다.&lt;br /&gt;
상위 계층으로 갈수록 유저 친화적이며, 하위 계층으로 갈수록 기계 친화적이라고 할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l1-물리계층physical-layer&quot;&gt;L1 물리계층(Physical Layer)&lt;/h4&gt;
&lt;p&gt;말 그대로 물리적인 부분을 담당하는 계층입니다.&lt;br /&gt;
데이터 전송에 필요한 물리적인 구성들을 포함합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l2-데이터-링크-계층data-link-layer&quot;&gt;L2 데이터 링크 계층(Data Link Layer)&lt;/h4&gt;
&lt;p&gt;데이터 링크 계층은 점 대 점간 신뢰성 있는 전송을 보장하기 위한 계층입니다.&lt;br /&gt;
네트워크 위의 개체들 간 데이터를 전달하고, 물리 계층에서 발생할 수 있는 오류를 찾아내고 수정하는 데 
필요한 기능적, 절차적 수단을 제공합니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;흐름제어&lt;br /&gt;
전송하려는 데이터를 프레임 단위로 쪼개는 행동. 데이터를 쪼개어 송/수신하며, 
수신시에 Ack, Nack 패킷을 보냄.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;에러제어&lt;br /&gt;
에러를 어떻게 발견하고, 발견 시 어떤 행동을 취할것인가.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;다중 접속 제어&lt;br /&gt;
통신이 항상 1:1 통신이라는 법은 없기때문에 다중 접속시 어떻게 교통을 관리할 것인지 제어.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;주소 값은 물리적으로 할당 받는데, 이는 네트워크 카드가
만들어질 때부터 &lt;span class=&quot;emphasis&quot;&gt;맥(MAC) 주소(물리적 주소)&lt;/span&gt;가 정해져 있다는 뜻입니다.
네트워크 브릿지나 &lt;span class=&quot;emphasis&quot;&gt;스위치&lt;/span&gt; 등의 장비가 
이 계층에서 동작하며, 직접 이어진 곳에만 연결할 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l3-네트워크-계층network-layer&quot;&gt;L3 네트워크 계층(Network Layer)&lt;/h4&gt;
&lt;p&gt;데이터 링크 계층이 물리적인 네트워크 주소값을 할당 받는 계층이라면, 
네트워크 계층은 논리적인 네트워크 주소값을 할당받는 계층입니다. &lt;span class=&quot;emphasis&quot;&gt;(IP Address)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;네트워크 계층은 여러개의 노드를 거칠때마다 경로를 찾아주는 역할을 하는 계층으로 다양한 길이의 
데이터를 네트워크들을 통해 전달하고, 그 과정에서 전송 계층이 요구하는 서비스 품질을 제공하기 위한 
기능적, 절차적 수단을 제공합니다. 라우터가 이 계층에서 동작합니다.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;emphasis-org&quot;&gt;* 라우터?&lt;/span&gt;&lt;/p&gt;
&lt;h6 id=&quot;네트워크와-네트워크-간의-경로route를-설정하고-가장-빠른-길로-트래픽을-이끌어주는-네트워크-장비&quot;&gt;네트워크와 네트워크 간의 경로(Route)를 설정하고 가장 빠른 길로 트래픽을 이끌어주는 네트워크 장비&lt;/h6&gt;

&lt;p&gt;개방형 시스템들의 사이에서 네트워크 연결을 설정, 유지, 해제하는 기능을 부여하고, 전송 계층 사이에 
네트워크 서비스 데이터 유닛을 교환하는 기능을 제공합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l4-전송-계층transport&quot;&gt;L4 전송 계층(Transport)&lt;/h4&gt;
&lt;p&gt;데이터를 전송하는 측과 수신하는 측과의 관계에서 서로간의 신뢰있는 데이터를 전송할 수 있도록 합니다. 
특정 연결의 유효성을 제어하고, 일부 프로토콜은 상태 개념이 있고, 연결 기반입니다. 이는 전송 계층이 
패킷들의 전송이 유효한지 확인하고 전송에 실패한 패킷들을 다시 전송하여 재 전송을 요구합니다. 
가장 잘 알려진 전송 계층은 &lt;span class=&quot;emphasis&quot;&gt;TCP, UDP&lt;/span&gt;입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l5-세션-계층session-layer&quot;&gt;L5 세션 계층(Session Layer)&lt;/h4&gt;
&lt;p&gt;이번 계층부터 유저 친화적인 애플리케이션 계층과 관련이 있습니다. 
지금까지 하위 4계층은 데이터의 전송/수신, 오류검출 등의 데이터의 교류에 관한 것이었다면, 
세션계층부터는 응용 계층까지 전달될 데이터를 재구성하거나, 상위 계층으로부터 내려온 데이터를 
검사하고 재 요청하는 역할을 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l6-표현-계층presentation-layer&quot;&gt;L6 표현 계층(Presentation Layer)&lt;/h4&gt;
&lt;p&gt;세션 계층으로부터 받은 데이터를 응용계층이 이해할 수 있도록 변환합니다. 
응용 계층으로부터 받은 데이터를 세션 계층이 이해할 수 있도록 변환해주기도 합니다. 
한마디로 코드 간의 번역을 담당합니다. 인코딩이나 암호화 등의 동작이 이 계층에서 이루어집니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;l7-응용-계층application-layer&quot;&gt;L7 응용 계층(Application Layer)&lt;/h4&gt;
&lt;p&gt;일반적으로 우리가 알고있는 모든 응용 프로그램들이 존재하는 계층입니다. 
응용 프로그램에서 데이터 전송을 요구할 경우 하위 계층으로 전달합니다.&lt;/p&gt;
</description>
        <pubDate>Fri, 27 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/09/27/osi7.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/09/27/osi7.html</guid>
        
        <category>운영체제</category>
        
        <category>os</category>
        
        <category>operating system</category>
        
        <category>리눅스</category>
        
        <category>linux</category>
        
        <category>osi7</category>
        
        <category>계층</category>
        
        
      </item>
    
      <item>
        <title>Python 프로그래머스 - 완주하지 못한 선수</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=19JOAJmDm3xiyfB7xmxDclbuqgpmtjXOJ&quot; alt=&quot;프로그래머스&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;문제-설명&quot;&gt;문제 설명&lt;/h4&gt;
&lt;p&gt;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;제한사항&quot;&gt;제한사항&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.&lt;/li&gt;
  &lt;li&gt;completion의 길이는 participant의 길이보다 1 작습니다.&lt;/li&gt;
  &lt;li&gt;참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.&lt;/li&gt;
  &lt;li&gt;참가자 중에는 동명이인이 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;입출력-예&quot;&gt;입출력 예&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1m4m2zlPZQhGEdVRU0uWCFeYoGzw212kz&quot; alt=&quot;counter&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;입출력-예-설명&quot;&gt;입출력 예 설명&lt;/h4&gt;
&lt;p&gt;예제 #1“leo”는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.&lt;br /&gt;
예제 #2“vinko”는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.&lt;br /&gt;
예제 #3“mislav”는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;풀이&quot;&gt;풀이&lt;/h4&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;participant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;participant_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;participant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;completion_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;participant_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completion_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;파이썬의 Counter 모듈을 사용하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Counter&lt;/code&gt;&lt;/strong&gt;는 &lt;strong&gt;collections&lt;/strong&gt; 아래에 정의된 딕셔너리의 서브 클래스로 일련의 집합에서 각 원소의 출현 횟수를 세어서 유지합니다. 즉 &lt;strong&gt;원소:출현빈도&lt;/strong&gt;의 딕셔너리가 생성됩니다. 생성된 &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Counter&lt;/code&gt;&lt;/strong&gt; 딕셔너리는 일반 딕셔너리와 유사하게 키:값 쌍을 추가/삭제하거나 업데이트 할 수 있고, 각 원소를 출현 횟수만큼 반복하여 조합해서 복구합니다.&lt;/p&gt;

&lt;p&gt;Counter 모듈을 사용하여 문제를 풀어보면 리스트 인자로 주어진 participant, completion을 Couter를 이용하여 각 원소의 출현 횟수를 얻어낼 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;participant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mislav&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;stanko&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mislav&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ana&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stanko&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ana&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mislav&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;completion_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;participant_counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;participant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completion_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;participant_counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;위 코드의 결과는 아래와 같습니다.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'mislav'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'stanko'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'ana'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'mislav'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'stanko'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'ana'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;문제에서 완주하지 못한 선수는 한명이기 때문에 두 결과를 ‘-‘로 연산하여 나온 결과값의 key 값을 가져오면 완주하지 못한 선수의 이름을 가져올 수 있습니다.&lt;/p&gt;

</description>
        <pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/algorithm/2019/09/25/python-collections-counter.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/algorithm/2019/09/25/python-collections-counter.html</guid>
        
        <category>프로그래머스</category>
        
        <category>파이썬</category>
        
        <category>python</category>
        
        <category>완주하지 못한 선수</category>
        
        <category>알고리즘</category>
        
        
        <category>algorithm</category>
        
      </item>
    
      <item>
        <title>운영체제 (Operating System / OS) 구조 &amp; 동작</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=14deQUE0ykyNyS2jnnmzTbvdZaldeZomb&quot; alt=&quot;운영체제&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;운영체제의-구조&quot;&gt;운영체제의 구조&lt;/h3&gt;

&lt;p&gt;운영체제가 동작하는 원리는 불편함을 개선하면서 변화해왔습니다. 
DOS 시절 운영체제에서는 자원의 효율성이 굉장히 떨어졌습니다. 
우선순위가 앞서 있는 작업을 수행하는 동안 CPU가 사용되지 않음에도 불구하고 
뒤에 있는 작업은 CPU를 사용하지 못하고 앞의 작업을 기다려야했습니다. 
이렇게 유연하지 않은 구조는 컴퓨터 전체의 효율성을 떨어뜨렸습니다. 
그래서 나온것이 멀티 프로그래밍(Multiprogramming)입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;멀티-프로그래밍multiprogramming&quot;&gt;멀티 프로그래밍(Multiprogramming)&lt;/h4&gt;

&lt;p&gt;멀티 프로그래밍은 여러 작업들이 동시에 메모리에 올라갑니다.
그리고 앞의 작업이 CPU를 사용하지 않을 때 CPU는 대기상태에 들어갑니다. 
대기상태에 들어간 CPU는 다른 작업에서 사용할 수 있습니다. 
이러한 방식은 전의 방식보다 유연성을 제공하며 효율성을 높입니다. 반대로 I/O가 
대기 상태에 들어갈 때 I/O가 필요한 작업에게 그 자원을 할당해 줍니다.&lt;/p&gt;

&lt;p&gt;하지만 멀티프로그래밍에도 단점이 있습니다. 작업들마다 자원의 사용에 시간 차이가
생기는 것입니다. 예를들어 먼저 온 작업이 CPU나 I/O를 사용하고 있을 시 다른 작업은 
해당 자원을 사용하지 못하는 것입니다. 그럼 CPU나 I/O 중 한 자원은 대기를 하면서 
자원의 낭비가 일어나는 것입니다. 이러한 낭비는 효율성을 떨어뜨리게 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;멀티-테스킹multitasking&quot;&gt;멀티 테스킹(Multitasking)&lt;/h4&gt;

&lt;p&gt;멀티 테스킹은 멀티 프로그래밍의 확장입니다. 
각각 작업에 시간을 부여하고 CPU 작업을 하다가 그 시간이 지나가면 다른 작업에게 
CPU 자원을 할당해줍니다. 주어진 시간을 두고 번갈아가면서 자원을 사용하는 것입니다. 
이렇게 하게되면 시간 지연으로 인한 낭비를 줄일 수 있습니다. 
주어진 시간은 굉장히 짧으며 빈번한 Switching이 발생합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;운영체제의-동작&quot;&gt;운영체제의 동작&lt;/h3&gt;

&lt;p&gt;운영체제는 이전 포스팅에서 말했듯이 Interrupt를 기다리고 Interrupt가 발생시 움직입니다.
이러한 방식을 Interrupt-driven 방식이라고 합니다. 현재의 운영체제는 Interrupt에 의해 구동됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;시스템-콜system-call&quot;&gt;시스템 콜(System Call)&lt;/h4&gt;

&lt;p&gt;사용자 프로그램이 작동하다가 운영체제에게 어떠한 요청을 하는 것을 시스템 콜(System Call)이라고
합니다. 즉, 사용자 &lt;span class=&quot;emphasis&quot;&gt;프로그램이 운영체제의 서비스를 받기 위해 커널 함수를 호출&lt;/span&gt;하는 것입니다.&lt;/p&gt;

&lt;p&gt;Interrupt가 발생하면 운영체제는 해당 주소를 저장하고 그 주소에서 대기합니다. 
그리고 운영체제는 &lt;span class=&quot;emphasis&quot;&gt;Interrupt Vector Table&lt;/span&gt;에서 해당 Interrupt에 대한 번호를 찾습니다.
그 번호에 대응하는 행위로 &lt;span class=&quot;emphasis&quot;&gt;Interrupt Vector Routine&lt;/span&gt;을 실행시킵니다.
Interrupt Vector Routine이 끝나면 운영체제는 저장해 놓았던 주소로 되돌아가 다음 Interrupt를 
기다리게 되는 것입니다. 
시스템 콜은 이러한 Software Interrupt(&lt;strong&gt;Trap&lt;/strong&gt;)의 일종이라고 할 수 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Trap에는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exception&lt;/code&gt;이라는 것도 있는데 이는 프로그램이 &lt;strong&gt;오류&lt;/strong&gt;에 의해서 Interrupt를 발생시키는 것입니다.
운영체제는 한 작업의 Error로 인해 자원을 계속해서 점유하는 일 등 효율성을 저해하는 요인으로부터
&lt;strong&gt;보호&lt;/strong&gt;할 수단을 필요로 합니다. 그러한 방법에는 크게 두가지가 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;dual-mode-execution&quot;&gt;Dual-Mode Execution&lt;/h4&gt;

&lt;p&gt;이 방법의 조건으로 H/W의 지원이 필요합니다. 시스템을 보다 안전하게 하는 것이 목적입니다. 
이는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mode-Bit&lt;/code&gt;이라는 것을 필요로 하는데, Mode에는 &lt;strong&gt;Kernel Mode&lt;/strong&gt;와 &lt;strong&gt;User Mode&lt;/strong&gt; 두 가지가 
있습니다.&lt;/p&gt;

&lt;p&gt;사용자가 Kernel 상 작업, 즉 컴퓨터의 Core에 해당하는 작업을 직접 명령하고 수행하다가 
잘못하여 시스템 전체에 악영향을 끼칠 수 있습니다. 이를 방지하고자하는 방법입니다.&lt;/p&gt;

&lt;p&gt;각 명령어에 Mode-Bit을 심어 해당 명령어의 Mode-Bit와 형재 시스템 상의 Mode-Bit가 
같을 시에만 해당 명령어를 수행하게끔 합니다.
사용자가 Kernel을 사용해 어떤 작업을 하려고 한다면 Mode-Bit를 Kernel-Mode로 변경 후 
해당 작업을 실행하고 작업이 끝나면 다시 User-Mode로 변경됩니다. 
이렇게 Mode-Bit를 변경하는 행위가 위에서 설명한 System Call에 해당합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;timer&quot;&gt;Timer&lt;/h4&gt;

&lt;p&gt;두번째 방법으로 Timer가 있습니다. Timer는 무한 루프나 자원의 독점을 막는 역할을 해냅니다.
Timer는 특정 시간이 지나면 Interrupt를 발생시킵니다. 운영체제는 Timer가 끝난 작업을 
이 Interrupt를 통해 종료시키고 실행 되기 전 Scheduling 작업 전에 Timer를 작동시킵니다.&lt;/p&gt;
</description>
        <pubDate>Sun, 25 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/25/os-structure.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/25/os-structure.html</guid>
        
        <category>운영체제</category>
        
        <category>os</category>
        
        <category>operating system</category>
        
        <category>리눅스</category>
        
        <category>linux</category>
        
        <category>구조</category>
        
        
      </item>
    
      <item>
        <title>운영체제 (Operating System / OS)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=14deQUE0ykyNyS2jnnmzTbvdZaldeZomb&quot; alt=&quot;운영체제&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;운영체제-operating-system--os&quot;&gt;운영체제 (Operating System / OS)&lt;/h3&gt;

&lt;p&gt;운영체제란 컴퓨터 하드웨어 바로 윗단에 설치되는 소프트웨어입니다.&lt;br /&gt;
시스템의 각종 하드웨어 자원(CPU, Memory 등)과 소프트웨어 자원을 효율적으로 운영하고 관리하는 역할을 합니다.&lt;br /&gt;
때문에 사용자가 응용프로그램을 보다 편리하게 사용하고 하드웨어의 성능을 최적화 할 수 있도록 하는 역할을 하는 셈입니다.&lt;/p&gt;

&lt;p&gt;쉽게 말해 &lt;span class=&quot;emphasis&quot;&gt;운영체제는 어느정도 한계가 있는 자원을 효율적으로 사용하도록 돕습니다.&lt;/span&gt;&lt;br /&gt;
우리가 흔히 운영체제를 말할 때 ‘커널(Kernel)’을 떠올립니다. 커널은 운영체제의 핵심 부분으로 메모리에 상주합니다. 
여기서 메모리란 주기억장치 상의 메인 메모리를 의미합니다. 
대표적으로 Windows, Mac, Linux, Unix 등이 있습니다.&lt;/p&gt;

&lt;p&gt;이 운영체제가 없었던 시절이 있었습니다. 그때는 사용자가 어떤 프로그램을 실행하기 위해서 
직접 하드웨어를 관리하고, 그러기위해 전문화된 고도의 지식을 필요로 했습니다. 
하지만 운영체제가 등장했고 하드웨어와 일반 사용자들 간의 중개자 역할을 해내고 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;운영체제os-작동&quot;&gt;운영체제(OS) 작동&lt;/h4&gt;
&lt;p&gt;운영체제가 어떻게 작동하는 지 알아보기 위해 부팅 과정을 살펴보겠습니다.&lt;br /&gt;
컴퓨터가 부팅되기 위해서 CPU와 메인 메모리, 하드디스크가 준비되어야합니다. 컴퓨터를 구성하는 기본적인 녀석들입니다. 
&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPU&lt;/code&gt;&lt;/strong&gt;는 컴퓨터에서 연산을 담당, &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;메인 메모리&lt;/code&gt;&lt;/strong&gt;는 컴퓨터에서 작동하고 있는 프로그램인 프로세서들을 저장, 관리합니다. 
&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;하드디스크&lt;/code&gt;&lt;/strong&gt;는 아직 사용하지 않는 프로그램들을 저장하고 있습니다.&lt;/p&gt;

&lt;p&gt;컴퓨터 부팅 전에 운영체제는 하드디스크에 존재합니다. 컴퓨터를 부팅시키면 하드디스크에 있는
운영체제가 메인 메모리로 올라갑니다. 메인 메모리는 원래 RAM인 휘발성 메모리로 컴퓨터가 
OFF되면 자료를 모두 날려버립니다.&lt;br /&gt;
하지만 운영체제를 올리는 프로그램까지 날려버리면 
하드디스크에서 운영체제를 메인 메모리에 올릴 방법이 없어지기 때문에 
이 부분(운영체제를 메인메모리에 올리는)은 ROM인 비휘발성인 부분으로 지정되어 있습니다. 
이 프로그램이 운영체제를 메인메모리에 올리면서 운영체제가 컴퓨터를 제어하기 시작합니다.
운영체제는 메인 메모리에 올라가 다음 event를 기다립니다. 이러한 event를 시스템에서는 Interrupt라고 부릅니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;interrupt의-과정&quot;&gt;Interrupt의 과정&lt;/h4&gt;
&lt;p&gt;Interrupt가 발생하면 (예를들어 키보드 입력) 제일 먼저 운영체제는 해당 명령어의 주소를 저장합니다.
그 이유는 Interrupt에 대응하는 행위를 끝내고 다시 그 다음 명령어를 수행하기 위해 다시 그 위치에서
대기하기 위함입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;하드웨어hardware&quot;&gt;하드웨어(Hardware)&lt;/h4&gt;
&lt;p&gt;운영체제와 하드웨어가 어떻게 데이터를 주고받는지 알아보기위해 하드웨어에 대해 이야기해보겠습니다.
하드웨어는 입출력장치인 I/O와 해당 장치를 제어하는 Controller로 이루어져있습니다. 
우리가 사용하는 입출력장치 하드웨어에는 모두 Controller가 있으며 이 녀석들이 데이터를 운영체제에
전달합니다. 운영체제는 이 하드웨어와 데이터를 주고받기 위해 Device Driver(D/D)가 필요합니다. 
우리가 USB를 연결할 때 드라이버 장치가 필요하다고 알림창이 뜨는 그 녀석입니다. 
운영체제에서 보내는 데이터는 D/D의 Buffer에 저장되었다가 나가게 됩니다. 하드웨어에서 보내는
데이터 또한 Controller의 Buffer에 저장되었다가 전송됩니다. 이러한 전송 완료에 대한 메시지를 
Interrupt로 발생시킵니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Buffer&lt;/code&gt;&lt;/strong&gt; : 데이터 전송을 위한 임시 저장 장치&lt;br /&gt;
Buffer의 용량은 상당히 작습니다. 때문에 적은 양의 데이터 전송에는 무리가 없지만&lt;br /&gt;
대용량의 데이터를 전송할 때 문제가 생깁니다. 또한 빈번한 운영체제의 개입이 이루어집니다.&lt;br /&gt;
이러한 문제를 해결하기 위해 DMA(Direct Memory Access)가 도입됩니다.&lt;/p&gt;

&lt;p&gt;DMA는 장치에 대한 연결의 Interrupt를 발생시킨 후 운영체제는 모든 권한을 DMA에게 위임합니다.&lt;br /&gt;
그리하여 빈번한 운영체제의 개입이 없어지고 전송 단위 또한 커서 빠르게 전송할 수 있게 됩니다.&lt;/p&gt;
</description>
        <pubDate>Fri, 23 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/23/os.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/23/os.html</guid>
        
        <category>운영체제</category>
        
        <category>os</category>
        
        <category>operating system</category>
        
        <category>리눅스</category>
        
        <category>linux</category>
        
        
      </item>
    
      <item>
        <title>HTTP 완벽가이드::Connection</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1FOZcrxOvATtgpS30IUyknAumEHM9U-Y-&quot; alt=&quot;book&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이 포스팅은 책 ‘HTTP 완벽가이드’를 읽고 작성하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-커넥션-관리&quot;&gt;4. 커넥션 관리&lt;/h3&gt;

&lt;h4 id=&quot;41-tcp-커넥션&quot;&gt;4.1 TCP 커넥션&lt;/h4&gt;

&lt;p&gt;전 세계 모든 HTTP 통신은, 패킷 교환 네트워크 프로토콜들의 계층화된 집합인 
&lt;a href=&quot;https://jongjineee.github.io/datascience/2018/12/31/tcp_ip.html&quot;&gt;TCP/IP&lt;/a&gt;를 통해 이루어집니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;웹 브라우저가 TCP 커넥션을 통해 웹서버에 요청을 보냅니다.&lt;br /&gt;
&lt;strong&gt;URL : http://jongjineee.github.io:80/2019/08/18/http-connection.html&lt;/strong&gt;&lt;br /&gt;
1) 브라우저가 jongjineee.github.io라는 호스트 명을 추출합니다.&lt;br /&gt;
2) 브라우저가 이 호스트 명에 대한 IP 주소를 찾습니다.&lt;br /&gt;
3) 브라우저가 포트 번호 80번을 얻습니다.&lt;br /&gt;
4) 브라우저가 202.43.78.3의 80포트로 TCP 커넥션을 생성합니다.&lt;br /&gt;
5) 브라우저가 서버로 HTTP GET 요청 메시지를 보냅니다.&lt;br /&gt;
6) 브라우저가 서버에서 온 HTTP 응답 메시지를 읽습니다.&lt;br /&gt;
7) 브라우저가 커넥션을 끊습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;411-신뢰할-수-있는-데이터-전송-통로-tcp&quot;&gt;4.1.1 신뢰할 수 있는 데이터 전송 통로 TCP&lt;/h4&gt;

&lt;p&gt;HTTP 커넥션은 몇몇 사용 규칙을 제외하고는 TCP 커넥션에 불과합니다. TCP 커넥션은 인터넷을 안정적으로 연결해줍니다.
TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;412-tcp-스트림은-세그먼트로-나뉘어-ip-패킷을-통해-전송됩니다&quot;&gt;4.1.2 TCP 스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송됩니다.&lt;/h4&gt;

&lt;p&gt;TCP는 IP 패킷(혹은 IP 데이터그램)이라고 불리는 작은 조각을 통해 데이터를 전송합니다.
HTTP가 메시지를 전송하고자 할 경우, 현재 연결되어 있는 TCP 커넥션을 통해서 메시지 데이터의 내용을 순서대로 보냅니다.
TCP는 세그먼트라는 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷이라고 불리는 봉투에 담아서 인터넷을 통해 데이터를 전달합니다.
각 TCP 세그먼트는 하나의 IP 주소에서 다른 IP 주소로 IP 패킷에 담겨 전달됩니다.&lt;/p&gt;

&lt;p&gt;IP 패킷은 다음을 포함합니다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;IP 패킷 헤더&lt;/li&gt;
  &lt;li&gt;TCP 세그먼트 헤더&lt;/li&gt;
  &lt;li&gt;TCP 데이터 조각&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;413-tcp-커넥션-유지하기&quot;&gt;4.1.3 TCP 커넥션 유지하기&lt;/h4&gt;

&lt;p&gt;컴퓨터는 항상 TCP 커넥션을 여러 개 가지고 있습니다. TCP는 포트 번호를 통해서 이런 여러 개의 커넥션을 유지합니다.
TCP 커넥션은 네 가지 값으로 식별합니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;이 네 가지 값으로 유일한 커넥션을 생성합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;414-tcp-소켓-프로그래밍&quot;&gt;4.1.4 TCP 소켓 프로그래밍&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://jongjineee.github.io/2019/08/23/os.html&quot;&gt;운영체제&lt;/a&gt;는 TCP 커넥션의 생성과 관련된 여러 기능을 제공합니다.
소켓 API는 HTTP 프로그래머에게 TCP와 IP의 세부사항들을 숨깁니다. 소켓 API는 유닉스 운영체제용으로 먼저 개발되었지만, 지금은 소켓 API의 다양한
구현체들 덕분에 대부분의 운영체제와 프로그램 언어에서 이를 사용할 수 있게 되었습니다.&lt;/p&gt;

&lt;p&gt;소켓 API를 사용하면, TCP endpoint 데이터 구조를 생성하고, 원격 서버의 TCP endpoin에 그 데이터 구조를 연결하여 데이터 스트림을 읽고 쓸 수 있습니다.
TCP API는, 기본적인 네트워크 프로토콜의 &lt;a href=&quot;https://jongjineee.github.io/datascience/2018/12/31/hand_shake.html&quot;&gt;핸드셰이킹&lt;/a&gt;, 그리고 TCP 데이터 스트림과 
IP 패킷 간의 분할 및 재조립에 대한 모든 세부사항을 외부로부터 숨깁니다.&lt;/p&gt;

&lt;p&gt;일단 커넥션이 맺어지면 클라이언트는 HTTP 요청을 보내고 서버는 이를 읽습니다. 서버가 요청 메시지를 다 받으면, 그 요청을 분석하여 클라이언트가 원하는 동작을 수행합니다.
클라이언트에게 데이터를 보내고 클라이언트는 이를 받아 응답 데이터를 처리합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;42-tcp의-성능에-대한-고려&quot;&gt;4.2 TCP의 성능에 대한 고려&lt;/h4&gt;

&lt;p&gt;HTTP는 TCP 바로 위에 있는 계층이기 때문에 HTTP 트랜잭션의 성능은 그 아래 계층인 TCP 성능에 영향을 받습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;421-http-트랜잭션-지연&quot;&gt;4.2.1 HTTP 트랜잭션 지연&lt;/h4&gt;

&lt;p&gt;트랜잭션을 처리하는 시간은 TCP 커넥션을 설정하고, 요청을 전송하고, 응답 메시지를 보내는 것에 비하면 상당히 짧다는 것을 알 수 있습니다.
클라이언트나 서버가 너무 많은 데이터를 내려받거나 복잡하고 동적인 자원들을 실행하지 않는 한, 대부분의 HTTP 지연은 네트워크 지연 때문에 발생합니다.
이런 TCP 네트워크 지연은 하드웨어의 성능, 네트워크와 서버의 전송 속도, 요청과 응답 메시지의 크기, 클라이언트와 서버 간의 거리에 따라 크게 달라집니다. 
또한 TCP 프로토콜의 기술적인 복잡성도 지연에 큰 영향을 끼칩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;431-흔히-잘못-이해하는-커넥션-헤더&quot;&gt;4.3.1 흔히 잘못 이해하는 커넥션 헤더&lt;/h4&gt;

&lt;p&gt;HTTP는 클라이언트와 서버 사이에 프락시 서버, 캐시 서버 등과 같은 중개 서버가 놓이는 것을 허락한다. HTTP 메시지는 클라이언트에서 서버까지 중개 서버들을 하나하나 거치면서
전달됩니다. HTTP 커넥션 헤더 필드는 커넥션 토큰을 쉼표로 구분하여 가지고 있으며, 그 값들은 다른 커넥션에 전달되지 않습니다.
커넥션 헤더는 다음 세 가지 종류의 토큰이 전달될 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;HTTP 헤더 필드명으너 이 커넥션에만 해당되는 헤더들을 나열합니다.&lt;/li&gt;
  &lt;li&gt;임시적인 토큰 값은 커넥션에 대한 비표준 옵션을 의미합니다.&lt;/li&gt;
  &lt;li&gt;close 값은 커넥션이 작업이 완료되면 종료되어야 함을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;커넥션 토큰이 HTTP 헤더 필드 명을 가지고 있으면, 해당 필드들은 현재 커넥션만을 위한 정보이므로 다음 커넥션에 전달하면 안됩니다.
커넥션 헤더에 있는 모든 헤더 필드는 메시지를 다른 곳으로 전달하는 시점에서 삭제되어야합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;432-순차적인-트랜잭션-처리에-의한-지연&quot;&gt;4.3.2 순차적인 트랜잭션 처리에 의한 지연&lt;/h4&gt;

&lt;p&gt;커넥션 관리가 제대로 이루어지지 않으면 TCP 성능이 매우 나빠질 수 있습니다.
순차적인 처리로 지연에는 물리적인 지연 뿐만 아니라 하나의 데이터를 내려받는 중에는 웹페이지에 아무런 변화가 없어서 느끼는 심리적인 지연이 있습니다.
순차적인 처리 방식에는 특정 브라우저의 경우 객체를 화면에 배치하려면 객체의 크리를 알아야하기 때문에 모든 객체를 내려받기 전까지 텅 빈 화면을 보여주는 단점이 
있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;44-병렬-커넥션&quot;&gt;4.4 병렬 커넥션&lt;/h4&gt;

&lt;p&gt;병렬 커넥션은 순차적 처리 방식의 단점을 해결해줍니다.&lt;br /&gt;
HTTP는 클라이언트가 여러 개의 커넥션을 맺음으로써 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;441-병렬-커넥션은-페이지를-더-빠르게-내려받는다&quot;&gt;4.4.1 병렬 커넥션은 페이지를 더 빠르게 내려받는다&lt;/h4&gt;

&lt;p&gt;단일 커넥션의 대역폭 제한과 커넥션이 동작하고 있지 않은 시간을 활용하면, 객체가 여러 개 있는 웹페이지를 더 빠르게 내려받을 수 있습니다.
다수의 트랜잭션 발생시, 별도의 커넥션에서 동시에 처리되어 총 지연시간이 줄어듭니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;442-병렬-커넥션이-항상-더-빠르지는-않다&quot;&gt;4.4.2 병렬 커넥션이 항상 더 빠르지는 않다&lt;/h4&gt;

&lt;p&gt;일반적으로 병렬 커넥션이 더 빠르지만, 항상 그렇지는 않습니다.
클라이언트 네트워크 대역폭이 좁을 때, 제한된 대역폭 내에서 여러 개의 커넥션을 생성하면서 생기는 부하 때문에 순차적으로 내려받는 것보다 더 오래 걸릴 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;443-병렬-커넥션은-더-빠르게-느껴질-수-있다&quot;&gt;4.4.3 병렬 커넥션은 더 빠르게 ‘느껴질 수’ 있다&lt;/h4&gt;

&lt;p&gt;병렬 커넥션이 페이지를 항상 더 빠르게 로드하지는 않습니다. 하지만 화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 더 빠르다고 느낍니다.
사람들은 총 다운로드 시간이 더 많이 걸린다 하더라도 눈으로 작업을 확인할 수 있으면 더 빠르다고 여기기 때문입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;45-지속-커넥션&quot;&gt;4.5 지속 커넥션&lt;/h4&gt;

&lt;p&gt;웹 클라이언트는 보통 같은 사이트에 여러 개의 커넥션을 맺습니다.
따라서 트랜잭션 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용할 수 있습니다.
이는 커넥션 맺기 위한 준비작업 시간을 절약해주고 느린 시작으로 인한 지연을 회피하도록 해줍니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;451-지속-커넥션-vs-병렬-커넥션&quot;&gt;4.5.1 지속 커넥션 vs 병렬 커넥션&lt;/h4&gt;

&lt;p&gt;병렬 커넥션에도 몇 가지 단점이 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요됩니다.&lt;/li&gt;
  &lt;li&gt;각각의 새로운 커넥션은 TCP 느린 시작으로 때문에 성능이 떨어집니다.&lt;/li&gt;
  &lt;li&gt;실제로 연결할 수 있는 병렬 커넥션의 수에 제한이 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;또한 지속 커넥션의 장점이 있습니다.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;커넥션을 맺기 위한 사전 작업과 지연을 줄여줍니다.&lt;/li&gt;
  &lt;li&gt;튜닝된 커넥션을 유지하며 커넥션의 수를 줄여줍니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;하지만 지속 켜넥션을 잘못 관리할 경우, 계속 연결된 상태로 있는 수많은 커넥션이 쌓게 될 것입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;452-http10의-keep-alive-커넥션&quot;&gt;4.5.2 HTTP/1.0+의 Keep-Alive 커넥션&lt;/h4&gt;

&lt;p&gt;초기의 keep-alive 커넥션은 상호 운용과 관련된 설계에 문제가 있었지만 아직 많은 클라이언트와 서버는 이 초기 keep-alive 커넥션을 사용하고 있습니다.
그리고 그 설계상의 문제는 HTTP/1.1에서 수정되었습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;453-keep-alive-동작&quot;&gt;4.5.3 Keep-Alive 동작&lt;/h4&gt;

&lt;p&gt;클라이언트는 커넥션을 유지하기 위해서 요청에 Connection:Keep-Alive 헤더를 호함시킵니다. 이 요청을 받은 서버는 그 다음 요청도 이 커넥션을 통해 받고자 한다면, 
응답 메시지에 같은 헤더를 포함시켜 응답합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;454-keep-alive-옵션&quot;&gt;4.5.4 Keep-Alive 옵션&lt;/h4&gt;

&lt;p&gt;Keep-Alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐입니다. 클라이언트나 서버가 이를 요청 받았다고 모조건 따를 필요는 없습니다.
언제든지 커넥션을 끊을 수 있으며 트랜잭션의 수를 제한할 수도 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;456-keep-alive와-dumb-프락시&quot;&gt;4.5.6 Keep-Alive와 dumb 프락시&lt;/h4&gt;

&lt;p&gt;dumb 프락시에서 keep-alive를 사용할 때 생기는 문제가 있습니다.
웹 클라이언트 요청에 Connection:Keep-Alive 헤더가 있으면, 클라이언트가 현재 연결하고 있는 TCP 커넥션을 끊지 않고 계속 유지하려는 것입니다.&lt;/p&gt;

&lt;h5 id=&quot;connection-헤더의-무조건-전달&quot;&gt;Connection 헤더의 무조건 전달&lt;/h5&gt;
&lt;p&gt;프락시는 Connection 헤더를 이해하지 못해서 해당 헤더를 삭제하지 않고 요청 그대로를 다음 프락시에 전달
프락시는 keep-alive를 전혀 모르지만, 받았던 모든 데이터를 그대로 클라이언트에게 전달하고 나서 서버가 커넥션 끊기를 기다림. 그러나 서버는 프락시가 커넥션을 유지하길 요청한 것으로 알고 있기 때문에 커넥션을 끊지 않음. 따라서 프락시는 커넥션이 끊어지기 전까지 무작정 기다리게 됨.
이런 잘못된 통신 때문에 브라우저는 타임아웃이 나서 커넥션이 끊길 때까지 기다림
이런 종류의 잘못된 통신을 피하려면, 프락시는 Connection 헤더들을 절대 전달하면 안 됨&lt;/p&gt;

&lt;p&gt;특히 문제는 프락시에서 시작되는데, 프락시는 Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프락시에 전달합니다.&lt;br /&gt;
프락시는 keep-alive를 그대로 클라이언트에게 전달하고 서버가 커넥션 끊기를 기다립니다. 하지만 서버는 프락시가 커넥션을 유지하길 요청한 것으로 알고 있고 때문에 커넥션을 끊지 않습니다.&lt;br /&gt;
서버는 keep-alive 응답을 프락시에게 전달하고 프락시는 이를 다시 클라이언트에게 보냅니다.&lt;br /&gt;
응답을 받은 클라이언트는 서버와 커넥션을 유지하고 있다고 생각하지만 정작 프락시는 이를 알지 못합니다.&lt;br /&gt;
클라이언트가 다음 요청을 보내기 시작하면 커넥션이 유지되고 있는 프락시에 그 요청을 보냅니다.&lt;br /&gt;
프락시는 같은 커넥션상에서 다른 요청이 오는 경우는 예상하지 못하기 때문에 그 요청은 프락시로부터 무시되고 브라우저는 아무런 응답도 받지 못하고 로드 중이라는 표시만 나옵니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;457-proxy-connection-살펴보기&quot;&gt;4.5.7 Proxy-Connection 살펴보기&lt;/h4&gt;

&lt;p&gt;Proxy-Connection은 클라이언트의 요청이 중개서버를 통해 이어지는 경우 모든 헤더를 무조건 전달하는 문제를 해결하기 위해 사용됩니다.
브라우저에서 일반적으로 전달하는 Connection 헤더 대신에 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달합니다.
프락시가 Proxy-Connection 헤더를 무조건 전달하더라도 웹 서버는 이를 무시하기 때문에 별문제가 되지 않습니다. 또한 영리한 프락시라면, 의미 없는 Proxy-Connection 헤더를 
Connection 헤더로 바꿈으로써 원하던 효과를 얻게 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;458-http11의-지속-커넥션&quot;&gt;4.5.8 HTTP/1.1의 지속 커넥션&lt;/h4&gt;

&lt;p&gt;HTTP/1.1 에서는 keep-alive 커넥션을 지원하지 않는 대신, 설계가 더 개선된 지속 커넥션을 사용합니다. 이 목적은 keep-alive 커넥션과 같지만 더 잘 작동합니다.
HTTP/1.1 에서 지속 커넥션은 기본으로 활성화되어 있습니다. 이는 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더를 명시해야 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;46-파이프라인-커넥션&quot;&gt;4.6 파이프라인 커넥션&lt;/h4&gt;

&lt;p&gt;HTTP/1.1은 지속 커넥션을 통해 요청을 파이프라이닝할 수 있습니다. 여러 개의 요청은 응답이 도착하기 전까지 &lt;a href=&quot;https://jongjineee.github.io/datascience/2018/01/03/stack_que.html&quot;&gt;큐&lt;/a&gt;에 쌓입니다. 이는 대기 시간이 긴 네트워크 상황에서 네트워크상의 왕복으로 인한 시간을 줄여서 성능을 높여줍니다.&lt;/p&gt;

&lt;h4 id=&quot;47-커넥션-끊기에-대한-미스터리&quot;&gt;4.7 커넥션 끊기에 대한 미스터리&lt;/h4&gt;

&lt;p&gt;커넥션 관리에는 명확한 기준이 없습니다. 이 이슈는 수많은 개발자가 알고 있는 것보다 더 미묘하며, 그에 관한 기술 문서도 별로 없습니다.&lt;/p&gt;

&lt;h4 id=&quot;471-마음대로-커넥션-끊기&quot;&gt;4.7.1 ‘마음대로’ 커넥션 끊기&lt;/h4&gt;

&lt;p&gt;어떠한 
HTTP 클라이언트, 서버, 혹은 프락시든 언제든지 TCP 전송 커넥션을 끊을 수 있습니다. 보통 커넥션은 메시지를 다 보낸 다음 끊지만, 에러가 있는 상황에서는 헤더의 중간이나 다른 엉뚱한 
곳에서 끊길 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;472-content-length와-truncation&quot;&gt;4.7.2 Content-Length와 Truncation&lt;/h4&gt;

&lt;p&gt;각 HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-Length 헤더를 가지고 있어야 합니다. 
클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 실제 전달 된 엔터티의 길이와 Content-Length의 값이 일치하는지, 존재하는지를 확인해야 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;473-커넥션-끊기의-허용-재시도-멱등성&quot;&gt;4.7.3 커넥션 끊기의 허용, 재시도, 멱등성&lt;/h4&gt;

&lt;p&gt;커넥션은 에러가 없더라도 언제든 끊을 수 있습니다. 때문에 HTTP 애플리케이션은 예상치 못하게 커넥션이 끊어졌을 때 적절히 대응할 수 있는 준비가 되어야합니다.
클라이언트는 수행 중 전송 커넥션이 끊기면 재시도를 해도 되는 지 확인 한 후 재시도를 해야합니다. 이는 파이프라인 커넥션에서 좀 어렵습니다.
한 번 혹은 여러 번 실행됐는지에 상관없이 같은 결과를 반환한다면 이 트랜잭션은 멱등(idempotent)하다고 이해합니다.
클라이언트는 POST와 같이 멱등이 아닌 요청은 파이프라인을 통해 요청하면 안됩니다. 그렇지 않으면 전송 커넥션이 예상치 못하게 끊어져 버렸을 때, 알 수 없는 결과를 초래할 수 있습니다.
때문에 대부분의 브라우저는 캐시된 POST 요청 페이지를 다시 로드하려고 할 때, 요청을 다시 보내기를 원하는지 묻는 대화상자를 보여줍니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;474-우아한-커넥션-끊기&quot;&gt;4.7.4 우아한 커넥션 끊기&lt;/h4&gt;

&lt;h5 id=&quot;전체-끊기와-절반-끊기&quot;&gt;전체 끊기와 절반 끊기&lt;/h5&gt;
&lt;p&gt;close()를 호출하면 TCP 커넥션의 입력 채널과 출력 채널의 커넥션은 모두 끊어집니다. 이를 ‘전체 끊기’라고 합니다.
입력 채널이나 출력 채널 중 하나를 개별적으로 끊으려면 shutdown()을 호출합니다. 이를 ‘절반 끊기’라고 합니다.&lt;/p&gt;

&lt;h5 id=&quot;tcp-끊기와-리셋-에러&quot;&gt;TCP 끊기와 리셋 에러&lt;/h5&gt;

&lt;p&gt;단순한 HTTP 애플리케이션은 전체 끊기만을 사용할 수 있습니다. 
하지만 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신할 때 기기들에 예상치 못한 쓰기 에러를 발생하는 것을 예방하기 위해 ‘절반 끊기’를 사용해야 합니다.
보통은 커넥션의 출력 채널을 끊는 것이 안전합니다. 
만약 클라이언트에서 이미 끊긴 입력 채널에 데이터를 전송하면, 서버의 운영체제는 TCP ‘connection reset by peer’ 메시지를 클라이언트에게 보냅니다. 
대부분 운영체제는 이를 심각한 에러로 취급하여 버퍼에 저장된, 아직 읽히지 않은 데이터를 모두 삭제합니다.&lt;/p&gt;

&lt;h5 id=&quot;우아하게-커넥션-끊기&quot;&gt;우아하게 커넥션 끊기&lt;/h5&gt;

&lt;p&gt;일반적으로 애플리케이션이 우아한 커넥션 끊기를 구현하는 것은 애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 겁입니다.
양쪽에서 더는 데이터를 전송하지 않을 것이라고 알려주면 커넥션은 리셋의 위험 없이 온전히 종료됩니다.
상대방이 절반 끊기를 구현한다는 보장이 없기 때문에 커넥션을 끊고자 하는 애플리케이션은 출력 채널에 절반 끊기를 하고 난 후에도 데이터나 스트림의 끝을 식별하기 위해 입력 채널에 
대해 상태 검사를 주기적으로 해야합니다.&lt;/p&gt;

</description>
        <pubDate>Sun, 18 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/18/http-connection.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/18/http-connection.html</guid>
        
        <category>http</category>
        
        <category>https</category>
        
        <category>computerscience</category>
        
        <category>connection</category>
        
        
      </item>
    
      <item>
        <title>HTTP 완벽가이드::MESSAGE</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1FOZcrxOvATtgpS30IUyknAumEHM9U-Y-&quot; alt=&quot;book&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이 포스팅은 책 ‘HTTP 완벽가이드’를 읽고 작성하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-http-메시지&quot;&gt;3. HTTP 메시지&lt;/h3&gt;

&lt;h4 id=&quot;31-http-메시지의-흐름&quot;&gt;3.1 HTTP 메시지의 흐름&lt;/h4&gt;

&lt;p&gt;HTTP 메시지는 HTTP 애플리케이션 간에 주고받는 데이터의 블록입니다.&lt;br /&gt;
이 메시지는 클라이언트, 서버, 프락시 사이를 흐릅니다.&lt;br /&gt;
메시지가 흐르는 방향을 인바운드와 아웃바인드로 이름을 붙여 설명하는데
인바운드는 클라이언트에서 서버로 흐르는 메시지를 말하며 아웃바운드는 반대로 서버에서 클라이언트로 흐르는 것을 말힙니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;32-메시지의-각-부분&quot;&gt;3.2 메시지의 각 부분&lt;/h4&gt;

&lt;p&gt;HTTP 메시지는 단순한, 데이터의 구조화된 블록입니다. 각 메시지는 클라이언트로부터의 요청이나 서버로부터의 응답 중 하나를 포함합니다. 메시지는 시작줄, 헤더 블록, 본문 이렇게 세 부분으로 이루어집니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1yNZUzUBO6cdgBwWjQMXyQq9lLgmR-R1A&quot; alt=&quot;message-layout&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;321-메시지-문법&quot;&gt;3.2.1 메시지 문법&lt;/h4&gt;

&lt;p&gt;모든 HTTP 메시지는 요청 메시지나 응답 메시지로 분류됩니다. 요청 메시지는 웹 서버에 어떤 동작을 요구합니다. 응답 메시지는 요청의 결과를 클라이언트에게 돌려줍니다. 요청과 응답 모두 기본적으로 구조가 같습니다.
&lt;br /&gt;
각 형식은 다음과 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;요청 메시지 형식&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;메서드 요청&amp;gt; &amp;lt;URL&amp;gt; &amp;lt;버전&amp;gt;  
&amp;lt;헤더&amp;gt;  
  
&amp;lt;엔터티 본문&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;응답 메시지 형식&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;버전&amp;gt; &amp;lt;상태 코드&amp;gt; &amp;lt;사유 구절&amp;gt;  
&amp;lt;헤더&amp;gt;  
  
&amp;lt;엔터티 본문&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;322-시작줄&quot;&gt;3.2.2 시작줄&lt;/h4&gt;

&lt;p&gt;모든 HTTP 메시지는 시작줄로 시작합니다. 요청 메시지의 시작줄은 무엇을 해야하는지 말해줍니다. 응답 메시지의 시작줄은 무슨 일이 일어났는지 말해줍니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;323-헤더&quot;&gt;3.2.3 헤더&lt;/h4&gt;

&lt;p&gt;HTTP 헤더 필드는 요청과 응답 메시지에 추가 정보를 더합니다. 그들은 기본적으로 이름/값 쌍의 목록입니다. 예를들어, 다음의 헤더줄은 Content-Length 헤더 필드에 19라는 값을 할당합니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Content-length: 19
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;324-엔터티-본문&quot;&gt;3.2.4 엔터티 본문&lt;/h4&gt;

&lt;p&gt;HTTP 메시지는 엔터티 본문에 이미지, 비디오, HTML 문서 등 여러 종류의 디지털 데이터를 실어 나를 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;33-메서드&quot;&gt;3.3 메서드&lt;/h3&gt;

&lt;h4 id=&quot;331-get&quot;&gt;3.3.1 GET&lt;/h4&gt;

&lt;p&gt;GET은 가장 흔히 쓰이는 메서드입니다. 주로 서버에게 리소스를 달라고 요청하기 위해 쓰입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;332-head&quot;&gt;3.3.2 HEAD&lt;/h4&gt;

&lt;p&gt;서버가 응답으로 헤더만을 돌려줍니다. 엔터티 본문은 반환되지 않습니다. 이는 클라이언트가 리소스를 실제로 가져올 필요 없이 헤더만을 조사할 수 있도록 해줍니다. 이를 사용하면&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;리소스를 가져오지 않고도 그에 대해 무엇인가(타입 등)을 알아낼 수 있습니다.&lt;/li&gt;
  &lt;li&gt;응답의 상태 코드를 통해, 개체가 존재하는지 확인이 가능합니다.&lt;/li&gt;
  &lt;li&gt;헤더를 확인하여 리소스가 변경되었는지 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;333-put&quot;&gt;3.3.3 PUT&lt;/h4&gt;

&lt;p&gt;PUT 메서드는 서버에 문서를 씁니다. 서버가 요청의 본문을 가지고 요청 URL의 이름대로 새 문서를 만들거나, 이미 URL이 존재한다면 본문을 사용해서 교체하는 것입니다.&lt;br /&gt;
PUT은 콘텐츠를 변경할 수 있게 해주기 때문에, 사용자에게 비밀번호를 입력하여 로그인을 하도록 요구하기도 합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;334-post&quot;&gt;3.3.4 POST&lt;/h4&gt;

&lt;p&gt;POST 메서드는 서버에 입력 데이터를 전송하기 위해 설계되었습니다. 실제로는 HTML Form을 지원하기 위해 흔히 사용됩니다. Form의 테이터를 서버로 전송합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;335-trace&quot;&gt;3.3.5 TRACE&lt;/h4&gt;

&lt;p&gt;TRACE 메서드는 클라이언트에게 자신의 요청이 서버에 도달했을 때 어떻게 보이게 되는지 알려줍니다.&lt;br /&gt;
이는 서버에 ‘루프백(loopback)’ 진단을 시작합니다. 요청 전송의 마지막 단계의 서버는 자신이 받은 요청 메시지를 본문에 넣어 TRACE 응답을 되돌려줍니다. 클라이언트는 자신과 목적지 사이의 요청/응답 연쇄를 따라가면서 자신이 보낸 메시지가 망가졌는지 수정되었는지를 확인합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;336-options&quot;&gt;3.3.6 OPTIONS&lt;/h4&gt;

&lt;p&gt;OPTIONS 메서드는 웹 서버에게 여러 가지 종류의 지원 범위에 대해 물어봅니다. 서버에게 특정 리소스에 대해 어떤 메서드가 지원되는지 물어볼 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;337-delete&quot;&gt;3.3.7 DELETE&lt;/h4&gt;

&lt;p&gt;DELETE 메서드는 서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;338-확장-메서드&quot;&gt;3.3.8 확장 메서드&lt;/h4&gt;

&lt;p&gt;HTTP는 필요에 따라 확장해도 문제가 없도록 되어있습니다. 확장 메서드는 HTTP/1.1 명세에 정의되지 않은 메서드입니다. 이는 개발자들이 자신의 서버의 HTTP 메소드를 확장하여 사용할 수 있도록 해줍니다.
하지만 마음대로 확장 메서드를 사용하게되면 이를 처음 접하는 클라이언트는 어떤 의미인지 모를 수 있습니다. 그런 경우에는 이를 관용적으로 대하는 것이 좋다고합니다. 확장 메서드를 다룰 때는 “엄격하게 보내고 관대하게 받아들여라”라는 오랜 규칙을 따릅니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;34-상태코드&quot;&gt;3.4 상태코드&lt;/h3&gt;
&lt;h4 id=&quot;341-100-199-정보성-상태-코드&quot;&gt;3.4.1 100-199: 정보성 상태 코드&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;상태 코드&lt;/th&gt;
      &lt;th&gt;사유 구절&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;100&lt;/td&gt;
      &lt;td&gt;Continue&lt;/td&gt;
      &lt;td&gt;요청의 시작 부분 일부가 받아들여졌으며, 클라이언트는 나머지를 계속 이어서 보내야 한다. 이것을 보낸 후, 서버는 반드시 요청을 받아 응답해야 한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;101&lt;/td&gt;
      &lt;td&gt;Switching Protocols&lt;/td&gt;
      &lt;td&gt;클라이언트가 Upgrade 헤더에 나열한 것 중 하나로 서버가 프로토콜을 바꾸었음을 의미한다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;342-200-299-성공-상태-코드&quot;&gt;3.4.2 200-299: 성공 상태 코드&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;상태 코드&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;사유 구절&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;200&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;OK&lt;/td&gt;
      &lt;td&gt;요청 정상&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;201&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Created&lt;/td&gt;
      &lt;td&gt;서버 개체를 생성하라는 요청(ex. PUT)을 위한 것이다. 서버는 상태 코드를 보내기 전 반드시 객체를 생성해야 한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;202&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Accepted&lt;/td&gt;
      &lt;td&gt;요청은 받아들여졌으나 서버는 아직 어떤 동작도 수행하지 않는다. 요청 처리 완료에 대한 보장도 없다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;204&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No Content&lt;/td&gt;
      &lt;td&gt;응답 메시지는 헤더와 상태줄은 포함하지만, 엔터티 본문은 포함하지 않는다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;206&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Partial Content&lt;/td&gt;
      &lt;td&gt;부분 혹은 범위 요청이 성공&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;343-300-399-리다이렉션-상태-코드&quot;&gt;3.4.3 300-399: 리다이렉션 상태 코드&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;상태 코드&lt;/th&gt;
      &lt;th&gt;사유 구절&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;300&lt;/td&gt;
      &lt;td&gt;Multiple Choices&lt;/td&gt;
      &lt;td&gt;클라이언트가 동시에 여러 리소스를 가리키는 URL을 요청할 경우, 그 리소스의 목록과 함께 반환한다. 사용자는 목록에서 원하는 하나를 선택할 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;301&lt;/td&gt;
      &lt;td&gt;Moved Permanently&lt;/td&gt;
      &lt;td&gt;요청한 URL이 옮겨졌을 때 사용함. 응답에 Location 헤더에 현재 리소스의 URL을 포함해야 한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;302&lt;/td&gt;
      &lt;td&gt;Found&lt;/td&gt;
      &lt;td&gt;301 상태 코드와 같음. 그러나 Location 헤더로 주어진 URL을 리소스를 임시로 가리키기 위한 목적으로 사용해야 한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;303&lt;/td&gt;
      &lt;td&gt;See Other&lt;/td&gt;
      &lt;td&gt;클라이언트에게 리소스를 다른 URL에서 가져와야 한다고 말할 때 사용한다. 새 URL은 응답 메시지의 Location 헤더에 들어있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;304&lt;/td&gt;
      &lt;td&gt;Not Modified&lt;/td&gt;
      &lt;td&gt;리소스가 수정되지 않았음을 의미한다. 엔터티 본문을 가져서는 안 된다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;344-400-499-클라이언트-에러-상태-코드&quot;&gt;3.4.4 400-499: 클라이언트 에러 상태 코드&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;상태 코드&lt;/th&gt;
      &lt;th&gt;사유 구절&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;400&lt;/td&gt;
      &lt;td&gt;Bad Request&lt;/td&gt;
      &lt;td&gt;클라이언트가 잘못된 요청을 보냈다고 말해준다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;401&lt;/td&gt;
      &lt;td&gt;Unauthorized&lt;/td&gt;
      &lt;td&gt;리소스를 얻기 전에 클라이언트에게 스스로를 인증하라고 요구하는 내용의 응답을 반환한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;403&lt;/td&gt;
      &lt;td&gt;Forbidden&lt;/td&gt;
      &lt;td&gt;요청이 서버에 의해 거부되었다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;404&lt;/td&gt;
      &lt;td&gt;Not Found&lt;/td&gt;
      &lt;td&gt;서버가 요청한 URL을 찾을 수 없다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;408&lt;/td&gt;
      &lt;td&gt;Request Timeout&lt;/td&gt;
      &lt;td&gt;클라이언트 요청을 완수하는 시간이 오래 걸리는 경우, 이 상태 코드로 응답하고 연결을 끊을 수 있다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;409&lt;/td&gt;
      &lt;td&gt;Conflict&lt;/td&gt;
      &lt;td&gt;요청이 리소스에 대해 일으킬 수 있는 몇몇 충돌을 지칭하기 위해 사용한다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;345-500-599-서버-에러-상태-코드&quot;&gt;3.4.5 500-599: 서버 에러 상태 코드&lt;/h4&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;상태 코드&lt;/th&gt;
      &lt;th&gt;사유 구절&lt;/th&gt;
      &lt;th&gt;의미&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;500&lt;/td&gt;
      &lt;td&gt;Internal Server Error&lt;/td&gt;
      &lt;td&gt;서버가 요청을 처리할 수 없는 에러를 만났을 때 사용한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;501&lt;/td&gt;
      &lt;td&gt;Not Implemented&lt;/td&gt;
      &lt;td&gt;클라이언트가 서버의 능력을 넘은 요청을 했을 때 사용한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;502&lt;/td&gt;
      &lt;td&gt;Bad Gateway&lt;/td&gt;
      &lt;td&gt;프락시나 게이트웨이처럼 행동하는 서버가 그 요청 응답 연쇄에 있는 다음 링크로부터 가짜 응답에 맞닥뜨렸을 때 사용한다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;503&lt;/td&gt;
      &lt;td&gt;Service Unavailable&lt;/td&gt;
      &lt;td&gt;현재는 서버가 요청을 처리해줄 수 없지만 나중에는 가능하다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;504&lt;/td&gt;
      &lt;td&gt;Gateway Timeout&lt;/td&gt;
      &lt;td&gt;408과 비슷하지만, 다른 서버에 요청을 보내고 응답을 기다리다 타임아웃이 발생한 게이트웨이나 프락시에서 온 응답이라는 점이 다르다.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;505&lt;/td&gt;
      &lt;td&gt;HTTP Version Not Supported&lt;/td&gt;
      &lt;td&gt;서버가 지원할 수 없거나 지원하지 않으려고 하는 버전의 프로토콜로 된 요청을 받았을 때 사용한다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;35-헤더&quot;&gt;3.5 헤더&lt;/h3&gt;

&lt;p&gt;헤더와 메서드는 클라이언트와 서버가 무엇을 하는지 결정하기 위해 함께 사용됩니다.&lt;br /&gt;
헤더에는 특정 종류의 메시지에만 사용할 수 있는 헤더와, 더 일반 목적으로 사용할 수 있는 헤더, 그리고 응답과 요청 메시지 양쪽 모두에서 정보를 제공하는 헤더가 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;일반 헤더&lt;br /&gt;
일반 헤더는 클라이언트와 서버 양쪽 모두가 사용합니다. 예를들어 Date 헤더는 서버와 클라이언트를 가리지 않고 메시지가 만들어진 일시를 지칭하기 위해 사용되는 일반 목적 헤더입니다.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Date: Tue, 3 Oct 1974 02:16:00 GMT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;요청 헤더&lt;br /&gt;
요청 헤더는 요청 메시지를 위한 헤더입니다. 요청이 최초 발생한 곳에서 누가 혹은 무엇이 그 요청을 보냈는지에 대한 정보나 클라이언트의 정보를 줍니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;응답 헤더&lt;br /&gt;
응답 헤더는 클라이언트에게 부가 정보를 제공합니다. 누가 응답을 보내고 있는지 혹은 응답자의 능력은 어떻게 되는지 알려주며, 응답에 대한 특별한 설명도 제공합니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;엔터티 헤더&lt;br /&gt;
요청과 응답 양쪽 모두 엔터티를 포함할 수 있기 때문에 이 헤더들은 양 타입의 메시지에 모두 나타날 수 있습니다. 일반적으로 엔터티 헤더는 메시지의 수신자에게 자신이 다루고 있는 것이 무엇인지 말해줍니다.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 18 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/18/http-message.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/18/http-message.html</guid>
        
        <category>http</category>
        
        <category>https</category>
        
        <category>computerscience</category>
        
        <category>message</category>
        
        
      </item>
    
      <item>
        <title>HTTP 완벽가이드::URL</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1FOZcrxOvATtgpS30IUyknAumEHM9U-Y-&quot; alt=&quot;book&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이 포스팅은 책 ‘HTTP 완벽가이드’를 읽고 작성하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;2-url과-리소스&quot;&gt;2. URL과 리소스&lt;/h3&gt;

&lt;p&gt;우리의 도시는 각기 다른 이름들에 대한 작명 표준에 동의하기 때문에, 도시에 있는 소중한 리소스를 모두 쉽게 공유할 수 있습니다.&lt;br /&gt;
URL(Uniform Resource Locator)은 이와 같이 인터넷의 리소스를 가리키는 표준이름입니다. URL은 전자정보 일부를 가리키고 그것이 어디에 있고 어떻게 접근할 수 있는지 알려줍니다.&lt;/p&gt;

&lt;h4 id=&quot;21-인터넷의-리소스-탐색하기&quot;&gt;2.1 인터넷의 리소스 탐색하기&lt;/h4&gt;

&lt;p&gt;앞서 말했듯 URL은 브라우저가 정보를 찾는데 필요한 리소스의 위치를 가리키며, URL을 이용해 사람과 애플리케이션이 인터넷상의 수십억 개의 리소스를 찾고 사용하며 공유할 수 있습니다.
또 URL을 통해 사용자는 HTTP 및 다른 프로토콜을 통해 접근할 수 있습니다. 
URL은 URI라고 불리는 더 일반화된 부류의 부분집합입니다. URI는 두 가지 주요 부분집합인, URL과 URN으로 구성된 종합적인 개념입니다. 
URL을 사용하면 리소스를 일관된 방식으로 지칭할 수 있습니다. 대부분의 URL은 동일하게 &lt;span class=&quot;emphasis&quot;&gt;‘스킴://서버위치/경로’&lt;/span&gt; 구조로 이루어져 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;22-url-문법&quot;&gt;2.2 URL 문법&lt;/h4&gt;

&lt;p&gt;URL문법은 &lt;span class=&quot;emphasis&quot;&gt;스킴&lt;/span&gt;에 따라서 달라집니다.&lt;br /&gt;
다른 URL 스킴을 사용한다는 것이 전혀 다른 문법을 사용한다는 뜻은 아닙니다. 대부분의 URL은 일반 URL문법을 따르며, 서로 다른 URL 스킴도 형태와 문법 면에서 매우 유사합니다.
대부분의 URL 스킴의 문법은 일반적으로 9개 부분으로 나뉩니다.
&lt;br /&gt;
&lt;span class=&quot;emphasis&quot;&gt;&lt;스킴&gt; : // &lt;사용자 이름=&quot;&quot;&gt; : &lt;비밀번호&gt; @ &lt;호스트&gt; : &lt;포트&gt; / &lt;경로&gt;;&lt;파라미터&gt;?&lt;질의&gt;#&lt;프래그먼트&gt;&amp;lt;/span&amp;gt;
&lt;br /&gt;&lt;/프래그먼트&gt;&lt;/질의&gt;&lt;/파라미터&gt;&lt;/경로&gt;&lt;/포트&gt;&lt;/호스트&gt;&lt;/비밀번호&gt;&lt;/사용자&gt;&lt;/스킴&gt;&lt;/span&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;스킴&lt;/code&gt;&lt;/strong&gt; : 리소스를 가져오려면 어떤 프로토콜을 사용하여 서버에 접근해야하는지 가리킨다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;사용자이름&lt;/code&gt;&lt;/strong&gt; : 몇몇 스킴은 리소스에 접근을 하기 위해 사용자 이름을 필요로 한다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;비밀번호&lt;/code&gt;&lt;/strong&gt; : 사용자의 비밀번호를 가리키며, 사용자 이름에 콜론(:)으로 이어서 기술한다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;호스트&lt;/code&gt;&lt;/strong&gt; : 리소스를 호스팅하는 서버의 호스트 명이나 IP 주소&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;포트&lt;/code&gt;&lt;/strong&gt; : 리소스를 호스팅하는 서버가 열어놓은 포트번호, 많은 스킴이 기본 포트를 가지고 있다(HTTP의 기본 포트는 80이다.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;경로&lt;/code&gt;&lt;/strong&gt; : 이전 컴포넌트와 빗금(/)으로 구분되어 있으며, 서버 내 리소스가 서버 어디에 있는지 가리킨다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;파라미터&lt;/code&gt;&lt;/strong&gt; : 특정 스킴들에서 입력 파라미터를 기술하는 용도로 사용한다. 파라미터는 이름/값을 쌍으로 가진다. 파라미터는 여러개를 가질 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;질의&lt;/code&gt;&lt;/strong&gt; : 스킴에서 애플리케이션에 파라미터를 전달하는데 쓰인다. Ex) https://www.joes-hardware.com/inventory-check.cgi?item=12731&amp;amp;color=blue&amp;amp;size=large&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;프래그먼트&lt;/code&gt;&lt;/strong&gt; : 리소스의 조각이나 일부분을 가리키는 이름이다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;23-단축-url&quot;&gt;2.3 단축 URL&lt;/h4&gt;

&lt;p&gt;웹 클라이언트는 몇몇 단축 URL을 인식하고 사용합니다.&lt;/p&gt;

&lt;h5 id=&quot;231-상대-url&quot;&gt;2.3.1 상대 URL&lt;/h5&gt;

&lt;p&gt;URL은 상대 URL과 절대 URL 두 가지로 나뉩니다. 상대 URL을 통해 리소스에 접근하기 위해서는 &lt;span class=&quot;emphasis&quot;&gt;기저(base) URL&lt;/span&gt;이 필요합니다. 
HTML 작성 시 &lt;span class=&quot;emphasis&quot;&gt;./hammers.html&lt;/span&gt; 로 작성하는 URL을 상대 URL이라고 합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;기저url&lt;/code&gt;&lt;/strong&gt; : &lt;strong&gt;http://www.joes-hardware.com/tools.html&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;상대 url&lt;/code&gt;&lt;/strong&gt; : &lt;strong&gt;./hammers.html&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;새로운 절대 url&lt;/code&gt;&lt;/strong&gt; : &lt;strong&gt;http://www.joes-hardware.com/hammers.html&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h5 id=&quot;232-url-확장&quot;&gt;2.3.2 URL 확장&lt;/h5&gt;

&lt;p&gt;어떤 브라우저들은 URL을 입력한 다음이나 입력하고 있는 동안에 자동으로 URL을 확장합니다. 이는 사용자가 URL을 빠르게 입력하게 도와줍니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;호스트명 확장&lt;/strong&gt;&lt;br /&gt;
호스트명 확장 기능을 지원하는 브라우저는 단순한 단어로 입력한 호스트 명을 전체 호스트 명으로 확장할 수 있습니다. 
예를 들어 주소 입력란에 ‘yahoo’를 입력하면, 브라우저는 호스트 명에 자동으로 ‘www.’ 와 ‘.com’을 붙여서 ‘www.yahoo.com’을 만듭니다.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;히스토리 확장&lt;/strong&gt;&lt;br /&gt;
브라우저가 사용하는 또다른 기술은 과거에 사용자가 방문했던 URL의 기록을 저장해 놓는 것입니다. URL을 입력하면, 그 입력된 URL의 앞 글자들을 포함하는 완결된 형태의 URL들을 선택하게 해줍니다.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h4 id=&quot;24-안전하지-않은-문자&quot;&gt;2.4 안전하지 않은 문자&lt;/h4&gt;

&lt;p&gt;안전한 전송이란, 정보가 유실될 위험 없이 URL을 전송할 수 있다는 것을 의미합니다. 문자가 제거되는 일을 피하고자 URL은 상대적으로 작고 안전한 알파벳 문자만 포함하도록 허락합니다.
하지만 URL에 이진 데이터나 알파벳 외의 문자도 포함하려고 하는 경우가 많아지자 이스케이프라는 기능을 추가하여, 안전하지 않은 문자를 안전한 문자로 &lt;span class=&quot;emphasis&quot;&gt;인코딩&lt;/span&gt;할 수 있게 하였습니다.&lt;/p&gt;

&lt;h5 id=&quot;241-url-문자-집합&quot;&gt;2.4.1 URL 문자 집합&lt;/h5&gt;

&lt;p&gt;역사적으로 많은 컴퓨터 애플리케이션이 US-ASCII 문자 집합을 사용해왔습니다. US-ASCII는 문자를 서식화하고 하드웨어상에서 신호를 주고받기 위해, 7비트를 사용하여 영문 자판에 있는 키 대부분과 몇몇 출력되지 않는 제어 문자를 표현합니다.
&lt;span class=&quot;emphasis&quot;&gt;이스케이프 문자열&lt;/span&gt;은 US-ASCII에서 사용이 금지된 문자들로, 특정 문자나 데이터를 인코딩할 수 있게 함으로써 이동성과 완성도를 높였다.&lt;/p&gt;

&lt;h5 id=&quot;242-인코딩-체계&quot;&gt;2.4.2 인코딩 체계&lt;/h5&gt;

&lt;p&gt;인코딩은 안전하지 않은 문자를 퍼센티지 기호(%)로 시작해, ASCII 코드로 표현되는 두 개의 16진수 숫자로 이루어진 &lt;span class=&quot;emphasis&quot;&gt;‘이스케이프’&lt;/span&gt; 문자로 바꿉니다.&lt;/p&gt;

&lt;h5 id=&quot;243-문자-제한&quot;&gt;2.4.3 문자 제한&lt;/h5&gt;

&lt;p&gt;몇몇 문자는 URL 내에서 특별한 의미로 예약되어 있습니다.&lt;br /&gt;
어떤 문자는 US-ASCII의 출력 가능한 문자 집합에 포함되어 있지 않은 경우도 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;URL은 강력한 도구입니다. 하지만 모든 것들이 그렇듯 완벽한 것은 아닙니다. 사실 URL은 주소일뿐 실제 이름이 아닙니다. 주소란 URL이 특점 시점에 해당 리소스가 위치하는 곳을 알려준다는 뜻입니다.
이러한 스킴은 리소스가 해당 URL에서 옮겨지면 그 URL을 더이상 사용할 수 없습니다.
사람처럼 리소스의 이름을 포함하여 다른 몇 가지 정보만 있으면 리소스의 위치가 바뀌더라도 위치를 찾을 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;emphasis&quot;&gt;URN&lt;/span&gt;은 객체가 옮겨지더라도 항상 객체를 가리킬 수 있는 이름을 제공합니다.&lt;br /&gt;
&lt;span class=&quot;emphasis&quot;&gt;PURL&lt;/span&gt;은 리소스의 실제 URL 목록을 관리하고 추적하는 리소스 위치 중개 서버를 두고, 해당 리소스를 우회적으로 제공하기때문에 URL로 URN을 제공할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1trYGbVTSaoZNvNjftlzmYA57bgJyZKyv&quot; alt=&quot;first_process&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;죠의 컴퓨터 가게의 URL이 무엇인지 리소스 리졸버에게 묻습니다. 리졸버로부터 리소스의 현재 위치를 받습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1-6bvCro3toUN2fOP6cpCG3iO6j4V2TEO&quot; alt=&quot;second_process&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;실제 URL로 리소스를 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;한동안 URN 방식이 활용되었었습니다.&lt;br /&gt;
URL에서 URN으로 주소 체계를 바꾸는 것은 매우 큰 작업입니다. 표준화는 매우 중요한 작업인 만큼 느리게 진행되기때문에 URN으로 전환하기 위한 모든 것이 준비되려면 시간이 걸립니다.
웹이 성장함에 따라 사용자들은 학습을 통해 고질적인 문제를 다루는 법을 익혀왔습니다. URL은 나름 한계를 가지고 있지만, 웹 개발 커뮤니티에서 이를 가장 긴급한 사안이라고는 이야기하지 않습니다.
URL은 현재는 물론 가까운 미래에도 인터넷에 있는 리소스를 명명하는 방법이 될 것입니다. 이를 대체할 수 있는 작명 스킴이 나오기 전까지는 계속 되며 다만, URL은 그 한계를 가진 상태에서, 그것을 해결할 수 있는 새로운 표준(아마도 URN) 같은 것들이 나오고 적용될 것입니다.&lt;/p&gt;
</description>
        <pubDate>Fri, 09 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/09/http-url.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/09/http-url.html</guid>
        
        <category>http</category>
        
        <category>https</category>
        
        <category>computerscience</category>
        
        <category>url</category>
        
        
      </item>
    
      <item>
        <title>콘텐츠 미리보기 oEmbed</title>
        <description>&lt;h2 id=&quot;oembed&quot;&gt;oEmbed&lt;/h2&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;1-oembed란&quot;&gt;1. oEmbed란?&lt;/h3&gt;

&lt;p&gt;블로그에 글을 작성해보거나 콘텐츠를 만들어본 분이라면 Youtube 또는 다른 미디어 콘텐츠를 게시글에 공유해본 경험이 있을 것입니다.&lt;br /&gt;
이렇게 어떤 미디어 콘텐츠를 다른 서비스에서 공유하고자 할 때는 해당 콘텐츠의 링크를 붙여넣거나 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;태그의 소스를 붙여 넣어야합니다.&lt;br /&gt;
하지만 대부분의 커뮤니티에서는 보안상 태그의 사용을 허용하지 않습니다. 때문에 링크를 붙여넣는 방법밖에 없습니다.&lt;/p&gt;

&lt;p&gt;oEmbed는 이를 보완하기 위해 만들어진 포맷입니다. 이는 다른 사이트에서 URL을 통해 내장된 콘텐츠를 보여주는 간단한 API입니다.
공식적인 포맷은 아니지만 Youtube, Facebook 등 영향력 있는 서비스들이 참여하면서 대중화가 되고 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;2-oembed-데이터&quot;&gt;2. oEmbed 데이터&lt;/h3&gt;

&lt;p&gt;간단히 oEmbed 사용의 예를 보여드리려고합니다.&lt;br /&gt;
사용자는 oEmbed를 이용하고자하는 서비스에 아래의 Youtube url을 입력합니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://www.youtube.com/watch?v=OWrolGt94z8&amp;amp;feature=youtu.be
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Consumer( oEmbed 데이터를 요청 )는 이 url을 받아서 아래와 같은 HTTP Request를 생성합니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://www.youtube.com/oembed?url=http%3A//www.youtube.com/watch?v=OWrolGt94z8&amp;amp;format=json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Request를 받은 Provider( oEmbed 응답 )는 oEmbed response를 돌려줍니다.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;author_name&quot;:&quot;Shoot for Love \uc29b\ud3ec\ub7ec\ube0c&quot;,
  &quot;provider_name&quot;:&quot;YouTube&quot;,
  &quot;width&quot;:480,
  &quot;author_url&quot;:&quot;https:\/\/www.youtube.com\/user\/shootforloveworld&quot;,
  &quot;height&quot;:270,
  &quot;title&quot;:&quot;\uc548\uc815\ud658 45m \ud55c\uac15\ud6a1\ub2e8\uc29b. \uacf5 \uada4\uc801 \ubcf4\uc18c \u3137\u3137\u3137&quot;,
  &quot;type&quot;:&quot;video&quot;,
  &quot;version&quot;:&quot;1.0&quot;,
  &quot;thumbnail_width&quot;:480,
  &quot;provider_url&quot;:&quot;https:\/\/www.youtube.com\/&quot;,
  &quot;thumbnail_height&quot;:360,
  &quot;html&quot;:&quot;\u003ciframe width=\&quot;480\&quot; height=\&quot;270\&quot; src=\&quot;https:\/\/www.youtube.com\/embed\/OWrolGt94z8?feature=oembed\&quot; frameborder=\&quot;0\&quot; allow=\&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\&quot; allowfullscreen\u003e\u003c\/iframe\u003e&quot;,&quot;thumbnail_url&quot;:&quot;https:\/\/i.ytimg.com\/vi\/OWrolGt94z8\/hqdefault.jpg&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;3-oembed-프로젝트-&quot;&gt;3. oEmbed 프로젝트 &lt;a href=&quot;https://github.com/Jongjineee/oEmbed-project&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fab fa-github&quot;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;이번에 oEmbed의 API를 사용해보고싶어 간단한 프로젝트를 진행해보았습니다.&lt;br /&gt;
oEmbed를 지원하는 다양한 서비스들의 콘텐츠 URL을 넣으면 해당 미디어 콘텐츠를 볼 수 있고 oEmbed 데이터를 수집하여 보여주는 서비스입니다.
Ruby on Rails를 이용하여 간단히 구현해보았습니다. 데이터를 저장할 필요가 없기에 DB는 만들지 않고 진행하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://oembed.com/&quot; title=&quot;oEmbed&quot;&gt;oEmbed&lt;/a&gt;&lt;/strong&gt;에서 &lt;strong&gt;&lt;a href=&quot;https://oembed.com/providers.json&quot; title=&quot;Provider&quot;&gt;Provider&lt;/a&gt;&lt;/strong&gt;들의 데이터를 Json형식으로 제공합니다.
Provider 별로 endpoint가 다르기때문에 이를 이용해야합니다.&lt;/p&gt;

&lt;h3 id=&quot;4-과정&quot;&gt;4. 과정&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=19qtO0PuLPIcJ_r9d1hJLULc_xSPky0cc&quot; alt=&quot;oembed_url_input&quot; width=&quot;50%&quot; height=&quot;50%&quot; class=&quot;center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;사용자가 서비스에 &lt;span class=&quot;emphasis&quot;&gt;URL&lt;/span&gt;을 입력합니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1WQL5_rb7azIJKKntwhRzXVfKLCsNSv5F&quot; alt=&quot;oembed_extract_domain&quot; /&gt;&lt;/p&gt;

&lt;p&gt;사용자가 입력한 URL에서 &lt;span class=&quot;emphasis&quot;&gt;providers.json&lt;/span&gt;의 데이터와 비교할 &lt;span class=&quot;emphasis&quot;&gt;도메인&lt;/span&gt;을 추출합니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=134sVwrcidlQG2DIyKWyO_ID686Stmao6&quot; alt=&quot;oembed_compare_domain&quot; /&gt;&lt;/p&gt;

&lt;p&gt;추출한 도메인을 이용해 providers.json의 &lt;span class=&quot;emphasis&quot;&gt;“provider_url”&lt;/span&gt; 데이터와 비교합니다.&lt;br /&gt;
비교하여 해당 도메인이 존재한다면 도메인이 제공하는 &lt;span class=&quot;emphasis&quot;&gt;“url”&lt;/span&gt; 데이터를 받아옵니다.&lt;/p&gt;

&lt;p&gt;가져온 url을 이용해 providers에게 보낼 &lt;span class=&quot;emphasis&quot;&gt;endpoint&lt;/span&gt;를 만들어줍니다. 일반적으로 endpoint는 도메인/oembed 형식으로 합니다.&lt;br /&gt;
이후 &lt;span class=&quot;emphasis&quot;&gt;URL&lt;/span&gt;과 &lt;span class=&quot;emphasis&quot;&gt;format&lt;/span&gt;을 파라미터로 받습니다.&lt;br /&gt;
따라서 위에서 입력한 url의 endpoint는 아래와 같이 됩니다.&lt;br /&gt;
&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=OWrolGt94z8&amp;amp;feature=youtu.be&amp;amp;format=json&quot;&gt;https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=OWrolGt94z8&amp;amp;feature=youtu.be&amp;amp;format=json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1exlEANpM3WCLnnGlBWn7TUb2eCW0A5kR&quot; alt=&quot;oembed_page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이제 이전과 같이 해당 url의 json 데이터를 가져와 자신의 페이지에 예쁘게 뿌려주면 됩니다.&lt;br /&gt;
미리보기는 해당 url의 &lt;span class=&quot;emphasis&quot;&gt;“html”&lt;/span&gt; 부분이며 이를 String이 아닌 html 코드로 인식하도록 해주면 됩니다.&lt;/p&gt;

</description>
        <pubDate>Fri, 09 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/08/09/oembed.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/08/09/oembed.html</guid>
        
        <category>oembed</category>
        
        <category>youtube</category>
        
        <category>프로젝트</category>
        
        
      </item>
    
      <item>
        <title>HTTP 완벽가이드::HTTP란?</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1FOZcrxOvATtgpS30IUyknAumEHM9U-Y-&quot; alt=&quot;book&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이 포스팅은 책 ‘HTTP 완벽가이드’를 읽고 작성하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;http란&quot;&gt;HTTP란&lt;/h4&gt;
&lt;p&gt;     HTTP는 신뢰성 있는 데이터 전송 프로토콜을 사용하기 때문에, 데이터가 전송 중 손상되거나 꼬이지 않음을 보장합니다. 
이는 개발자에게도 이롭습니다. HTTP 통신이 전송 중 파괴되거나, 중복되거나, 왜곡되는 것을 걱정하지 않아도 되기 때문에 우리는 데이터가 통신 중 잘못되는 상황을 걱정하지 않고 기능 개발에 집중할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;웹-클라이언트와-서버&quot;&gt;웹 클라이언트와 서버&lt;/h4&gt;

&lt;p&gt;     웹 서버는 인터넷의 데이터를 저장하고, HTTP 클라이언트가 요청한 데이터를 제공합니다.
클라이언트는 서버에게 HTTP 요청을 보내고 서버는 요청된 데이터를 HTTP 응답으로 돌려줍니다. 
HTTP 클라이언트와 HTTP 서버는 월드 와이드 웹의 기본 요소입니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1h2hEPUZ6wLGnBfxxgGWKLlvmBycgokqa&quot; alt=&quot;client-server&quot; /&gt;&lt;/p&gt;

&lt;p&gt;가장 흔한 클라이언트는 크롬이나 인터넷 익스플로러와 같은 웹 브라우저입니다.&lt;br /&gt;
웹 브라우저는 서버에게 HTTP 객체를 요청하고 사용자의 화면에 보여줍니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;리소스&quot;&gt;리소스&lt;/h4&gt;

&lt;p&gt;     웹 서버는 웹 리소스를 관리하고 제공합니다. 가장 단순한 웹 리소스는 웹 서버 파일 시스템의 정적 파일입니다. 
리소스에는 정적 리소스 뿐만 아니라 동적 리소스도 있습니다. 동적 리소스는 사용자가 누구인지, 어떤 정보를 요청했는지, 몇 시 인지에 따라 다른 콘텐츠를 생성하는 것을 말합니다. 요약하자면 어떤 종류의 콘텐츠 소스도 리소스가 될 수 있다는 것입니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;미디어 타입&lt;/strong&gt; &lt;br /&gt;
     인터넷은 수천 가지의 데이터 타입을 다루기 때문에, HTTP는 웹에서 전송되는 객체 각각에 신중하게 MIME 타입이라는 데이터 포맷 라벨을 붙입니다. 이는 원래 각기 다른 전자메일 시스템 사이에서 메시지가 오갈 때 겪는 문제점을 해결하기 위해 설계 되었습니다. HTTP에서도 멀티미디어 콘텐츠를 기술하고 라벨을 붙이기 위해 채택되었습니다.
웹서버는 모든 HTTP 객체 데이터에 MIME 타입을 붙입니다. 웹 브라우저는 서버로부터 객체를 돌려받을 때, 다룰 수 있는 객체인지 MIME 타입을 통해 확인합니다.
예를들어 HTML로 작성된 텍스트 문서는 text/html 라벨이 붙고, JPEG 이미지는 image/jpeg 가 붙습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;uri&quot;&gt;URI&lt;/h4&gt;

&lt;p&gt;     웹 서버 리소스는 각자 이름을 갖고 있기 때문에, 클라이언트는 관심 있는 리소스를 지목할 수 있습니다. 서버 리소스 이름은 URI로 불립니다. 이는 정보 리소스를 고유하게 식별하고 위치를 지정할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1D5IdjQoIy5wlrg9VzLFAqnZ8kkKd9Ggx&quot; alt=&quot;http-uri&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt; &lt;br /&gt;
     URL은 리소스 식별자의 가장 흔한 형태입니다. URL은 특정 서버의 한 리소스에 대한 구체적인 위치를 서술합니다.
URL의 첫 번째 부분은 스킴이라고 불리는데, 리소스에 접근하기 위해 사용되는 프로토콜을 서술합니다. 보통 HTTP 프로토콜입니다.
두 번째 부분은 서버의 인터넷 주소를 제공합니다.
마지막은 웹 서버의 리소스를 가리킵니다.
&lt;br /&gt;
&lt;br /&gt;
오늘날 대부분의 URI는 URL입니다!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;트랜잭션&quot;&gt;트랜잭션&lt;/h4&gt;

&lt;p&gt;     HTTP 트랜잭션은 요청 명령과 응답 결과로 구성되어 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;메서드&quot;&gt;메서드&lt;/h4&gt;

&lt;p&gt;     HTTP 요청 메시지는 한 개의 메서드를 갖습니다. 메서드는 서버에게 어떤 동작이 취해져야 하는지 말해줍니다.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;서버에서 클라이언트로 지정한 리소스를 보내라&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;클라이언트에서 서버로 보낸 데이터를 지정한 이름의 리소스로 저장하라&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;지정한 리소스를 서버에서 삭제하라&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;클라이언트 데이터를 서버 게이트웨이 애플리케이션으로 보내라&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;저장한 리소스에 대한 응답에서, HTTP 헤더 부분만 보내라&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;상태-코드&quot;&gt;상태 코드&lt;/h4&gt;

&lt;p&gt;     HTTP 응답 메시지는 상태 코드와 함께 반환됩니다. 상태 코드는 클라이언트에게 요청이 성공했는지 아니면 추가 조치가 필요한지 알려주는 세 자리 숫자입니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;메시지&quot;&gt;메시지&lt;/h4&gt;

&lt;p&gt;     HTTP 메시지는 단순한 줄 단위의 문자열입니다. 이진 형식이 아닌 일반 텍스트이기 때문에 사람이 읽고 쓰기 쉽습니다.
클라이언트에서 웹 서버로 보낸 HTTP 메시지를 요청 메시지, 서버에서 클라이언트로 가는 메시지는 응답 메시지라고 부릅니다.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;tcpip&quot;&gt;TCP/IP&lt;/h4&gt;

&lt;p&gt;     HTTP는 애플리케이션 계층 프로토콜입니다. HTTP는 네트워크 통신의 핵심적인 세부사항에 대해서 신경쓰지 않고, 대신 TCP/IP에게 맡깁니다.
TCP는 다음을 제공합니다.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;오류없는 데이터 전송&lt;/li&gt;
  &lt;li&gt;순서에 맞는 전달(데이터는 언제나 보낸 순서대로 도착한다.)&lt;/li&gt;
  &lt;li&gt;조각나지 않는 데이터 스트림 (언제든 어떤 크기로든 보낼 수 있다.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;인터넷은 이 TCP/IP에 기초하고 있습니다. 
TCP/IP는 각 네트워크와 하드웨어의 특성을 숨기고, 어떤 종류의 컴퓨터나 네트워크든 서로 신뢰성 있는 의사소통을 하게 해줍니다.&lt;/p&gt;

&lt;p&gt;더 자세한 내용은 &lt;strong&gt;&lt;a href=&quot;https://jongjineee.github.io/datascience/2018/12/31/tcp_ip.html&quot; title=&quot;TCP/IP&quot;&gt;게시글&lt;/a&gt;&lt;/strong&gt;을 참고하세요 :)&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;tcpip-커넥션&quot;&gt;TCP/IP 커넥션&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;웹브라우저는 서버의 URL 에서 호스트 명을 추출한다.&lt;/li&gt;
  &lt;li&gt;웹브라우저는 서버의 호스트 명을 IP로 변환한다.&lt;/li&gt;
  &lt;li&gt;웹브라우저는 URL에서 포트번호를 추출한다.&lt;/li&gt;
  &lt;li&gt;웹브라우저는 웹 서버와 TCP 커넥션을 맺는다.&lt;/li&gt;
  &lt;li&gt;웹브라우저는 서버에 HTTP 요청을 보낸다.&lt;/li&gt;
  &lt;li&gt;서버는 웹브라우저에 HTTP 응답을 돌려준다.&lt;/li&gt;
  &lt;li&gt;커넥션이 닫히면, 웹브라우저는 문서를 보여준다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;웹의-구성요소&quot;&gt;웹의 구성요소&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;프락시&lt;/code&gt;&lt;/strong&gt; 클라이언트와 서버 사이에 위치한 HTTP 중개자&lt;br /&gt;
프락시는 클라이언트와 서버 사이에 위치하여, 클라이언트의 모든 HTTP 요청을 받아 서버에 전달합니다. 
응답과 요청의 필터링 역할을 하며 주로 보안을 위해 사용됩니다. 즉, 모든 웹 트래픽 흐름 속에서 신뢰할 만한 중개자 역할을 합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;캐시&lt;/code&gt;&lt;/strong&gt; 많이 찾는 웹페이지를 클라이언트 가까이에 보관하는 HTTP 창고&lt;br /&gt;
자신을 거처가는 문서들 중 자주 찾는 것의 사본을 저장해둡니다. 클라이언트는 멀리 떨어진 웹 서버보다 근처의 캐시에서 훨씬 더 빨리 문서를 다운 받을 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;게이트웨이&lt;/code&gt;&lt;/strong&gt; 다른 애플리케이션과 연결된 특별한 웹서버&lt;br /&gt;
게이트웨이는 주로 HTTP 트래픽을 다른 프로토콜로 변환하기 위해 사용됩니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;터널&lt;/code&gt;&lt;/strong&gt; 단순히 HTTP 통신을 전달하기만 하는 특별한 프락시&lt;br /&gt;
터널은 두 커넥션 사이에서 데이터를 열어보지 않고 그대로 전달해줍니다. 
이를 활용하는 대표적인 예로 암호화된 SSL 트래픽을 HTTP 커넥션으로 전송함으로써 웹 트래픽만 허용하는 사내 방화벽을 통과시킵니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;에이전트&lt;/code&gt;&lt;/strong&gt; 자동화된 HTTP 요청을 만드는 준지능적 웹클라이언트 (봇)&lt;/p&gt;

</description>
        <pubDate>Thu, 18 Jul 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/07/18/http-opening.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/07/18/http-opening.html</guid>
        
        <category>http</category>
        
        <category>https</category>
        
        <category>computerscience</category>
        
        
      </item>
    
      <item>
        <title>레일즈 Joins VS Includes</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1o28Np0nb4uQRGblbpKkw7dmlyiuHiaVd&quot; alt=&quot;ruby&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;active-record&quot;&gt;Active Record&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;액티브레코드는 관계형데이터베이스(RDBMS)의 테이블을 객체로 연결(ORM : Object Relational Mapping)해서
네이티브 데이터베이스 SQL을 사용하지 않고도 데이터를 조작할 수 있도록 다양한 메소드를 제공해 줍니다.
액티브레코드에는 개발자가 쉽게 데이터를 조작할 수 있도록 도와주는 많은 메소드가 있습니다.&lt;br /&gt;
이 액티브레코드의 메소드들 중 Includes와 Joins에 대해서 공부한 내용을 정리했습니다.
Includes와 Joins 메소드는 결과가 비슷하여 둘의 차이를 잘 모르고 사용하는 경우가 많습니다.&lt;/p&gt;

&lt;h2 id=&quot;joins와-includes의-차이&quot;&gt;Joins와 Includes의 차이&lt;/h2&gt;
&lt;hr /&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;status: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;joins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;age&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;첫행에서 joins 메소드를 사용하여 users를 검색합니다.&lt;br /&gt;
이후 do 루프에서 user의 name과 age를 받습니다.
위 코드는 얼핏보기에 이상한 점이 없으며 결과또한 별다른 에러 없이 잘 나옵니다.
하지만 이 코드를 실행해보면 처음 users를 검색하면서 1번의 쿼리가 실행되고, 이후 do 루프를 통해 N번의 쿼리가 실행됩니다.
이러한 로딩 방식을 lazy loading이라고 부르며 이로인해 발생하는 문제를 N+1 문제라고 부릅니다.&lt;/p&gt;

&lt;p&gt;N+1 문제는 데이터의 규모에 따라 심각한 문제가 될 수 있습니다. users의 수가 100개라고 하면 101번의 쿼리가 실행되는데
이는 속도의 차이를 별로 못느낄 수 도 있겠지만 그 수가 10만명만 되어도 쿼리 속도에 문제가 있다는 것을 금방 알아차릴 수 있습니다.
한번 코드를 실행하면 10만 1번의 쿼리를 실행하게 되니까요.&lt;/p&gt;

&lt;p&gt;Rails에서는 이러한 문제를 해결하기위해 Includes라는 메소드를 제공해줍니다.
Rails의 공식문서를 보면 Inlcludes에 대해서 아래와 같이 설명합니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h1 id=&quot;with-includes-active-record-ensures-that-all-of-the-specified-associations-are-loaded-using-the-minimum-possible-number-of-queries&quot;&gt;With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;어째서 위와같이 쿼리를 줄일 수 있다는 것인지 위에서 사용한 Joins의 자리에 Includes를 사용해보겠습니다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;status: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;age&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;위 코드를 실행해보면 두번의 쿼리가 발생합니다.&lt;br /&gt;
users 데이터를 찾을 때 한번, users에 대한 profile을 찾는데 한번의 쿼리가 발생합니다.
이러한 로딩 방식을 eager loading이라고 부릅니다.&lt;/p&gt;

&lt;p&gt;모든 상황에서 Incldues가 Joins보다 효율적이라면 Joins 메소드가 있을 이유가 없을 것입니다.&lt;br /&gt;
Joins 메소드가 Includes 보다 효율적으로 사용되는 경우를 알아봅시다.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;joins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'profile.status = ?'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'active'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Joins와 Includes를 설명하면서 보여드린 코드와는 조금 다릅니다.&lt;br /&gt;
첫행에서 ‘active’ 상태의 users를 검색하고 do 루프에서는 profile의 데이터를 사용하지 않습니다.
profile은 단지 filter의 역할을 하는 것입니다.&lt;/p&gt;
</description>
        <pubDate>Thu, 18 Jul 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/07/18/includes_joins.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/07/18/includes_joins.html</guid>
        
        <category>joins</category>
        
        <category>includes</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        <category>루비</category>
        
        <category>레일즈</category>
        
        
      </item>
    
      <item>
        <title>2019 상반기 회고 &amp; 글또 3기 시작</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1FOZcrxOvATtgpS30IUyknAumEHM9U-Y-&quot; alt=&quot;book&quot; /&gt;&lt;/p&gt;

&lt;p&gt;지난 상반기에는 꽤 이것저것 하고자 했던 것이 많았다. 그중에는 이룬 것도 있고 실패한 것도 있었다.&lt;br /&gt;
욕심이 많고 누리고 싶은 것도 많지만 아직도 꿈만 아주 열심히 꾸고 있다.&lt;/p&gt;

&lt;h3 id=&quot;-스터디&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 스터디&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;가장 먼저 시작한 것은 스터디였다. 새해가 시작되고 나름 의욕적으로 리액트 스터디를 하게 되었다.&lt;br /&gt;
승근님의 도움으로 스터디를 시작하였고 초반에는 꽤 많은 것들을 배웠다.&lt;br /&gt;
그렇게 몇 개월은 퇴근하고 카페에 가서 졸음이 쏟아질 때까지 공부했던 것 같다.&lt;br /&gt;
아쉽게도 중간에 해산하게 되었지만 배운 것들이 많았고 그때 배운 오픈소스 공부 방법은 아직도 유용하게 사용하고 있다.&lt;br /&gt;
조만간 다시 스터디원들을 만나고 싶다. 개발자들과 개발 이야기를할 때면 어릴 적 게임 이야기를 신나게 하던 내가 보인다.&lt;/p&gt;

&lt;h3 id=&quot;-운동&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 운동&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;욕심이 많았다. 개발자는 6시에 제발 퇴근을 해달라고 말해도 퇴근을 하지 않는다.&lt;br /&gt;
하던 일이 있고 마무리를 하지 않으면 찝찝해서 계속 생각난다. 또 다음날이 무서워서 그런 것일 수도 있다.&lt;br /&gt;
그렇게 늦은 시간 퇴근을 하고 스터디를 한 후 마무리로 헬스장을 갔다.&lt;br /&gt;
샤워 후 밤 11시가 넘어 버스에 오르면 쓰러지듯 창문에 머리를 기대고 잠이 들었다.&lt;br /&gt;
며칠을 무리하니 도저히 버티지 못할 것 같아 운동을 포기했다.&lt;br /&gt;
여유가 있으면 다시 시작해봐야겠다.&lt;/p&gt;
&lt;h5 id=&quot;누군가-개발자의-3요소-중-하나가-불룩-나온-배라고-했었다&quot;&gt;&lt;del&gt;누군가 개발자의 3요소 중 하나가 불룩 나온 배라고 했었다…&lt;/del&gt;&lt;/h5&gt;

&lt;h3 id=&quot;-운전면허--정보처리기사&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 운전면허 &amp;amp; 정보처리기사&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;고집이 똥고집이라 친구들이 모두 운전면허를 발급받을 때&lt;br /&gt;
‘운전할 차도 없으면서 무슨 자격증!’이라며 펑펑 알바비를 썼다.&lt;br /&gt;
덕분에 스물일곱의 나이에도 운전면허증이 없었다. 지금에서 결국 땄지만 몇차례의 귀중한 주말 오전을 희생하였다.
면허증을 찾으러 가야 하는데 귀찮아서 찾지 못하고 있다.&lt;/p&gt;

&lt;p&gt;정보처리기사는 취업을 준비할 때 무엇을 해야 할지 모르겠어서 일단 도전했었다.&lt;br /&gt;
그냥 외우는 식의 공부에는 흥미가 없어서 실기시험에서 보기 좋게 떨어졌다.&lt;br /&gt;
이후 스터디가 와해되고 방황할 때 필기합격이 유지되는 기간이 얼마 남지 않아서 퇴근하고 공부를 했다.&lt;br /&gt;
솔직히 많이 하지도 않았고 지금 하는 일을 멋진 문장으로 포장하여 외우는 것이라 어렵지 않게 땄다.
그래도 시험 전날에는 밤을 새웠다.&lt;/p&gt;

&lt;h3 id=&quot;-글또-3기&quot;&gt;&lt;i class=&quot;fab fa-gripfire&quot; style=&quot;color :#800000&quot;&gt;&lt;/i&gt; 글또 3기&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;전공과는 전혀 상관이 없는 개발자가 되어 주변에 이야기를 나눌 개발자가 없었다.&lt;br /&gt;
회사에서라도 같이 일을 하는 개발자가 있으면 좋으련만 혼자서 서비스를 운영하고 개발하려니&lt;br /&gt;
투덜투덜 불만만 늘어가고 있었다.&lt;br /&gt;
그러던 중 우연히 성윤님을 팔로우해놓아서 글또 3기를 모집한다는 글을 보게 되었다.&lt;br /&gt;
얼른 지원서를 작성하여 제출했다. 정말 참여하고 싶어서 꽤 고민하고 정성 들여 작성하였다.&lt;/p&gt;

&lt;p&gt;블로그가 중요하다는 것은 나도 경험해봤기 때문에 잘 알고 있는 사실이다.&lt;br /&gt;
하지만 자꾸 미루게 된다. 블로그에 올리고 싶은 소재들은 열심히 적어놓고 정작 퇴근하고는
다시 일을 하고 있다.&lt;/p&gt;

&lt;p&gt;아무튼 운 좋게 모임에 참여하게 되었고 OT를 갔다 왔다.&lt;br /&gt;
잠실역의 ‘우아한형제들 작은집’에서 진행이 된다고 하여 잔뜩 기대했다.&lt;br /&gt;
큰집도 가보고 싶었지만, 작은집도 나를 자극하기에는 충분했던 것 같다.&lt;/p&gt;

&lt;p&gt;오랜만에 개발자분들이랑 모여서 이야기를 하는데 처음에는 잔뜩 긴장을하고 어색해서&lt;br /&gt;
무슨 말을 해야 할 지 몰랐다.&lt;br /&gt;
글또 모임에 와서 당연하겠지만 지난 글또 2기에서는 어떤 식으로 진행이 되었고 글쓰기가
부담이 되지 않는지 등의 대화 주제가 오갔다.&lt;br /&gt;
어느새 개발이야기와 각자 기업문화 등의 주제로 넘어갔고 
어색한 분위기는 금방 사라졌다. 다들 신나게 이야기하는 모습이 좋아서 넋을 놓고 구경을 하기도 했다.&lt;/p&gt;

&lt;p&gt;기대하던 글또 3기가 시작되었고 이제 어떤 글을 쓸지 고민하고 슬슬 써봐야겠다.&lt;br /&gt;
꼭 완주하여 다음 4기, 5기 그 이후에도 참여하여 좋은 개발자분들과 좋은 관계를 이어가고 싶다.&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Jul 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2019/07/04/geultto.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2019/07/04/geultto.html</guid>
        
        <category>글또</category>
        
        <category>블로그</category>
        
        <category>개발 블로그</category>
        
        
      </item>
    
      <item>
        <title>Study Plan</title>
        <description>&lt;h2 id=&quot;2019-study-plan&quot;&gt;2019 STUDY PLAN&lt;/h2&gt;

&lt;h3 id=&quot;-수업방침&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 수업방침&lt;/h3&gt;
&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;학습은 영문 공식 문서를 이용한다.&lt;/li&gt;
  &lt;li&gt;예제는 해당 기술 GitHub repository의 examples를 이용한다.&lt;/li&gt;
  &lt;li&gt;스터디는 자족적인 커뮤니티이다. 멤버들은 서로 기술적으로 토론하고 발표하고 뉴스를 공유한다.&lt;/li&gt;
  &lt;li&gt;발표, 블로그 등의 컨텐츠를 각자 만들어서 외부에 공개한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-수업-자료&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 수업 자료&lt;/h3&gt;
&lt;hr /&gt;

&lt;h4 id=&quot;typescript&quot;&gt;TypeScript&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html&quot;&gt;http://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.ht ml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;react&quot;&gt;React&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://reactjs.org/tutorial/tutorial.html&quot;&gt;https://reactjs.org/tutorial/tutorial.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;redux&quot;&gt;Redux&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://redux.js.org/&quot;&gt;https://redux.js.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://redux-saga.js.org/&quot;&gt;https://redux-saga.js.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/reduxjs/redux/tree/master/examples&quot;&gt;
  https://github.com/reduxjs/redux/tree/master/example
&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/redux-saga/redux-saga/tree/master/examples&quot;&gt;
  https://github.com/redux-saga/redux-saga/tree/master/examples
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;nextjs&quot;&gt;Next.js&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/&quot;&gt;https://nextjs.org/learn/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/zeit/next.js/tree/canary/examples&quot;&gt;
  https://github.com/zeit/next.js/tree/canary/examples&lt;br /&gt;
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-수업-방식&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 수업 방식&lt;/h3&gt;
&lt;hr /&gt;

&lt;p&gt;전반부, 후반부 한 시간 씩 기본 두 시간으로 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;[전반부] 수업 전에 공식 문서와 example을 각자 분석해온 후 Q&amp;amp;A 시간을 갖는다.&lt;/li&gt;
  &lt;li&gt;[후반부] 매주 1~2 명이 주제 발표를 진행한 후 토론한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;-수업-외-활동&quot;&gt;&lt;i class=&quot;fa fa-bolt&quot; style=&quot;color:#01077c&quot;&gt;&lt;/i&gt; 수업 외 활동&lt;/h3&gt;
&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;매일 오픈소스에서 인상깊게 본 프로젝트를 공유하고 그 이유를 설명한다. GitHub의 Follower 들의 활동이나 discover 기능을 이용한다.&lt;/li&gt;
  &lt;li&gt;(예정) 인상깊게 본 오픈소스 프로젝트의 소스 코드 일부분을 공유하고 그 이유를 설명한다.&lt;/li&gt;
  &lt;li&gt;(예정) 각자 블로그를 작성한다.&lt;/li&gt;
  &lt;li&gt;(예정) 외부 발표에 발표자로 참여한다.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 11 Jan 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/etc/2019/01/11/study_plan.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/etc/2019/01/11/study_plan.html</guid>
        
        
        <category>Etc</category>
        
      </item>
    
      <item>
        <title>No space left on device</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1BaMo6RSXrVHVd_5baZIr79FOkIKh6aGN&quot; alt=&quot;error&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;cannot-write-no-space-left-on-device&quot;&gt;Cannot write: No space left on device&lt;/h2&gt;

&lt;h3 id=&quot;error&quot;&gt;Error&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;Staging 서버를 구축하고 서버에서 테스트를 진행하면서 열심히 개발을 하던 중이었습니다.
로컬에서 먼저 테스트를 하면서 ‘이제 됐다!’ 하고 깃허브에 신나게 커밋을 날리고!
이제 Staging 서버에서 테스트를 해보기위해서 capistrano를 이용해 배포를 진행할 때!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ cannot create temp file for here-document: No space left on device&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;라는 에러를 만났습니다… 
처음에는 Staging 서버의 EC2서버 EBS의 용량이 부족한 줄 알고 AWS에서 확인했는데 20G 였습니다.
이정도면 충분한 용량인데 어디서 이 용량을 다 사용했는지 알아보기로 했습니다.&lt;/p&gt;

&lt;h3 id=&quot;df--du&quot;&gt;df &amp;amp; du&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;리눅스에서 파티션별 저장공간의 사용량을 알고 싶다면 df 명령어를 사용합니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ df -h&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위와 같이 -h를 함께 사용하면 사람이 읽을 수 있는 형태의 크기로 출력(예:1K, 512M, 4G)해줍니다!&lt;br /&gt;
옵션으로는 여러가지가 있으니 본인이 편한 옵션을 사용하시면 되겠습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1NTHc8heC675Jrrn9RrE_fIcaEzC8CZfw&quot; alt=&quot;df&quot; /&gt;&lt;/p&gt;

&lt;p&gt;제 파티션의 용량을 확인해보고 표시한 부분과 같이 서버의 사용량이 100%가 된 것을 볼 수 있습니다. 
staging 서버이고 최근에 생성했기 때문에 정상적인 현상이 아닙니다. 
어느 디렉토리에서 어떤 일이 일어났는지 알아봐야합니다.&lt;/p&gt;

&lt;p&gt;du 명령어는 disk usage의 약자로 디렉토리(폴더)와 파일의 용량을 출력해주는 명령어입니다.
du 명령어에도 많은 옵션이 있습니다. 필요한 옵션을 찾아서 사용해주시면 됩니다. 
저는 현재경로에서부터 가장 용량을 많이 사용하는 디렉토리 10개를 뽑아봤습니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ du -hsx * | sort -rh | head -n 10&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;경로를 살펴본 결과 cron.log에 오류 log들이 쌓여있었습니다.
rails whenever의 작업 중에 서버에 모이는 메시지들을 모았다가 1분에 한번씩 뿌려주는 작업이 있는데 
staging서버를 구축하면서 해당 기능을 잊고있었습니다.&lt;/p&gt;

&lt;p&gt;cron.log의 log들을 모두 삭제해주고&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ if @environment == &quot;production&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;이와 같은 코드를 이용해 환경을 분리해주어 staging 서버에서는 해당 작업을 실행시키지 않도록 했습니다. :)
이런 일이 서비스 서버에서는 발생하지 않도록 서버의 용량을 잘 확인해야할 것 같습니다…ㅠㅠ&lt;/p&gt;
</description>
        <pubDate>Fri, 11 Jan 2019 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/datascience/2019/01/11/linux_du.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/datascience/2019/01/11/linux_du.html</guid>
        
        <category>linux</category>
        
        <category>리눅스</category>
        
        <category>du</category>
        
        <category>no space</category>
        
        <category>error</category>
        
        
        <category>DataScience</category>
        
      </item>
    
      <item>
        <title>TCP/IP</title>
        <description>&lt;h2 id=&quot;tcpip&quot;&gt;TCP/IP&lt;/h2&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;1-tcpip&quot;&gt;1. TCP/IP&lt;/h3&gt;

&lt;p&gt;컴퓨터와 네트워크 기기가 상호간에 통신하기 위해서는 서로 같은 방법으로 통신을 해야합니다.
때문에 서로간에 그 규칙이 필요합니다. 이를 &lt;span class=&quot;emphasis&quot;&gt;프로토콜&lt;/span&gt;이라고합니다.
TCP/IP를 찾아보면 계층에 대한 설명이 항상 따라옵니다. 앞선 포스팅에서 OSI 7계층에 대해서 설명했는데요.
OSI 7계층은 네트워크 전송 시 데이터 표준을 정리한 것입니다. 이 이론을 실제 사용하는 인터넷 표준이 
TCP/IP 4계층입니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1rP784KDDn1-iz0hAh_IAslhxpz3jkI0v&quot; alt=&quot;tcp_ip&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이렇게 계층화를 하면 하나의 사양을 변경할 시, 해당 계층만 변경해도 문제를 해결할 수 있습니다.
각 계층은 연결되어 있는 부분만 결정되어 있기 때문에 각 계층의 내부는 자유롭게 설계할 수 있습니다.&lt;/p&gt;

&lt;p&gt;제일 위에서부터 사용자가 HTTP를 통해 서버에 Request를 보내려합니다. 
이때 한가지 소개할 부분이 또 생깁니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;2-소켓socket&quot;&gt;2. 소켓(Socket)&lt;/h3&gt;

&lt;p&gt;프로토콜에는 내부 메모리가 있습니다. 이 메모리에 통신 동작을 제어하기 위한 제어정보를 기록합니다.
이 메모리의 영역을 소켓이라고 할 수 있습니다. 대표적인 제어 정보로 상대의 IP 주소, 포트번호, 통신 동작의
진행상태 등이 포함됩니다. 
데이터의 송수신은 &lt;span class=&quot;emphasis&quot;&gt;‘데이터 통로’&lt;/span&gt;를 통해 수행됩니다. 이 ‘데이터 통로’의 역할을 소켓이 하는 것입니다. 
이 소켓(데이터 통로)를 연결하는 출입구의 역할을 &lt;span class=&quot;emphasis&quot;&gt;‘포트’&lt;/span&gt;가 합니다.&lt;/p&gt;

&lt;p&gt;소켓이 생성되면서 &lt;span class=&quot;emphasis_blue&quot;&gt;디스크립터&lt;/span&gt;라는 것이 생성됩니다. 디스크립터는 소켓을 식별하는 역할을 합니다. 
소켓을 식별하는 이유는 동시에 여러 개의 소켓이 생길 수 있기 때문입니다.
소켓이 하나라면 우리는 사이트 하나를 방문할 때 동시에 다른 사이트를 방문하지 못하게 됩니다. 
이렇게 여러 웹 서버와의 소켓을 식별하도록 디스크립터가 생성되는 것입니다.&lt;/p&gt;

&lt;p&gt;자 이제 소켓이 생성되고 디스크립터가 소켓을 식별했습니다. 상대 웹 서버에게 데이터가 도달했습니다. 
여기까지의 과정을 &lt;span class=&quot;emphasis&quot;&gt;‘접속’&lt;/span&gt;이라고 합니다.
소켓의 역할은 여기서 끝이 아닙니다. 서버 측에 요청을 보냈고 응답이 오게되면 소켓은 수신 동작을 합니다.
응답해온 데이터를 애플리케이션 내부의 버퍼에 저장하고 데이터 송수신이 끝나면 연결을 끊습니다.&lt;/p&gt;

&lt;p&gt;소켓이 접속 동작을 마치면 데이터를 전달해야합니다. 
HTTP 리퀘스트 메시지로 패킷을 전달합니다. 
&lt;span class=&quot;emphasis&quot;&gt;패킷&lt;/span&gt;은 데이터를 일정한 크기로 자른 단위로 인터넷에서 정보를 전달하는 단위입니다. 
나누어진 패킷이 순서대로 도착한다는 보장이 없기 때문에 규칙이 필요합니다. &lt;span class=&quot;emphasis&quot;&gt;TCP&lt;/span&gt;는 이 패킷을 재조립하고
패킷에 손상이 있거나 손실된 패킷이 있다면 재전송을 요청합니다.&lt;/p&gt;

&lt;p&gt;앞서 말씀드렸듯 TCP에는 패킷이 올바르게 도착하지 않았으면 재전송을 요청합니다. 요청을 받은 TCP는 다시
전송을 합니다. 따라서 TCP는 패킷을 송신한 후에 확인 동작을 합니다.
TCP가 데이터를 패킷으로 나눌 때 해당 조각이 몇번째 바이트에 해당하는지를 세어둡니다.
이후 세어둔 값을 TCP 헤더에 기록합니다. 이를 &lt;span class=&quot;emphasis&quot;&gt;시퀀스 번호&lt;/span&gt;라고 합니다.
이 시퀀스 번호는 &lt;span class=&quot;emphasis&quot;&gt;ACK 번호&lt;/span&gt;가 됩니다. 송신측에서 ACK 번호를 TCP 헤더에 기록하여 보내면 수신측에서는
이 수신된 ACK 번호를 기록하여 송신측에게 알려줍니다. 이러한 방식을 이용하면 누락된 데이터가 있는지 
확인할 수 있습니다. 이러한 동작을 &lt;span class=&quot;emphasis_blue&quot;&gt;수신 확인 응답&lt;/span&gt;이라고 합니다.&lt;/p&gt;

&lt;p&gt;수신측에서 데이터를 받아 문제가 없으면 ACK 번호를 송신측에 다시 보내주고 데이터 조각을 수신 버퍼에
일시적으로 보관합니다. 이후 패킷을 연결하여 데이터를 원래 모습으로 복원한 뒤 애플리케이션에게
건네줍니다.&lt;/p&gt;

&lt;p&gt;이제 클라이언트와 서버 간의 데이터 송수신 과정이 끝났습니다. 이 과정이 끝나면 &lt;span class=&quot;emphasis_blue&quot;&gt;연결 끊기 단계&lt;/span&gt;에
들어갑니다. 서버 측에서 TCP헤더를 만들고 여기에 연결 끊기를 나타내는 &lt;span class=&quot;emphasis&quot;&gt;FIN 비트&lt;/span&gt;에 1을 설정합니다.
이후 IP에 의뢰하여 클라이언트에 송신합니다.&lt;/p&gt;

&lt;p&gt;클라이언트는 FIN이 1로 변경된 TCP 헤더를 받고 자신의 소켓에 상대방이 연결을 끊는 동작에 들어갔다고
알립니다. 패킷을 받았다는 ACK를 반송하고 애플리케이션에 데이터를 건네줍니다. 
서버에서 보낸 데이터를 전부 수신했다는 사실을 브라우저에게 알리고 서버와 똑같이 FIN을 1로 설정 후 TCP
헤더를 만들어 보내고 서버에서 ACK 번호가 돌아오면 서버와의 연결이 끝이 납니다.&lt;/p&gt;

</description>
        <pubDate>Mon, 31 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/datascience/2018/12/31/tcp_ip.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/datascience/2018/12/31/tcp_ip.html</guid>
        
        <category>os</category>
        
        <category>tcp</category>
        
        <category>ip</category>
        
        <category>tcp ip</category>
        
        
        <category>DataScience</category>
        
      </item>
    
      <item>
        <title>3 Way Handshake &amp; 4 Way Handshake</title>
        <description>&lt;h2 id=&quot;3-way-handshake--4-way-handshake&quot;&gt;3 Way Handshake &amp;amp; 4 Way Handshake&lt;/h2&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;1-3-way-handshake&quot;&gt;1. 3 Way Handshake&lt;/h3&gt;

&lt;p&gt;앞선 포스팅에서 용어의 설명을 제외하고 TCP 통신의 과정에 대해 이야기했습니다. &lt;br /&gt;
TCP 통신을 위해 네트워크를 연결해야합니다. 이 연결의 과정을 &lt;span class=&quot;emphasis&quot;&gt;3 Way Handshake&lt;/span&gt;라고 부릅니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1vLCxFe3PdepG8PEvbpFsl-hR_8vz8z6N&quot; alt=&quot;3way_handshake&quot; /&gt;&lt;/p&gt;

&lt;p&gt;그림을 보면 이해하기 쉬울 것 같습니다. &lt;br /&gt;
먼저 서버의 포트는 열려있는 상태이며 &lt;span class=&quot;emphasis&quot;&gt;LISTEN&lt;/span&gt; 상태라고 합니다. 클리이언트는 &lt;span class=&quot;emphasis&quot;&gt;Closed&lt;/span&gt; 상태입니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;1. 클라이언트에서 서버에 연결 요청을 하기위해 SYN 패킷을 보냅니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;2. 서버는 클라이언트의 SYN 패킷을 받고 클라이언트에게 요청을 수락한다는 ACK와 클라이언트도 포트를 열어달라는 SYN 패킷을 전송합니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;3. 클라이언트는 서버의 수락 응답을 받고 상태를 ESTABLISHED로 변경합니다. 서버에 요청을 잘 받았다는 ACK를 보냅니다. 클라이언트의 ACK를 받은 서버는 ESTABLISHED로 상태를 변경합니다.&lt;/p&gt;

&lt;p&gt;위와 같이 세 번의 통신이 정상적으로 이루어지면, 서로의 포트가 &lt;span class=&quot;emphasis&quot;&gt;ESTABLISHED&lt;/span&gt; 되면서 연결이 됩니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;1-4-way-handshake&quot;&gt;1. 4 Way Handshake&lt;/h3&gt;

&lt;p&gt;이제 연결을 해제하는 방식을 알아보겠습니다. 해제하는 과정은 4 Way Handshake라고 부릅니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1mVFFOII0tptCEJA5_tXOBtNKBoYbc1LH&quot; alt=&quot;4way_handshake&quot; /&gt;&lt;/p&gt;

&lt;p&gt;먼저 클라이언트와 서버는 3 Way Handshake로 연결을 하고 데이터를 주고 받은 상태입니다. &lt;span class=&quot;emphasis&quot;&gt;(ESTABLISHED)&lt;/span&gt;&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;1. 이후 클라이언트는 연결을 종료하겠다는 FIN 플래그를 전송합니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;2. 서버는 클라이언트의 FIN 플래그를 받고 알겠다는 확인 ACK를 보냅니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;3. 데이터를 모두 보내고 통신이 끝났으면 연결이 종료되었다고 클라이언트에게 FIN 플래그를 전송합니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;4. 클라이언트는 FIN 메시지를 확인했다는 ACK 를 보냅니다.&lt;/p&gt;

&lt;p class=&quot;indent&quot;&gt;5. 클라이언트의 ACK 메시지를 받은 서버는 소켓 연결을 CLOSE 합니다.&lt;/p&gt;

&lt;p&gt;TCP 헤더에는 총 6비트로 되어있는 Code Bit(Flag bit)가 존재합니다. 하나의 비트들이 모두 의미를 갖습니다.
Urg-Ack-Psh-Rst-Syn-Fin 순서로 되어 있으며 해당 위치의 비트가 1이면
해당 패킷은 그 비트의 의미를 갖습니다.
예를들어 SYN 패킷일 경우 000010 이 되고 ACK 패킷은 010000이 됩니다.&lt;/p&gt;
</description>
        <pubDate>Mon, 31 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/datascience/2018/12/31/hand_shake.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/datascience/2018/12/31/hand_shake.html</guid>
        
        <category>datascience</category>
        
        <category>3 way handshake</category>
        
        <category>4 way handshake</category>
        
        <category>os</category>
        
        
        <category>DataScience</category>
        
      </item>
    
      <item>
        <title>스테이징 서버 구축</title>
        <description>&lt;h2 id=&quot;staging-server-구축하기&quot;&gt;Staging Server 구축하기!&lt;/h2&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;1-staging-server-structure&quot;&gt;1. Staging Server Structure&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;EC2 생성 - 기존 EC2 서버와 동일한 환경으로 생성합니다.
    &lt;ul&gt;
      &lt;li&gt;Ubuntu 16.04 LTS 프리티어&lt;/li&gt;
      &lt;li&gt;스토리지(8)&lt;/li&gt;
      &lt;li&gt;기존 그룹(Web Server - HTTP, SSH, HTTPS)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;S3는 API로 접속하는 개념으로 AWS KEY가 있으면 접속 가능(pem key)&lt;/li&gt;
  &lt;li&gt;RDS(DB)는 Production의 RDS 스냅샷을 복원하여 사용합니다.&lt;/li&gt;
  &lt;li&gt;Elastic Search 또한 접속의 개념으로 호스트 주소를 입력하여 접속합니다. RDS 또한 호스트 주소를 입력합니다.&lt;/li&gt;
  &lt;li&gt;각 서버에 대한 host와 password 등은 credential에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1fGYbd6ESE1GxbwlLcIxPfe0YovQVqSjh&quot; alt=&quot;staging_server_structure&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AWS - Elastic IP &lt;br /&gt; EC2를 생성하면 할당되는 IP주소는 서버를 껐다가 킬때마다 변하기 때문에 &lt;br /&gt;Elastic IP를 통해 고정 IP주소를 생성합니다.&lt;/li&gt;
  &lt;li&gt;고정된 IP 받아 EC2의 IP를 넣어줬으면 DNS에서 IP를 사용하고자 하는 도메인에 할당합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;2-서버-접속&quot;&gt;2. 서버 접속&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ ssh [user]@[도메인]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;SSH를 통한 접속은 ssh [user]@[도메인]의 형식으로 이루어 집니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1fGYbd6ESE1GxbwlLcIxPfe0YovQVqSjh&quot; alt=&quot;ssh&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;처음 EC2를 생성하면 계정은 ubuntu로 되어있습니다. &lt;br /&gt;때문에 ssh 접속 시 ssh ubuntu@&amp;lt; 도메인 &amp;gt;으로 접속합니다.&lt;/li&gt;
  &lt;li&gt;DNS에 등록한 도메인을 통해 ssh로 접속이 되는지 확인하면 EC2 생성이 잘 된 것!&lt;/li&gt;
  &lt;li&gt;이제 ubuntu에 필요한 환경을 설정해주면 됩니다. :)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;3-환경구축-방법&quot;&gt;3. 환경구축 방법&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;배포는 Capistrano를 통해 이루어집니다.
&lt;span class=&quot;reference&quot;&gt; * Capistrano는 루비로 작성된 원격 서버 배포 자동화 도구이다. 레일즈 뿐만 아니라 다른 언어로 작성된 코드도 쉽게 배포할 수 있다. &lt;/span&gt;&lt;/li&gt;
  &lt;li&gt;Staging서버 환경 설정은 이 Capistrano를 통해 배포를 하면서 발생하는 에러를 하나하나 해결하는 방법으로 구축합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;4-ec2-user-계정-및-권한-설정&quot;&gt;4. EC2 user 계정 및 권한 설정&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;보안상 새로운 계정을 추가합니다. -&amp;gt; $ sudo adduser deploy / 패스워드는 Production 서버와 동일하게 설정했다.&lt;/li&gt;
  &lt;li&gt;sudo 그룹에 deploy 계정을 추가해줍니다. deploy계정에서 sudo 명령어 사용이 가능하게 됩니다! -&amp;gt; $ sudo adducer deploy sudo&lt;/li&gt;
  &lt;li&gt;sudo 그룹에 추가했으면 이제 계정을 deploy로 전환해줍니다.-&amp;gt;  $ sudo su - deploy&lt;/li&gt;
  &lt;li&gt;.ssh 디렉토리를 /home/deploy/에 추가해줍니다. -&amp;gt; $ mkdir .ssh&lt;/li&gt;
  &lt;li&gt;.ssh 디렉토리에 authorized_keys 파일을 추가해줍니다. -&amp;gt; $ touch authorized_keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1L5zzwbgAv8UqxIwVa0fnMn4zxR4jlAOh&quot; alt=&quot;rsa_structure&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;그림에서 보이듯이 authorized_keys는 내 로컬의 id_rsa를 참조하여 해당 사용자가 접근 허용되는지 판명합니다.&lt;/li&gt;
  &lt;li&gt;Staging 서버의 authorized_keys에는 Production 서버에서 사용되는 키를 넣어 로컬에는 하나의 키만 넣어줬습니다.&lt;/li&gt;
  &lt;li&gt;.ssh 디렉토리에서 rsa를 이용하여 개인키와 공개키를 생성해줍니다. 이 키는 Github에 등록될 키와 비교됩니다.&lt;/li&gt;
  &lt;li&gt;이후 .ssh 디렉토리와 디렉토리 안의 각 파일에 대한 권한을 설정해줍니다.&lt;/li&gt;
  &lt;li&gt;Chown 으로 소유권을 변경해주고 Chmod로 권한을 지정해줍니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;4-ec2-서버-환경설정&quot;&gt;4. EC2 서버 환경설정&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Production서버와 동일한 스펙으로 Staging 서버에 필요한 라이브러리와 서버 등을 install 해줍니다.&lt;/li&gt;
  &lt;li&gt;이는 config/deploy/staging.rb 를 production.rb 와 동일하게 만들어 cap staging deploy 명령어를 통해 배포를 하면서 에러를 보고 구축했습니다.&lt;/li&gt;
  &lt;li&gt;처음 deploy를 할 때 db:create 명령어는 직접 해줘야합니다.&lt;/li&gt;
  &lt;li&gt;각 서버나 라이브러리의 버전은 동일하게 맞춰주거나 최신화를 해줍니다.&lt;/li&gt;
  &lt;li&gt;nginx의 경우 etc/nginx/sites-enabled에 파일을 만들어 환경을 설정해줍니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-https-인증서-ssl&quot;&gt;5. HTTPS 인증서 (SSL)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;환경설정을 마치고 deploy가 이상없이 되고 HTTPS 인증서를 받아야합니다.&lt;/li&gt;
  &lt;li&gt;certbot를 install하여 이용합니다. 기존에는 돈을 주고 사야했지만 certbot가 무료로 발급을 해줍니다.&lt;/li&gt;
  &lt;li&gt;인증을 신청하기 전에 nginx가 default로 보여주는 페이지의 경로를 찾아갑니다.(Welcome nginx!) 저는 var/www/html 였습니다!&lt;/li&gt;
  &lt;li&gt;해당 파일의 위치에 .well_known 이라는 숨김 파일을 생성해줘야합니다. 이는 certbot이 사용자가 해당 도메인의 주인이라는 것을 확인할 수 있도록 해주는 것입니다.&lt;/li&gt;
  &lt;li&gt;이후 인증의 과정을 거치면 SSL 인증서가 받아지고 이를 etc/nginx/sites-enabled에 생성한 파일에 넣어줍니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

</description>
        <pubDate>Thu, 27 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/etc/2018/12/27/staging_server.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/etc/2018/12/27/staging_server.html</guid>
        
        <category>staging</category>
        
        <category>스테이징</category>
        
        <category>서버</category>
        
        <category>server</category>
        
        
        <category>Etc</category>
        
      </item>
    
      <item>
        <title>Ruby on Rails 시작하기</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1o28Np0nb4uQRGblbpKkw7dmlyiuHiaVd&quot; alt=&quot;ruby&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ruby-on-rails-시작하기&quot;&gt;Ruby on Rails 시작하기!&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;이번에는 &lt;span class=&quot;emphasis&quot;&gt;우분투 OS환경&lt;/span&gt;에 &lt;span class=&quot;emphasis&quot;&gt;Ruby on Rails&lt;/span&gt;를 설치해보겠습니다!
Django를 이용해서 공부를 해왔는데 이번에 들어간 기업에서 Ruby on Rails를 사용합니다.
그래서 루비 공부도하고 레일즈로 개발도 하고… :)&lt;/p&gt;

&lt;p&gt;일단 혼자 프로젝트를 해보는 것이기때문에 제 노트북의 &lt;span class=&quot;emphasis&quot;&gt;VirtualBox&lt;/span&gt;에 
&lt;span class=&quot;emphasis&quot;&gt;우분투 18.04&lt;/span&gt;를 설치했습니다. 
이제 Ruby on Rails를 설치해보겠습니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;  $ sudo apt-get update&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;먼저 우분투를 시작했으니 업데이트를 해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;  $ sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;업데이트를 하고 루비 설치에 필요한 기본적인 의존 패키지들을 설치해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ sudo apt-get install git-all&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;프로젝트를 관리하기위해 깃을 설치해줍니다. 
깃헙을 설치한 이유는 프로젝트를 관리하기 위해서 이기도 하지만 rbenv를 받아오기 위해서 받습니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;앞에서 말한 &lt;span class=&quot;emphasis&quot;&gt;rbenv&lt;/span&gt;를 깃을 통해 받아옵니다.
루비를 버전별로 실행환경을 관리하는 툴로 rvm, rbenv 등이 있습니다. 
rvm이 가장 오래된 관리 툴이지만 레일즈 분야에서 rbenv를 더 선호합니다. 
rbenv를 이용하면 애플리케이션 별로 각기 다른 루비 버전을 쉽게 사용할 수 있고, 
새 버전이 릴리즈될 때도 쉽게 업데이트가 가능합니다.&lt;/p&gt;

&lt;p&gt;rbenv를 받아오면 &lt;span class=&quot;emphasis&quot;&gt;.bashrc&lt;/span&gt;로 이동합니다.
&lt;span class=&quot;reference&quot;&gt;* rc : bashrc 에서 rc가 의미하는 것이 무엇인지 궁금해서 찾아보니 runcom file로 구동시 자동 실행되는
파일이라고 합니다.&lt;/span&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ export PATH=&quot;$HOME/.rbenv/bin:$PATH&quot;
$ eval &quot;$(rbenv init -)&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;받아온 rbenv를 사용하기 위해 간단한 &lt;span class=&quot;emphasis&quot;&gt;PATH&lt;/span&gt;를 설정해줍니다.
PATH를 설정해주지 않으면 rbenv를 실행할 때마다 해당 경로를 모두 적어줘야하는 불편함이 생깁니다.
PATH를 설정해주면 rbenv의 관리하에 있는 루비 명령을 rbenv로 넘겨주게됩니다.&lt;/p&gt;

&lt;p&gt;이동한 .bashrc에 가장 아래에 위의 명령어를 입력해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ echo 'export PATH=&quot;$HOME/.rbenv/bin:$PATH&quot;' &amp;gt;&amp;gt; ~/.bashrc
$ echo 'eval &quot;$(rbenv init -)&quot;' &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위의 코드를 실행하면 .bashrc에 명령어가 입력이 되지만 저는 리눅스 명령어도 아직 생소한지라
다른 분들이 하라는대로 따라하기보다 중간중간 리눅스 명령어도 같이 공부하는 중입니다…&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ exec $SHELL&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;편하신 방법으로 하시고 위 명령어를 통해 프로세스를 덮어줍니다!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위 명령어를 입력하면 .rbenv/plugins/ruby-build 디렉토리 위치에 &lt;span class=&quot;emphasis&quot;&gt;ruby-build&lt;/span&gt;를 설치합니다.&lt;/p&gt;

&lt;p&gt;rbenv의 PATH설정까지 마쳤으면 Ruby를 설치하기 위해 rbenv의 플러그인 ruby-build가 필요합니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ echo 'export PATH=&quot;$HOME/.rbenv/plugins/ruby-build/bin:$PATH&quot;' &amp;gt;&amp;gt; ~/.bashrc
$ exec $SHELL&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;ruby-build를 설치하고 PATH를 설정해줍니다. 
해당 명령어도 .bashrc에 입력해주는 명령어입니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv install -l&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ruby를 설치하기위해 설치가 가능한 버전을 확인해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv install 2.5.1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;각자 원하는 버전을 선택해서 설치해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv global 2.5.1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위 명령어는 이 버전을 시스템 전체에 걸쳐 반영되도록 하기 위해 사용합니다. 
위와 같은 방식으로 버전을 바꾸고 싶을 때 해당 버전을 인스톨 하고 &lt;span class=&quot;emphasis&quot;&gt;global&lt;/span&gt;로 지정해주면 됩니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv rehash&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위 명령어를 입력해줍니다. rehash 옵션은 새로운 환경을 재설정하는 옵션입니다.
rbenv는 현재 활성화 된 Ruby 버전에서 사용되는 파일을 가리키는 shims 디렉토리를 생성하여
작동합니다. &lt;span class=&quot;emphasis&quot;&gt;rehash&lt;/span&gt; 서브 명령을 통하면 rbenv는 서버에 설치된 모든 Ruby 버전의 모든 Ruby 명령과
일치하도록 해당 디렉토리에 shim을 유지합니다. 
때문에 새로 루비를 설치하거나 gem을 설치한 다음 반드시 rehash를 실행해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ gem install bundler&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ruby설치가 끝났으면 Ruby의 gem을 관리하는 &lt;span class=&quot;emphasis&quot;&gt;bundler&lt;/span&gt;를 설치합니다.
&lt;span class=&quot;reference&quot;&gt;* Bundler : 번들러는 필요한 정확한 gem과 버전을 추적하고 설치하여 루비 프로젝트를 위한 일관된
환경을 제공합니다. 때문에 의존성에 관련된 오류를 필요한 gem이 개발하는 환경에 있는지 확인해 주기때문에 쉽게 해결할 수 있게 도와줍니다. &lt;/span&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv rehash&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;위에서 설명했듯이 bundler를 설치했으니 새로운 환경을 재설정하기위해 rehash를 해줍니다.&lt;/p&gt;

&lt;p&gt;bundler 까지 설치했으면 이제 Rails를 설치해보겠습니다.
Rails는 상당히 많은 프로그램에 의존합니다. 때문에 NodeJS와 같은 Javascript runtime을 설치해줍니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get update
$ sudo apt-get install nodejs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;NodeJS를 설치하기 위해 PPA repository를 추가하고 설치해줍니다.
그리고 이제 Rails를 설치합니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ gem install rails&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;rbenv를 사용하고 있으니 우리는 rehash를 해줘야합니다.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-language&quot; data-lang=&quot;language&quot;&gt;$ rbenv rehash&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sat, 22 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/rubyonrails/2018/12/22/start_ror.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/rubyonrails/2018/12/22/start_ror.html</guid>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        <category>rubyonrails</category>
        
        <category>루비</category>
        
        <category>레일즈</category>
        
        
        <category>RubyonRails</category>
        
      </item>
    
      <item>
        <title>First Post</title>
        <description>&lt;h2 id=&quot;github-블로그-시작&quot;&gt;Github 블로그 시작!&lt;/h2&gt;

&lt;h3 id=&quot;시작&quot;&gt;시작&lt;/h3&gt;

&lt;p&gt;처음 블로그를 시작한 것은 네이버에서 였다. &lt;br /&gt;
웹 개발자로 취업을 하기 위해서 개발관련 공부를 하는데 아는 지인이 블로그에 내용을 정리해서 올리면 꽤 많은 도움이 된다고하여
호기심에 시작하게 되었다. 실제로 블로그를 하지 않을 때는 공부하는 내용에 대해 이해가 잘 되지 않아도 넘어가는 때가 많았다.
공부를 하면서 어려운 용어나 이해가 되지 않는 부분이 생기면, 그 부분을 찾고 따라가다가 결국 내가 어떤 공부를 하고 있었는지 
잊어버리는 경우가 많았기 때문에 최대한 모르는 부분이 있어도 감으로 대충 짐작하고 공부를 하는 경우가 많았기 때문이었다.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;블로그를 하면 시간이 좀 걸리지만 확실히 공부를 하게 되고 앞서 말한 경우를 최대한 대비하기 위해 하나하나 적어가면서 공부를 
하게되었다. 나름 책임감이 생긴 것 같다. 글을 쓰고 누군가가 보게되니 공부한 내용에 대해서는 알아야한다는 것이다. 이런 공부 방법은
취업을 한 지금 굉장히 도움이 되고있다. 블로그를 하기 전의 나는 이것 저것 무작정 시도해보고 되면 성공 아니면 말고 식이었다. 
그렇게 뭔가 만들어내거나 구현해내도 어떻게 만들었고 어떤 오류를 어떤 방식으로 해결했는지 절대 알지 못했다. 
누구한테 설명할 때 내가 만들었다고 하기 민망할 정도였다. 그러나 블로그를 하면서 공부했던 경험 덕분에 머리 속을 정리하면서 
일을 하고 있다는 기분이 든다. 나만 알고있는 기분이고 생각이지만 꽤 기분이 좋게 일을 할 수 있다. 
혼란스러운 부분이 어떤 부분이고 에러가 나는 부분이 어느 부분인지 안다는 것과 누군가에게 내가 어떤 작업을 하고있는 중이고
지금은 어느 부분의 어떤 과정을 진행중인지 설명하기 쉽다는 것 등이 나를 기분 좋게 만드는 것 같다.&lt;/p&gt;

&lt;p&gt;나는 많은 블로그의 글을 참고하면서 일을 하고 있다. 또 많은 사람들이 블로그의 글을 보며 개발관련 일이나 공부를 하고 있을 것이다.
내가 읽는 글이 아닌 누군가가 읽을 글이기에 최대한 쉽게 쓰여진 포스트들이 많은 블로그가 되면 좋겠다. 
개인적으로는 일할 때 참고하는 글이 아닌 퇴근후 읽을 수 있는 좋은 글들을 많이 쓰고싶다!&lt;/p&gt;
</description>
        <pubDate>Sat, 22 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/2018/12/22/first-post.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/2018/12/22/first-post.html</guid>
        
        
      </item>
    
      <item>
        <title>스택 / 큐 (stack and que)</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://drive.google.com/uc?id=1-331-WItPcbNuWKl34MzUECc9edYGgIu&quot; alt=&quot;stack&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;스택--큐&quot;&gt;스택 &amp;amp; 큐&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;스택과 큐의 차이는 기술 면접에서 등장하는 단골 질문이라고 합니다. 
비전공자인 저는 프로젝트 중 컴퓨터사이언스에 접근할 기회가 거의 없었습니다. 
때문에 스택과 큐를 코딩으로 만들 줄은 알았지만 그 단점과 보완의 방식에 대한 지식을 이번에 
새롭게 알게 되었습니다.&lt;/p&gt;

&lt;h3 id=&quot;1-스택stack&quot;&gt;1. 스택(stack)&lt;/h3&gt;

&lt;p&gt;스택은 자료구조에서 무언가 쌓는 의미를 갖는 자료구조입니다. 
순서대로 쌓인 스택의 맨 첫번째 데이터를 꺼내려면 맨 마지막에 쌓은 데이터부터 차례로 
꺼내야합니다. 
이를 일반적으로 &lt;span class=&quot;emphasis&quot;&gt;LIFO ( Last In First Out )&lt;/span&gt; 이라고 부릅니다. 
스택에서 사용되는 함수로는 push()와 pop()이 있습니다. 
push() 는 자료를 넣을 때, pop()은 자료를 삭제할 때 사용됩니다. 
앞에서 스택은 LIFO 방식으로 사용되기 때문에 push()를 하게되면 맨 마지막 index에 입력되고,
pop()을 하게되면 맨 마지막 index의 데이터가 삭제됩니다.&lt;/p&gt;

&lt;p&gt;이러한 성격때문에 스택 방식은 먼저 들어온 작업이 마지막에 이루어지는 구조를 이룹니다. 
때문에 우선순위에 관련된 문제가 생길 수 있으며 데이터가 새로 계속해서 들어오게되면 
맨 처음 들어온 데이터가 오랫동안 잔류하는 경우가 생깁니다.&lt;/p&gt;

&lt;h3 id=&quot;2-큐que&quot;&gt;2. 큐(que)&lt;/h3&gt;

&lt;p&gt;큐는 처음에 저장한 데이터를 가장 먼저 꺼내는 &lt;span class=&quot;emphasis&quot;&gt;FIFO ( First In First Out )&lt;/span&gt; 의 구조로 되어있습니다. 
입출력이 양방향에서 이루어지며 Front 는 가장 먼저 입력된 데이터의 index이고, Rear는 가장 나중에
입력된 데이터의 index 입니다. 
데이터 의 입력을 Enqueue, 삭제를 Dequeue라고 합니다.&lt;/p&gt;

&lt;p&gt;큐는 데이터를 모두 입력 후 하나씩 삭제를 하게 되면 Rear는 그대로 있고 Front가 움직입니다. 
Front가 점점 뒤로 가면서 결국 Rear와 만나게 됩니다. 
그렇게 되면 마지막에는 Front 위치의 앞에는 비어있는 공간이 있지만 
Rear가 움직일 수 있는 공간이 없기 때문에 오버플로우 현상을 발생기킵니다. 
더이상 데이터가 들어갈 공간이 없다고 인식하는 것입니다. 
때문에 데이터 삭제를 명령한 후 뒤에 있는 데이터들을 앞으로 옮겨와야 합니다. 
이러한 방식은 소수의 데이터에서는 괜찮을 수 있을지 몰라도 
데이터의 양이 많아지면 연산에 많은 시간이 걸리게 됩니다. 
때문에 나온 &lt;span class=&quot;emphasis&quot;&gt;원형 큐&lt;/span&gt;가 있습니다. 
지금까지 데이터의 큐의 형태를 선형으로 봤다면 원형으로 생각하고 순환이 이루어지게 합니다. 
배열의 Rear가 다음 index로 넘어갈 때 &lt;span class=&quot;emphasis_blue&quot;&gt;’( index + 1 ) % ( 배열의 사이즈 )’&lt;/span&gt;를 이용하여
배열의 마지막 index에서 맨 처음 index로 넘어가게 할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;reference&quot;&gt; * 연결리스트 &lt;br /&gt;
앞에서 설명한 스택과 큐에는 한계가 있습니다. 배열의 크기를 처음에 정해주고 배열의 크기를 넘어서면
에러가 발생합니다. 이는 배열을 사용하지 않는 열결리스트를 사용함으로 보완할 수 있습니다. 
앞선 큐에서는 Front와 Rear가 index였지만 연결리스트에서는 Node입니다. 
새로 추가할 데이터를 Node에 넣은 후 Rear에 이 Node를 넣어줍니다. 
이 방식으로 스택과 큐를 구현하면 메모리가 허용하는한 많은 데이터를 저장할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 03 Jan 2018 00:00:00 +0000</pubDate>
        <link>https://jongjineee.github.io/datascience/2018/01/03/stack_que.html</link>
        <guid isPermaLink="true">https://jongjineee.github.io/datascience/2018/01/03/stack_que.html</guid>
        
        <category>stack</category>
        
        <category>que</category>
        
        <category>스택</category>
        
        <category>큐</category>
        
        
        <category>DataScience</category>
        
      </item>
    
  </channel>
</rss>
