<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>아키텍토필의 소프트웨어 시스템 설계</title>
    <link>https://architectophile.tistory.com/</link>
    <description>소프트웨어 아키텍트의 스터디 룸</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 19:19:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>아키텍토필</managingEditor>
    <image>
      <title>아키텍토필의 소프트웨어 시스템 설계</title>
      <url>https://tistory1.daumcdn.net/tistory/3717110/attach/3c3a233d326e4642ad8538f02d2629ab</url>
      <link>https://architectophile.tistory.com</link>
    </image>
    <item>
      <title>[Raspberry Pi] 패스워드 변경 방법</title>
      <link>https://architectophile.tistory.com/18</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;raspberry-pi-logo.png&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/er2Cdu/btqHBSAAZkS/kFrvfoIadvjGkMc1NncGwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/er2Cdu/btqHBSAAZkS/kFrvfoIadvjGkMc1NncGwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/er2Cdu/btqHBSAAZkS/kFrvfoIadvjGkMc1NncGwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fer2Cdu%2FbtqHBSAAZkS%2FkFrvfoIadvjGkMc1NncGwK%2Fimg.png&quot; data-filename=&quot;raspberry-pi-logo.png&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;169&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;[Raspberry Pi] 패스워드 변경 방법&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. 서론&lt;/h2&gt;
&lt;p&gt;이번 포스트에서는 &lt;code&gt;라즈베리파이(Raspberry Pi)&lt;/code&gt;의 기본(&lt;code&gt;default&lt;/code&gt;) &lt;code&gt;패스워드(passwd)&lt;/code&gt;를 잊어버렸을 경우 &lt;code&gt;패스워드&lt;/code&gt;를 새롭게 변경하는 방법에 대해서 알아볼 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 준비사항(Prerequisites)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Raspberry Pi&lt;/code&gt; 보드(이 글에서는 Raspberry Pi 3 Model B v1.2 사용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PC&lt;/code&gt; 또는 &lt;code&gt;노트북&lt;/code&gt;(cmdline.txt 파일 수정을 위해 필요)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;모니터&lt;/code&gt;(HDMI를 통해 라즈베리파이와 연결)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;키보드&lt;/code&gt;(명령을 실행하고 비밀번호 입력하기 위함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. cmdline.txt 파일 수정&lt;/h2&gt;
&lt;h3&gt;1) SD 카드 제거&lt;/h3&gt;
&lt;p&gt;우선 라즈베리파이가 실행 중인 경우 &lt;code&gt;shutdown&lt;/code&gt;하거나 전원을 분리하여 라즈베리파이를 종료한다. 그리고 라즈베리파이에 꽂혀있는 &lt;code&gt;SD 카드&lt;/code&gt;를 분리한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2) cmdline.txt 파일 수정&lt;/h3&gt;
&lt;p&gt;제거된 &lt;code&gt;SD 카드&lt;/code&gt;를 &lt;code&gt;PC&lt;/code&gt;에 연결한다. &lt;code&gt;SD 카드&lt;/code&gt; 내부의 파일 중에서 &lt;code&gt;cmdline.txt&lt;/code&gt; 파일을 연다.&lt;/p&gt;
&lt;p&gt;파일을 열면 아래와 비슷한 명령이 쓰여있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cmdline.txt&lt;/code&gt;의 기존 내용에서 아래의 명령을 마지막에 추가한 다음 파일을 저장한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;init=/bin/sh&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;주의사항&lt;/i&gt;&lt;/b&gt; :&lt;br /&gt;해당 &lt;code&gt;init=/bin/sh&lt;/code&gt; 명령을 추가할 때는 반드시 기존의 &lt;code&gt;cmdline.txt&lt;/code&gt; 파일 내용에서 &lt;b&gt;&lt;i&gt;같은 줄에 명령을 추가해야 한다.&lt;/i&gt;&lt;/b&gt; 엔터(line break)를 사용해서는 안 되고 스페이스(space)로 한 칸 띄운 뒤에 &lt;code&gt;init=/bin/sh&lt;/code&gt;을 추가한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. 라즈베리파이 비밀번호 변경&lt;/h2&gt;
&lt;h3&gt;1) 라즈베리파이 부팅&lt;/h3&gt;
&lt;p&gt;이제 &lt;code&gt;PC&lt;/code&gt;에 꽂혀 있던 &lt;code&gt;SD 카드&lt;/code&gt;를 제거하고, 다시 &lt;code&gt;라즈베리파이&lt;/code&gt;에 &lt;code&gt;SD 카드&lt;/code&gt;를 꽂는다.&lt;br /&gt;그리고 &lt;code&gt;라즈베리파이&lt;/code&gt;에 전원을 입력하여 부팅한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2) 루트 시스템 마운트&lt;/h3&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ mount -o remount, rw /&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3) 비밀번호 변경&lt;/h3&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ passwd pi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 명령을 실행하면 새로운 비밀번호를 입력하고, 한 번더 동일한 비밀번호를 입력하여 확인하고 나면 &lt;code&gt;(passwd: password updated successfully)&lt;/code&gt;라는 메시지와 함께 비밀번호 변경이 완료된다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ sync
$ exec /sbin/init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 &lt;code&gt;sync&lt;/code&gt; 명령을 실행하여 캐쉬된 데이터를 스토리지에 저장하고, &lt;code&gt;init&lt;/code&gt; 명령을 실행한다. 몇 초간 기다리면 라즈비안이 부팅된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. cmdline.txt 파일 원상복구&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;라즈비안&lt;/code&gt;을 종료한 다음, &lt;code&gt;라즈베리파이&lt;/code&gt;에 꽂혀 있는 &lt;code&gt;SD 카드&lt;/code&gt;를 다시 분리하여, &lt;code&gt;PC&lt;/code&gt;에서 기존에 수정했던 &lt;code&gt;cmdline.txt&lt;/code&gt; 파일을 다시 연다. 그리고 아까 추가했던 &lt;code&gt;init=/bin/sh&lt;/code&gt; 부분을 &lt;b&gt;&lt;i&gt;삭제하여&lt;/i&gt;&lt;/b&gt; 기존 내용으로 복구한 다음 저장한다.&lt;/p&gt;
&lt;p&gt;그리고 다시 &lt;code&gt;SD 카드&lt;/code&gt;를 &lt;code&gt;라즈베리파이&lt;/code&gt;에 꽂은 다음 전원을 연결하여 부팅하여 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;i&gt;Raspberry Pi tutorials. (2018, Mar 14). &lt;a href=&quot;https://howtoraspberrypi.com/recover-password-raspberry-pi/&quot;&gt;How to recover the password of your Raspberry Pi if you lost it. 2018 Update.&lt;/a&gt; [Web Blog Post]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#라즈베리파이&lt;/code&gt; &lt;code&gt;#라즈베리파이 비밀번호&lt;/code&gt; &lt;code&gt;#라즈베리 비밀번호 변경&lt;/code&gt; &lt;code&gt;#라즈베리 파이 비번&lt;/code&gt; &lt;code&gt;#라즈베리 파이 비번 변경&lt;/code&gt; &lt;code&gt;#Raspberry Pi&lt;/code&gt; &lt;code&gt;#Raspberry Pi password&lt;/code&gt; &lt;code&gt;#Raspberry Pi passwd&lt;/code&gt; &lt;code&gt;#raspberrypi password&lt;/code&gt; &lt;code&gt;#how to change raspberry pi password&lt;/code&gt; &lt;code&gt;#how to change password on Raspberry Pi&lt;/code&gt; &lt;code&gt;#passwd raspberry pi&lt;/code&gt; &lt;code&gt;#linux&lt;/code&gt; &lt;code&gt;#Raspbian&lt;/code&gt; &lt;code&gt;#Raspbian passwd&lt;/code&gt; &lt;code&gt;#raspberry pi root password&lt;/code&gt; &lt;code&gt;#raspberry pi root passwd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;copy; 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Embedded/Raspberry Pi</category>
      <category>Raspberry Pi</category>
      <category>raspberry pi passwd</category>
      <category>Raspberry Pi Password</category>
      <category>라즈베리 passwd</category>
      <category>라즈베리 password</category>
      <category>라즈베리 비밀번호</category>
      <category>라즈베리파이</category>
      <category>라즈베리파이 기본 비밀번호</category>
      <category>라즈베리파이 비밀번호</category>
      <category>라즈베리파이 비번 변경</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/18</guid>
      <comments>https://architectophile.tistory.com/18#entry18comment</comments>
      <pubDate>Tue, 1 Sep 2020 14:51:45 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] Undefined와 Null의 차이</title>
      <link>https://architectophile.tistory.com/17</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;javascript.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxPor/btqC49tnqT1/2awo8nVOm8iFakKwXntXpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxPor/btqC49tnqT1/2awo8nVOm8iFakKwXntXpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxPor/btqC49tnqT1/2awo8nVOm8iFakKwXntXpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxPor%2FbtqC49tnqT1%2F2awo8nVOm8iFakKwXntXpK%2Fimg.png&quot; data-filename=&quot;javascript.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;300&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[JavaScript] Undefined와 Null의 차이&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. Undefined와 Null의 차이&lt;/h2&gt;
&lt;p&gt;우리는 가끔 &lt;code&gt;undefined&lt;/code&gt; 타입과 &lt;code&gt;null&lt;/code&gt; 타입을 혼동할 때가 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; 타입은 어떤 변수(variable)가 &lt;code&gt;선언(declare)&lt;/code&gt;은 되었지만 아직 어떤 값(value)으로 &lt;code&gt;할당(assign)&lt;/code&gt;하지는 않았다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;반면에 &lt;code&gt;null&lt;/code&gt; 타입은 어떤 변수(variable)가 &lt;code&gt;선언(declare)&lt;/code&gt;이 되었고 &lt;code&gt;null&lt;/code&gt; 값(value)으로 &lt;code&gt;할당(assign)&lt;/code&gt;도 되었다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JavaScript&lt;/code&gt;에서 변수를 선언하고 아무런 값도 할당하지 않은 경우, 자동으로 디폴트 값으로 &lt;code&gt;undefined&lt;/code&gt; 값이 할당된다.&lt;/p&gt;
&lt;p&gt;따라서 &lt;code&gt;null&lt;/code&gt; 값이 할당되었다는 것은 &lt;b&gt;&lt;i&gt;누군가 직접 &lt;code&gt;null&lt;/code&gt; 값을 할당하였다는 것을 의미한다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;i&gt;Ajay Matharu. (2013, Mar 2). &lt;a href=&quot;https://www.ajaymatharu.com/javascript-difference-between-undefined-and-null/&quot;&gt;JAVASCRIPT: Difference between UNDEFINED and NULL&lt;/a&gt; [Web Blog Post]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#자바스크립트&lt;/code&gt; &lt;code&gt;#자바스크립트 타입&lt;/code&gt; &lt;code&gt;#자바스크립트 undefined&lt;/code&gt; &lt;code&gt;#자바스크립트 null&lt;/code&gt; &lt;code&gt;#자바스크립트 null undefined 차이&lt;/code&gt; &lt;code&gt;#자바스크립트 undefined null 차이&lt;/code&gt; &lt;code&gt;#JavaScript&lt;/code&gt; &lt;code&gt;#javascript undefined&lt;/code&gt; &lt;code&gt;#javascript null&lt;/code&gt; &lt;code&gt;#javascript null and undefined&lt;/code&gt; &lt;code&gt;#javascript undefined and null&lt;/code&gt; &lt;code&gt;#javascript difference between undefined and null&lt;/code&gt; &lt;code&gt;#javascript difference between null and undefined&lt;/code&gt; &lt;code&gt;#difference between null and undefined&lt;/code&gt; &lt;code&gt;#ECMAScript&lt;/code&gt; &lt;code&gt;#ES6&lt;/code&gt; &lt;code&gt;#ES5&lt;/code&gt; &lt;code&gt;#ES2017&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;copy; 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>difference between null and undefined</category>
      <category>ECMAScript</category>
      <category>JavaScript</category>
      <category>javascript null and undefined</category>
      <category>javascript null and undefined difference</category>
      <category>javascript undefined and null</category>
      <category>null undefined 차이</category>
      <category>자바스크립트</category>
      <category>자바스크립트 null</category>
      <category>자바스크립트 undefined</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/17</guid>
      <comments>https://architectophile.tistory.com/17#entry17comment</comments>
      <pubDate>Wed, 1 Apr 2020 10:49:01 +0900</pubDate>
    </item>
    <item>
      <title>맥(macOS)에서 키보드 단축키로 앱 여는 법</title>
      <link>https://architectophile.tistory.com/16</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;macbook.jpg&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AA2xN/btqCWMLe0dL/372kPGO0fLGxIR60m35pD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AA2xN/btqCWMLe0dL/372kPGO0fLGxIR60m35pD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AA2xN/btqCWMLe0dL/372kPGO0fLGxIR60m35pD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAA2xN%2FbtqCWMLe0dL%2F372kPGO0fLGxIR60m35pD1%2Fimg.jpg&quot; data-filename=&quot;macbook.jpg&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;433&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;맥(macOS)에서 키보드 단축키로 앱 여는 법&lt;/h1&gt;
&lt;br/&gt;

&lt;h2&gt;1. 서론&lt;/h2&gt;
&lt;p&gt;컴퓨터에서 다양한 작업을 하다보면 자주 사용하는 기능이나 앱을 키보드 단축키로 설정해놓으면 매우 편리하다. 맥(macOS)에서는 터미널(Terminal), 파인더(Finder), 또는 크롬 브라우저(Google Chrome) 등을 자주 사용하는데, 자주 쓰는 앱을 실행할 때마다 &lt;code&gt;Spotlight Search&lt;/code&gt; 또는 &lt;code&gt;Launchpad&lt;/code&gt;를 사용하는 것을 매우 번거로운 일이다.&lt;/p&gt;
&lt;p&gt;따라서 이번 포스트에서는 &lt;code&gt;맥(macOS)&lt;/code&gt;에서 &lt;code&gt;키보드 단축키(Keyboard Shortcuts)&lt;/code&gt;를 설정하여 &lt;strong&gt;&lt;em&gt;빠르고 편리하게 앱을 열 수 있는 방법에 대해서 알아보도록 하겠다.&lt;/em&gt;&lt;/strong&gt; 이를 위해서 우리는 맥에서 기본적으로 제공하는 &lt;code&gt;오토메이터(Automator)&lt;/code&gt; 툴을 사용하도록 할 것이다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. 단축키 설정 방법&lt;/h2&gt;
&lt;h3&gt;1) Automator 설정&lt;/h3&gt;
&lt;p&gt;(1) 메뉴바에서 돋보기 모양의 아이콘을 클릭하거나 또는 단축키 &lt;code&gt;Command + Space&lt;/code&gt;를 입력하여 &lt;code&gt;Spotlight Search&lt;/code&gt;를 연다. 그리고 &amp;quot;Automator&amp;quot; 또는 &amp;quot;오토메이터&amp;quot;를 입력하여 실행한다(또는 &lt;code&gt;런치패드(Launchpad)&lt;/code&gt;를 사용하여 오토메이터(Automator)를 실행한다.)&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(2) Automator 앱을 실행하면 다음과 같이 새로운 작업을 만드는 창이 뜨는데 여기서 &lt;code&gt;Quick Action&lt;/code&gt;을 선택한다(만약 새로운 작업 생성 창이 나타나지 않을 경우에는 &lt;code&gt;File &amp;gt; New&lt;/code&gt;를 선택한다.)&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(3) &lt;code&gt;Workflow receives current&lt;/code&gt; 항목에서 &lt;code&gt;no input&lt;/code&gt;을 선택한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.3.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.3.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;240&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(4) 왼쪽에 보이는 Actions 리스트 중에서 &lt;code&gt;Launch Application&lt;/code&gt;을 선택하고 오른쪽 방향으로 &lt;strong&gt;&lt;em&gt;드래그(drag)하여&lt;/em&gt;&lt;/strong&gt; 워크플로우에 추가한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.4.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.4.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(5) 단축키를 이용해 실행할 어플리케이션을 찾아서 선택한다. 이 예제에서는 &lt;code&gt;터미널(Terminal)&lt;/code&gt; 프로그램을 선택하였다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.5.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.5.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.5.3.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(6) 메뉴바에서 &lt;code&gt;File &amp;gt; Save&lt;/code&gt;를 선택하고, 추가할 &lt;strong&gt;&lt;em&gt;작업의 이름을 작성하고 저장한다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.6.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;240&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.1.6.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;h3&gt;2) 키보드 단축키(keyboard shortcut) 설정&lt;/h3&gt;
&lt;p&gt;(1) 맥의 시스템 설정(System Preferences)을 연다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;240&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(2) &lt;code&gt;키보드(Keyboard)&lt;/code&gt;를 클릭하고 &lt;code&gt;shrotcut&lt;/code&gt; 탭을 선택한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.2.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.2.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(3) 왼쪽에 메뉴에 있는 &lt;code&gt;Services&lt;/code&gt;를 선택하고, 방금 우리가 추가한 작업을 선택한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.3.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.3.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(4) &lt;code&gt;Add Shortcut&lt;/code&gt; 버튼을 클릭하고, 원하는 단축키 조합을 입력하여 설정한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.4.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.2.4.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;h3&gt;3) 단축키 동작 확인&lt;/h3&gt;
&lt;p&gt;(1) 앞서 설정한 단축키 조합을 입력하여 해당 어플리케이션이 실행되는 것을 확인한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.3.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;만약 지정한 단축키를 입력해도 &lt;strong&gt;&lt;em&gt;앱이 실행되지 않을 경우&lt;/em&gt;&lt;/strong&gt; 바탕화면을 선택한 후, 메뉴바에서 &lt;code&gt;Finder &amp;gt; Services&lt;/code&gt;를 한 번 클릭한 후 다시 바탕화면을 선택한 다음, 단축키를 입력하면 앱이 실행될 것이다. 이것은 &lt;strong&gt;&lt;em&gt;맥의 키보드 단축키 설정 버그로서&lt;/em&gt;&lt;/strong&gt; 현재 &lt;code&gt;macOS 10.15 Beta&lt;/code&gt; 버전을 기준으로 아직까지 버그가 수정되지 않은 것으로 보인다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-set-keyboard-shortcut-to-open-app-on-mac-2.3.1.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;240&quot;/&gt;

&lt;br/&gt;

&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;em&gt;wikiHow Staff. (2017, Sep 11). &lt;a href=&quot;https://www.wikihow.com/Set-a-Keyboard-Shortcut-to-Open-Mac-Apps&quot;&gt;How to Set a Keyboard Shortcut to Open Mac Apps&lt;/a&gt; [wikiHow]&lt;/em&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#맥북&lt;/code&gt; &lt;code&gt;#맥&lt;/code&gt; &lt;code&gt;#맥북 단축키&lt;/code&gt; &lt;code&gt;#맥 단축키&lt;/code&gt; &lt;code&gt;#맥북 터미널 단축키&lt;/code&gt; &lt;code&gt;#맥 터미널 단축키&lt;/code&gt; &lt;code&gt;#맥북 단축키 설정&lt;/code&gt; &lt;code&gt;#macOS&lt;/code&gt; &lt;code&gt;#mac shortcut&lt;/code&gt; &lt;code&gt;#MacBook terminal keyboard shortcut&lt;/code&gt; &lt;code&gt;#macOS shortcut&lt;/code&gt; &lt;code&gt;#how to set a keyboard shortcut to open an app&lt;/code&gt; &lt;code&gt;#macOS terminal shortcut&lt;/code&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;p&gt;© 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Others/Computer Tips</category>
      <category>mac keyboard shortcut</category>
      <category>MacBook keyboard shortcut</category>
      <category>macOS terminal shortcut</category>
      <category>맥</category>
      <category>맥 단축키</category>
      <category>맥 단축키 설정</category>
      <category>맥 키보드 단축키 설정하기</category>
      <category>맥북</category>
      <category>맥북 단축키 설정</category>
      <category>맥북 단축키 설정하는 법</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/16</guid>
      <comments>https://architectophile.tistory.com/16#entry16comment</comments>
      <pubDate>Wed, 25 Mar 2020 18:11:21 +0900</pubDate>
    </item>
    <item>
      <title>What is this?</title>
      <link>https://architectophile.tistory.com/pages/What-is-this</link>
      <description>&lt;p&gt;This is a test page.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/pages/What-is-this</guid>
      <pubDate>Wed, 25 Mar 2020 13:25:13 +0900</pubDate>
    </item>
    <item>
      <title>맥(macOS)에서 터미널(Terminal) 여는 법</title>
      <link>https://architectophile.tistory.com/14</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;macbook.jpg&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LwnhF/btqCXuJ0rAT/p7loekGWXxaThwJG0ykxBk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LwnhF/btqCXuJ0rAT/p7loekGWXxaThwJG0ykxBk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LwnhF/btqCXuJ0rAT/p7loekGWXxaThwJG0ykxBk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLwnhF%2FbtqCXuJ0rAT%2Fp7loekGWXxaThwJG0ykxBk%2Fimg.jpg&quot; data-filename=&quot;macbook.jpg&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;433&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;맥(macOS)에서 터미널(Terminal) 여는 법&lt;/h1&gt;
&lt;br/&gt;

&lt;h2&gt;1. 서론&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;터미널(Terminal)&lt;/code&gt;은 &lt;code&gt;맥(macOS)&lt;/code&gt;에서 사용하는 &lt;code&gt;커맨드 라인 툴(command line tool)&lt;/code&gt;로서, 맥북을 사용하다보면 자연스럽게 터미널 프로그램을 이용하여 리눅스(Linux)에서 처럼 명령을 직접 입력하여 프로세스를 실행하는 경우가 많다. &lt;/p&gt;
&lt;p&gt;따라서 이번 포스트에서는 &lt;code&gt;맥(macOS)&lt;/code&gt;에서 &lt;code&gt;터미널(Terminal)&lt;/code&gt; 프로그램을 열 수 있는 다양한 방법에 대해서 알아보도록 하겠다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. 터미널 여는 방법&lt;/h2&gt;
&lt;h3&gt;1) Spotlight Search 사용하기&lt;/h3&gt;
&lt;p&gt;(1) 메뉴바에서 돋보기 모양의 아이콘을 클릭하거나 또는 단축키 &lt;code&gt;Command + Space&lt;/code&gt;를 입력하여 &lt;code&gt;Spotlight Search&lt;/code&gt;를 연다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-2.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(2) &amp;quot;Teminal&amp;quot; 또는 &amp;quot;터미널&amp;quot;을 입력한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-2.1.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(3) 가장 상단에 &lt;code&gt;Terminal&lt;/code&gt; 프로그램이 나타나면 키보드 &lt;code&gt;엔터(Enter)&lt;/code&gt; 키를 누르거나 프로그램을 &lt;code&gt;더블클릭(double-click)&lt;/code&gt;하여 터미널을 연다.&lt;/p&gt;
&lt;br/&gt;

&lt;h3&gt;2) Finder 사용하기&lt;/h3&gt;
&lt;p&gt;(1) &lt;code&gt;Finder&lt;/code&gt;를 연다.&lt;/p&gt;
&lt;p&gt;(2) 상단의 메뉴바에서 &lt;code&gt;Go &amp;gt; Utilities&lt;/code&gt;를 클릭한다. 또는 단축키 &lt;code&gt;Shift + Command + U&lt;/code&gt;를 입력한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-2.2.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;280&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(3) 그 안에 보이는 &lt;code&gt;Terminal&lt;/code&gt; 프로그램을 더블클릭하여 연다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-2.2.3.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;h3&gt;3) Launchpad 사용하기&lt;/h3&gt;
&lt;p&gt;(1) Dock에 있는 &lt;code&gt;Launchpad&lt;/code&gt;를 연다. 또는 단축키 &lt;code&gt;Fn + F4&lt;/code&gt;를 입력한다.&lt;/p&gt;
&lt;p&gt;(2) &lt;code&gt;Other&lt;/code&gt; 폴더로 이동한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-2.3.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;(3) &lt;code&gt;Terminal&lt;/code&gt; 프로그램을 클릭하여 연다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;3. 터미널 바로가기(shortcut) 만들기&lt;/h2&gt;
&lt;p&gt;(1) 터미널 프로그램이 열려 있는 상태에서 Dock을 확인한다.&lt;/p&gt;
&lt;p&gt;(2) &lt;code&gt;컨트롤(Ctrl)&lt;/code&gt; 키를 누른 상태에서 터미널 아이콘을 클릭한다. 또는 터미널 아이콘을 마우스로 &lt;code&gt;우클릭(right-click)&lt;/code&gt;한다.&lt;/p&gt;
&lt;p&gt;(3) Options &amp;gt; Keep in Dock을 선택한다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/others/computer-tips/images/computer-tips-how-to-open-terminal-on-mac-3.1.3.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;em&gt;Sandy Writtenhouse. (2019, Apr 19). &lt;a href=&quot;https://www.idownloadblog.com/2019/04/19/ways-open-terminal-mac/&quot;&gt;7 simple ways to open Terminal on Mac&lt;/a&gt; [Web Blog Post]&lt;/em&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#맥북&lt;/code&gt; &lt;code&gt;#맥&lt;/code&gt; &lt;code&gt;#맥북 터미널&lt;/code&gt; &lt;code&gt;#맥 터미널&lt;/code&gt; &lt;code&gt;#맥북 터미널 열기&lt;/code&gt; &lt;code&gt;#맥 터미널 여는 법&lt;/code&gt; &lt;code&gt;#맥북 터미널 여는 법&lt;/code&gt; &lt;code&gt;#mac terminal&lt;/code&gt; &lt;code&gt;#MacBook terminal&lt;/code&gt; &lt;code&gt;#macOS terminal&lt;/code&gt; &lt;code&gt;#how to open mac terminal&lt;/code&gt; &lt;code&gt;#how to open macOS terminal&lt;/code&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;p&gt;© 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Others/Computer Tips</category>
      <category>mac terminal</category>
      <category>mac 터미널</category>
      <category>MacBook Terminal</category>
      <category>macos</category>
      <category>맥</category>
      <category>맥 터미널</category>
      <category>맥북</category>
      <category>맥북 터미널</category>
      <category>맥북 터미널 열기</category>
      <category>맥에서 터미널 여는 법</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/14</guid>
      <comments>https://architectophile.tistory.com/14#entry14comment</comments>
      <pubDate>Wed, 25 Mar 2020 11:39:44 +0900</pubDate>
    </item>
    <item>
      <title>[Git] 사용자 계정 삭제하고 다시 추가하기(on macOS)</title>
      <link>https://architectophile.tistory.com/13</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;git.png&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qc7rA/btqCWLdM3qo/vzygAVq7ETUhXYBqbgh6Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qc7rA/btqCWLdM3qo/vzygAVq7ETUhXYBqbgh6Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qc7rA/btqCWLdM3qo/vzygAVq7ETUhXYBqbgh6Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqc7rA%2FbtqCWLdM3qo%2FvzygAVq7ETUhXYBqbgh6Jk%2Fimg.png&quot; data-filename=&quot;git.png&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1125&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[Git] 사용자 계정 삭제하고 다시 추가하기(on macOS)&lt;/h1&gt;
&lt;br/&gt;

&lt;h2&gt;1. 서론&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Github&lt;/code&gt; 계정을 새로 만들었을 경우, 새로 만든 계정으로 스위치해서 &lt;code&gt;git commit&lt;/code&gt;, &lt;code&gt;git push&lt;/code&gt; 명령을 실행해야 한다. 하지만 기존의 계정 정보(credentials)가 로컬 머신에 저장되어 있기 때문에 &lt;code&gt;git&lt;/code&gt; 명령을 실행하면 예전 사용하던 계정으로 이벤트가 기록된다.&lt;/p&gt;
&lt;p&gt;그러므로 이전 계정의 정보(credentials)을 삭제하고 새로운 계정을 추가해야 하는데, 단순히 &lt;code&gt;git config --global user.name&lt;/code&gt;과 &lt;code&gt;git config --global user.email&lt;/code&gt;을 수정하는 것만으로는 계정이 스위치되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;macOS&lt;/code&gt;에서는 git의 계정 정보(credentials)가 &lt;code&gt;KeyChain&lt;/code&gt; 툴에 저장되는데, 따라서 새로운 계정으로 변경하기 위해서는 &lt;code&gt;KeyChain&lt;/code&gt; 안에 저장된 계정 정보를 삭제한 뒤, 새로운 계정을 등록해야 한다. &lt;/p&gt;
&lt;p&gt;이번 포스트에서는 &lt;code&gt;macOS&lt;/code&gt;에서 어떻게 git 계정을 변경하는지 알아보도록 하자.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. Git 계정 변경 방법&lt;/h2&gt;
&lt;h3&gt;1) KeyChain Access 실행&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;KeyChain Access&lt;/code&gt; 툴을 실행하고 &lt;code&gt;github.com&lt;/code&gt;으로 검색을 해보면 다음과 같이 기존에 저장된 계정 정보를 확인할 수 있다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/devops/git/issue-resolution/images/git-issue-resolution-delete-account-on-mac-2.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/devops/git/issue-resolution/images/git-issue-resolution-delete-account-on-mac-2.1.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;h3&gt;2) 계정 정보 삭제&lt;/h3&gt;
&lt;p&gt;다음 명령을 실행하여 기존에 등록된 git 계정의 정보(credentials)를 삭제한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ git credential-osxkeychain erase
host=github.com
protocol=https&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;위의 명령이 성공적으로 수행되면 아무런 메시지도 출력되지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그리고 다시 &lt;code&gt;KeyChain Access&lt;/code&gt; 툴로 가보면 이전에 등록되어 있던 계정 정보가 삭제된 것을 확인할 수 있다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/devops/git/issue-resolution/images/git-issue-resolution-delete-account-on-mac-2.2.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;h3&gt;3) 유저네임 및 이메일 변경&lt;/h3&gt;
&lt;p&gt;다음의 명령을 실행하여 유저네임과 이메일을 변경한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ git config --global user.name &amp;quot;githubusername&amp;quot;
$ git config --global user.email &amp;quot;githubaccount@email.com&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;이 때 &lt;code&gt;user.email&lt;/code&gt;에는 반드시 &lt;strong&gt;&lt;em&gt;Github 계정의 이메일 주소를 입력해야&lt;/em&gt;&lt;/strong&gt; 나중에 &lt;code&gt;Github&lt;/code&gt; 리포지토리에서 contributor로서 트랙킹하는 것이 가능하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;h3&gt;4) 새로운 계정으로 git push&lt;/h3&gt;
&lt;p&gt;이제 새로 등록된 계정으로 &lt;code&gt;git commit&lt;/code&gt;과 &lt;code&gt;git push&lt;/code&gt; 명령을 실행한다. 그러면 다음과 같이 새로 등록할 Github 계정의 아이디와 비밀번호를 입력하는 과정이 나타난다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/devops/git/issue-resolution/images/git-issue-resolution-delete-account-on-mac-2.4.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;그리고 새로운 계정으로 &lt;code&gt;git push&lt;/code&gt;가 완료된 후, 다시 &lt;code&gt;KeyChain Access&lt;/code&gt; 툴에 가보면 &lt;strong&gt;&lt;em&gt;새로운 계정 정보가 등록된&lt;/em&gt;&lt;/strong&gt; 것을 확인할 수 있다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/devops/git/issue-resolution/images/git-issue-resolution-delete-account-on-mac-2.4.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;648&quot;/&gt;

&lt;br/&gt;

&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;em&gt;Github. (?). &lt;a href=&quot;https://help.github.com/en/github/using-git/updating-credentials-from-the-osx-keychain&quot;&gt;Updating credentials from the OSX Keychain&lt;/a&gt; [Web Documentation]&lt;/em&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#깃&lt;/code&gt; &lt;code&gt;#깃헙&lt;/code&gt; &lt;code&gt;#깃허브&lt;/code&gt; &lt;code&gt;#깃 계정&lt;/code&gt; &lt;code&gt;#깃 계정 변경&lt;/code&gt; &lt;code&gt;#깃헙 계정&lt;/code&gt; &lt;code&gt;#깃헙 키체인&lt;/code&gt; &lt;code&gt;#깃헙 키체인 삭제&lt;/code&gt; &lt;code&gt;#Git&lt;/code&gt; &lt;code&gt;#Github&lt;/code&gt; &lt;code&gt;#git account&lt;/code&gt; &lt;code&gt;#git account delete&lt;/code&gt; &lt;code&gt;#git account switch&lt;/code&gt; &lt;code&gt;#git account change&lt;/code&gt; &lt;code&gt;#git user change&lt;/code&gt; &lt;code&gt;#git user switch&lt;/code&gt; &lt;code&gt;#how to change git user&lt;/code&gt; &lt;code&gt;#how to switch git user&lt;/code&gt; &lt;code&gt;#how to switch git account&lt;/code&gt; &lt;code&gt;#how to remove git credentials on macOS&lt;/code&gt; &lt;code&gt;#KeyChain Access&lt;/code&gt; &lt;code&gt;#git keychain&lt;/code&gt; &lt;code&gt;#github keychain&lt;/code&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;p&gt;© 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>DevOps/Git</category>
      <category>git change user</category>
      <category>git keychain</category>
      <category>git switch user</category>
      <category>git user</category>
      <category>깃</category>
      <category>깃 계정</category>
      <category>깃 계정 삭제</category>
      <category>깃 유저 변경</category>
      <category>깃허브</category>
      <category>깃헙</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/13</guid>
      <comments>https://architectophile.tistory.com/13#entry13comment</comments>
      <pubDate>Tue, 24 Mar 2020 15:53:41 +0900</pubDate>
    </item>
    <item>
      <title>[Nginx] 기본 설정 방법</title>
      <link>https://architectophile.tistory.com/12</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;nginx.png&quot; data-origin-width=&quot;2494&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k9XzT/btqCMnyJfA4/BOB5x2OnIXDpGPCepCS141/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k9XzT/btqCMnyJfA4/BOB5x2OnIXDpGPCepCS141/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k9XzT/btqCMnyJfA4/BOB5x2OnIXDpGPCepCS141/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk9XzT%2FbtqCMnyJfA4%2FBOB5x2OnIXDpGPCepCS141%2Fimg.png&quot; data-filename=&quot;nginx.png&quot; data-origin-width=&quot;2494&quot; data-origin-height=&quot;1436&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[Nginx] 기본 설정 방법&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Nginx는 가벼운 고성능의 웹서버로서 높은 트래픽 처리를 위해 디자인되었다.&lt;/p&gt;
&lt;p&gt;Nginx의 가장 강력한 기능 중 하나는 HTML이나 미디어 파일 같은 정적 컨텐츠를 효율적으로 서브하는 것이다. Nginx는 비동기적 이벤트 드리븐 모델(asynchronous event-driven model)을 사용하는데, 이것은 부하(load)에 대해 예측가능한 성능을 제공한다.&lt;/p&gt;
&lt;p&gt;Nginx는 동적 컨텐트에 대해서는 CGI, FastCGI, 또는 아파치(Apache)와 같은 다른 서버에게 처리를 맡긴다. 이러한 동적 컨텐츠는 다시 Nginx에게 전달되어 결과적으로 클라이언트에게 전달된다. 이 글은 당신이 Nginx의 파라미터와 컨벤션에 익숙하게 만들어줄 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. 디렉티브(Directives), 블록(Blocks), 그리고 컨텍스트(Contexts)&lt;/h2&gt;
&lt;p&gt;모든 nginx 컨피겨레이션 파일들은 &lt;code&gt;/etc/nginx&lt;/code&gt; 디렉토리 안에 위치한다. 가장 주요한 컨피겨레이션 파일은 &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; 파일이다.&lt;/p&gt;
&lt;p&gt;nginx에서 켠피겨레이션 옵션은 &lt;code&gt;디렉티브(directives)&lt;/code&gt;라고 불린다. 디렉티브는 &lt;code&gt;블록(blocks)&lt;/code&gt; 또는 &lt;code&gt;컨텍스트(contexts)&lt;/code&gt;라고 알려진 그룹으로 구성된다. 이 &lt;code&gt;블록&lt;/code&gt;과 &lt;code&gt;컨텍스트&lt;/code&gt;는 nginx에서 동일한 의미로 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#&lt;/code&gt; 문자로 시작하는 줄은 주석으로서 nginx에 의해 해석되지 않는다. 디렉티브를 나타낼 때는 반드시 &lt;code&gt;세미콜론(;)&lt;/code&gt;으로 끝나거나, 또는 &lt;code&gt;중괄호 블록({})&lt;/code&gt;로 끝나야 한다.&lt;/p&gt;
&lt;p&gt;아래에 보이는 것은 nginx 리포지토리에서 설치했을 때 디폴트로 포함되는 &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; 파일의 압축된 형태이다.&lt;br /&gt;이 컨피겨레이션 파일은 4개의 디렉티브(directives)로 시작한다: &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;worker_processes&lt;/code&gt;, &lt;code&gt;error_log&lt;/code&gt;, 그리고 &lt;code&gt;pid&lt;/code&gt;이다. 이 디렉티브들은 어떠한 특정한 &lt;code&gt;블록&lt;/code&gt;이나 &lt;code&gt;컨텍스트&lt;/code&gt; 안에 포함되지 않는다. 따라서 그들은 &lt;code&gt;메인 컨텍스트(main context)&lt;/code&gt; 안에 있다고 불린다. 그리고 &lt;code&gt;events&lt;/code&gt;와 &lt;code&gt;http&lt;/code&gt; 블록은 추가적인 디렉티브를 위한 공간이며, 그들 또한 &lt;code&gt;메인 컨텍스트&lt;/code&gt; 안에 존재한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
       . . .
}

http {
       . . .
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. http 블록&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;http&lt;/code&gt; 블록은 웹 트래픽을 처리하는 디렉티브들을 담고있다. 이 디렉티브들은 종종 &lt;i&gt;Universal&lt;/i&gt;으로 불리는데, 왜냐하면 그것들은 nginx가 서브하는 모든 웹사이트 컨피겨레이션에 전달되기 때문이다. &lt;code&gt;http&lt;/code&gt; 블록에서 사용되는 모든 디렉티브들의 목록은 &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_core_module.html&quot;&gt;Nginx 문서&lt;/a&gt;에서 찾아볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
                      '$status $body_bytes_sent &quot;$http_referer&quot; '
                      '&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;3. server 블록&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;위의 &lt;code&gt;http&lt;/code&gt; 블록 안에 있는 &lt;code&gt;include&lt;/code&gt; 디렉티브는 nginx에게 웹사이트 컨피겨레이션 파일이 어디에 위치해 있는지를 알려준다. 만약 nginx 리포지토리에서 설치했다면 위에서와 같이 &lt;code&gt;http&lt;/code&gt; 블록 안에 &lt;code&gt;include /etc/nginx/conf.d/*.conf;&lt;/code&gt; 디렉티브가 포함되어 있을 것이다. 호스팅하는 각 웹사이트는 반드시 &lt;code&gt;/etc/nginx/conf.d/&lt;/code&gt; 디렉토리 안에 &lt;code&gt;example.com.conf&lt;/code&gt; 형태로 만들어진 각자 자신만의 컨피겨레이션 파일을 갖고 있어야 한다. 그리고 사이트 중에서 비활성화된(disabled) 것들은 반드시 &lt;code&gt;example.com.conf.disabled&lt;/code&gt; 형태로 파일 이름을 설정해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만약 nginx를 Debian 또는 Ubuntu 리포지토리에서 설치했다면, &lt;code&gt;http&lt;/code&gt; 블록 안에는 &lt;code&gt;include /etc/nginx/sites-enabled/*;&lt;/code&gt; 디렉티브가 포함되어 있을 것이다. 그 &lt;code&gt;.../sites-enabled/&lt;/code&gt; 디렉토리는 &lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; 디렉토리 안의 컨피겨레이션 파일들을 가리키는 &lt;code&gt;심링크들(symlinks)&lt;/code&gt;을 담고 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;당신의 nginx 설치 소스에 따라 샘플 컨피겨레이션 파일은 &lt;code&gt;/etc/nginx/conf.d/default.conf&lt;/code&gt; 또는 &lt;code&gt;etc/nginx/sites-enabled/default&lt;/code&gt;에 위치할 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그리고 설치 소스와는 상관없이 서버 컨피겨레이션 파일은 다음과 같은 어떤 하나의 기본 &lt;code&gt;server&lt;/code&gt; 블록을 갖고 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen         80 default_server;
    listen         [::]:80 default_server;
    server_name    example.com www.example.com;
    root           /var/www/example.com;
    index          index.html;
    try_files $uri /index.html;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;4. 리스닝 포트(Listening Ports)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;listen&lt;/code&gt; 디렉티브는 nginx에게 HTTP 연결을 위해 필요한 &lt;code&gt;hostname/IP&lt;/code&gt;와 &lt;code&gt;TCP 포트(port)&lt;/code&gt;를 알려준다. &lt;code&gt;default_server&lt;/code&gt; 인자가 의미하는 것은 이 가상의 호스트가 다른 가상의 호스트들의 &lt;code&gt;listen statement&lt;/code&gt;와 매치되지 않는 모든 요청에 응답한다는 것이다. 그리고 두 번째 줄의 &lt;code&gt;listen&lt;/code&gt; 디렉티브는 IPv6 형식의 요청을 처리한다는 것이고 &lt;code&gt;default_server&lt;/code&gt;가 의미하는 것은 앞서 설명한 것과 동일하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;5. 이름 기반의 가상 호스팅(Name-Based Virtual Hosting)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;server_name&lt;/code&gt; 디렉티브는 하나의 IP 주소에 대해 여러개의 도메인(domains)을 사용할 수 있게 한다. 서버는 전달받은 요청 헤더(request header)에 기반하여 어떤 도메인을 서브할지 결정한다.&lt;/p&gt;
&lt;p&gt;일반적으로 호스트하고자 하는 하나의 도메인 또는 하나의 사이트당 하나의 파일을 생성한다. 다음의 예제들이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;example.com&lt;/code&gt;과 &lt;code&gt;www.example.com&lt;/code&gt; 두 개 모두의 도메인에 대한 요청을 처리하는 경우에는 다음과 같이 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/conf.d/example.com.conf

server {
    server_name   example.com www.example.com;
      . . .
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;code&gt;server_name&lt;/code&gt; 디렉티브는 또한 &lt;code&gt;와일드카드(*)&lt;/code&gt;를 사용할 수 있다. &lt;code&gt;*.example.com&lt;/code&gt;과 &lt;code&gt;.example.com&lt;/code&gt;는 둘 다 &lt;code&gt;example.com&lt;/code&gt;의 모든 &lt;b&gt;서브도메인들(subdomains)&lt;/b&gt;에 대한 요청을 처리하도록 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/conf.d/example.com.conf

server {    
    server_name   *.example.com;  # 또는 .example.com;
        . . .
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;code&gt;example.&lt;/code&gt;으로 시작하는 모든 도메인에 대한 요청을 처리하는 경우 다음과 같이 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/conf.d/example.com.conf

server {    
    server_name   example.*;
        . . .
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nginx는 유효하지 않은 도메인 네임을 &lt;code&gt;server_name&lt;/code&gt;에 사용할 수 있도록 한다. nginx는 요청의 HTTP 헤더에 있는 이름을 사용하여 요청에 응답하며, 해당 도메인 네임이 유효한지 아닌지는 상관없다.&lt;/p&gt;
&lt;p&gt;도메인 네임이 아닌 호스트네임(non-domain hostname)을 사용하는 것은 서버가 LAN에 있거나 또는 이미 서버에 요청을 보낼 클라이언트들을 알고 있는 경우 유용하다. 예를 들면 이것은 nginx가 듣고있는 IP 주소들로 설정된 &lt;code&gt;/etc/hosts&lt;/code&gt; 엔트리를 사용하는 전면 프록시 서버(front-end proxy servers)를 사용할 때 유용하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;6. location 블록&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;location&lt;/code&gt; 디렉티브는 서버 안의 리소스에 대한 요청을 어떻게 응답해야 할지를 설정한다. &lt;code&gt;server_name&lt;/code&gt; 디렉티브가 nginx에게 해당 도메인에 대한 요청을 어떻게 처리해야 하는지 설정하는 것처럼, &lt;code&gt;location&lt;/code&gt; 디렉티브는 특정 파일과 특정 디렉토리에 대한 요청을 처리한다. 예를 들면, &lt;code&gt;http://example.com/blog/&lt;/code&gt;와 같은 요청에 대한 처리를 설정한다. 다음과 같은 예가 있다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location / {
        . . .
    }

    location /images/ { 
        . . .
    }

    location /blog/ { 
        . . .
    }

    location /planet/ { 
        . . .
    }

    location /planet/blog/ { 
        . . .
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 &lt;code&gt;location&lt;/code&gt; 디렉티브들은 &lt;code&gt;리터럴 스트링(literal string)&lt;/code&gt;에 대한 매치이며, 호스트 세그멘트(host segment) 이후에 오는 HTTP 요청에서 어떠한 부분이라도 매치된다. 위와 같은 &lt;code&gt;location&lt;/code&gt; 디렉티브 설정에서 다음과 같은 몇 가지 요청들에 대한 예를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; &lt;code&gt;example.com&lt;/code&gt;을 위한 &lt;code&gt;server_name&lt;/code&gt; 엔트리가 존재한다는 가정하에, 위의 &lt;code&gt;location /&lt;/code&gt; 디렉티브가 요청에 대해 어떻게 응답할지를 결정할 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;Note:&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;nginx는 항상 &lt;b&gt;&lt;i&gt;가장 구체적인 매치(the most specific match)&lt;/i&gt;&lt;/b&gt;에 대해 요청을 처리할 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/planet/blog/&lt;/code&gt; 또는 &lt;code&gt;http://example.com/planet/blog/about/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; 이것은 &lt;code&gt;location /planet/blog/&lt;/code&gt; 디렉티브가 요청에 대해 어떻게 응답할지를 결정할 것이다. 비록 &lt;code&gt;location /planet/&lt;/code&gt; 디렉티브도 매치되긴 하지만 &lt;b&gt;&lt;i&gt;가장 구체적인 매치가 요청을 처리한다&lt;/i&gt;&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location ~ IndexPage\.php$ {
        . . .
    }

    location ~ ^/BlogPlanet(/|/index\.php)$ { 
        . . .
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 &lt;code&gt;location&lt;/code&gt; 디렉티브 뒤에 &lt;b&gt;&lt;code&gt;틸드(~)&lt;/code&gt;&lt;/b&gt; 문자가 올 경우, nginx는 &lt;code&gt;레귤러 익스프레션(regular expression)&lt;/code&gt; 매치를 수행한다. 위의 매치 예제들은 대소문자를 구분(case-sensitive)하기 때문에 위의 예제에서 첫 번째 location 디렉티브에 대해 &lt;code&gt;IndexPage.php&lt;/code&gt;는 매치가되지만 &lt;code&gt;indexpage.php&lt;/code&gt;는 매치되지 않는다.&lt;/p&gt;
&lt;p&gt;위의 예제에서 두 번째 location 디렉티브에 대해 &lt;code&gt;/BlogPlanet/&lt;/code&gt;과 &lt;code&gt;/BlogPlanet/index.php&lt;/code&gt;는 매치되지만, &lt;code&gt;/BlogPlanet&lt;/code&gt;, &lt;code&gt;/blogplanet/&lt;/code&gt;, 또는 &lt;code&gt;/blogplanet/index.php&lt;/code&gt;는 매치되지 않는다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;Note:&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;nginx는 &lt;a href=&quot;https://perldoc.perl.org/perlre.html&quot;&gt;Perl Compatible Regular Expressions(PCRE)&lt;/a&gt;을 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location ~* \.(pl|cgi|perl|prl)$ {
        . . .
    }

    location ~* \.(md|mdwn|txt|mkdn)$ { 
        . . .
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 레귤러 익스프레션을 대소문자 구분없이(case-insensitive) 매치되도록 하고 싶다면 &lt;code&gt;a tiled and asterisk(~*)&lt;/code&gt;를 사용하도록 한다. 위의 예제에서는 특정 파일 확장자로 끝나는 요청들을 어떻게 처리할지를 나타낸다. 첫 번째 location 디렉티브는 &lt;code&gt;.pl&lt;/code&gt;, &lt;code&gt;.PL&lt;/code&gt;, &lt;code&gt;.cgi&lt;/code&gt;, &lt;code&gt;.CGI&lt;/code&gt;, &lt;code&gt;.perl&lt;/code&gt;, &lt;code&gt;.Perl&lt;/code&gt;, &lt;code&gt;.prl&lt;/code&gt;, 그리고 &lt;code&gt;.PrL&lt;/code&gt;로 끝나는 어떠한 요청에 대해서도 매치될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location ^~ /images/IndexPage/ {
        . . .
    }

    location ^~ /blog/BlogPlanet/ {
        . . .
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 location 디렉티브 뒤에 &lt;code&gt;a caret and tilde(^~)&lt;/code&gt;를 붙인다면, nginx는 특정 스트링과 매치가 있으면 더 이상 다른 매치를 찾기위해 진행하지 않고 바로 그 매치되는 디렉티브를 사용한다. 다른 곳에 더욱 구체적으로 매치되는 디렉티브가 있더라도, 해당 요청이 이러한 디렉티브 중에 하나에 매치된다면 곧바로 그 디렉티브가 사용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location = / {
        . . .
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마지막으로 location 디렉티브 뒤에 &lt;code&gt;an equals sign(=)&lt;/code&gt;를 붙인다면, 요청된 경로(path)에 대해서 &lt;b&gt;&lt;i&gt;정확히 일치하는 매치&lt;/i&gt;&lt;/b&gt;를 찾으면 다른 location 디렉티브에 대한 서치를 마치고 그 정확히 일치하는 디렉티브를 사용한다. 따라서 위의 예제에서는 &lt;code&gt;http://example.com/&lt;/code&gt;는 정확히 일치하므로 매치되지만 &lt;code&gt;http://example.com/index.html&lt;/code&gt;는 매치되지 않는다. 정확한 매치를 사용하는 것은 요청 처리 시간을 줄이는데 도움이 되는데, 특정 경로의 요청이 자주 발생한다면 매우 유용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;최종 정리된 디렉티브 매칭 처리 순서는 다음과 같다.&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;정확한 스트링 매치(exact string matches)를 먼저 찾는다. 만야 매치가 발견될 경우 nginx는 더 이상 서치를 멈추고 해당 디렉티브를 사용하여 요청을 처리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 다음 나머지 리터럴 스트링 매치(literal string matches)를 찾는다. nginx는 만약 &lt;code&gt;^~&lt;/code&gt;를 가진 매치를 발견했을 경우 서치를 중단하고 해당 디렉티를 사용하여 요청을 처리한다. 만약 &lt;code&gt;^~&lt;/code&gt; 매치가 없을 경우 다음의 리터럴 스트링 매치를 계속 찾으며 가장 구체적인 매치를 저장해놓는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 다음 레귤러 익스프레션(&lt;code&gt;~&lt;/code&gt; 또는 &lt;code&gt;~*&lt;/code&gt;)을 가진 매치를 찾는다. 만약 요청과 매치되는 레귤러 익스프레션을 찾을 경우 서치를 중단하고 해당 디렉티브를 사용하여 요청을 처리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만약 레귤러 익스프레션과 매치되는 디렉티브를 발견하지 못한다면, 현재 저장되어 있는 가장 구체적으로 매치되는 리터럴 스트링 매치(literal string match)가 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;Note:&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;Nginx에서 &lt;code&gt;location&lt;/code&gt; 디렉티브 안에 &lt;code&gt;네스티드 블록(nested blocks)&lt;/code&gt;은 지원하지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;7. location 블록의 root와 index&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;location&lt;/code&gt; 디렉티브는 자체적인 인자값(arguments)을 갖고 있는 블록이다.&lt;/p&gt;
&lt;p&gt;nginx가 어떤 &lt;code&gt;location&lt;/code&gt; 디렉티브가 요청에 대해 가장 적절한 매치인지 결정하면, 이 요청에 대한 응답은 해당 &lt;code&gt;location&lt;/code&gt; 디렉티브 블록 안에 있는 관련된 컨텐츠에 의해 결정된다. 다음과 같은 예제를 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location / {
        root html;
        index index.html index.htm;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 예제에서 해당 location의 문서의 root는 &lt;code&gt;html/&lt;/code&gt; 디렉토리에 있다. nginx의 디폴트 프리픽스에 의해서 전체 경로는 &lt;code&gt;/etc/nginx/html/&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/blog/includes/style.css&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; nginx는 &lt;code&gt;/etc/nginx/html/blog/includes/style.css&lt;/code&gt;에 위치한 파일을 서브할 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;Note:&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;code&gt;root&lt;/code&gt; 디렉티브에는 &lt;b&gt;&lt;i&gt;절대경로(absolute paths) 또한 사용할 수 있다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;index&lt;/code&gt; 디렉티브는 아무런 파일명이 명시되어 있지 않을 때 어떤 것을 서브해야 하는지를 알려준다. 다음 예를 보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; nginx는 &lt;code&gt;/etc/nginx/html/index.html&lt;/code&gt;에 있는 파일을 서브할 것이다.&lt;/p&gt;
&lt;p&gt;만약 &lt;code&gt;index&lt;/code&gt; 디렉티브에 여러개의 파일이 명시되어 있다면, nginx는 파라미터들이 쓰여진 순서대로 처리하며, 그 중에서 파일이 존재하면 응답할 것이다. 즉 위의 예제에서는 &lt;code&gt;index.html&lt;/code&gt; 파일을 먼저 찾아보고 없을 경우에는 다음으로 &lt;code&gt;index.htm&lt;/code&gt; 파일을 찾아보고 이것도 없을 경우 &lt;code&gt;404 오류(Not Found)&lt;/code&gt;를 응답한다.&lt;/p&gt;
&lt;p&gt;다음은 도메인 &lt;code&gt;example.com&lt;/code&gt;에 대응하는 서버의 조금 더 복잡한 형태의 &lt;code&gt;location&lt;/code&gt; 디렉티브들을 보여주고 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;# /etc/nginx/sites-available/example.com

server {
    location / {
        root   /srv/www/example.com/public_html;
        index  index.html index.htm;
    }

    location ~ \.pl$ {
        gzip off;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        fastcgi_index index.pl;
        fastcgi_param SCRIPT_FILENAME /srv/www/example.com/public_html$fastcgi_script_name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제에서, 확장자가 &lt;code&gt;.pl&lt;/code&gt;로 끝나는 모든 요처들은 두 번재 &lt;code&gt;location&lt;/code&gt; 블록에 의해 처리되며, 그것은 &lt;code&gt;fastcgi&lt;/code&gt; 핸들러를 명시하여 요청들을 처리한다. 그 외 나머지 요청에 대해서는 nginx는 첫 번째 &lt;code&gt;location&lt;/code&gt; 디렉티브를 사용한다.&lt;br /&gt;리소스들은 파일 시스템의 &lt;code&gt;/srv/www/example.com/public_html/&lt;/code&gt; 디렉토리에 위치한다. 만약 요청에서 파일 이름이 명시되지 않을 경우에는 nginx는 &lt;code&gt;index.html&lt;/code&gt; 또는 &lt;code&gt;index.htm&lt;/code&gt; 파일을 제공할 것이다. 만약 두 개의 파일 모두 존재하지 않을 경우 서버는 &lt;code&gt;404 오류(Not Found)&lt;/code&gt;를 응답한다.&lt;/p&gt;
&lt;p&gt;몇 가지 요청들에 대해서 위의 예제 서버가 어떻게 응답하는지 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; 우선 &lt;code&gt;/srv/www/example.com/public_html/index.html&lt;/code&gt; 파일을 찾아보고 만약 존재하지 않으면 &lt;code&gt;/srv/www/example.com/public_html/index.htm&lt;/code&gt; 파일을 찾아볼 것이다. 만약 둘 다 존재하지 않으면 서버는 &lt;code&gt;404 오류(Not Found)&lt;/code&gt;를 응답한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/blog/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; 우선 &lt;code&gt;/srv/www/example.com/public_html/blog/index.html&lt;/code&gt; 파일을 찾아보고 만약 존재하지 않으면 &lt;code&gt;/srv/www/example.com/public_html/blog/index.htm&lt;/code&gt;파일을 찾아볼 것이다. 만약 둘 다 존재하지 않으면 서버는 &lt;code&gt;404 오류(Not Found)&lt;/code&gt;를 응답한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/tasks.pl&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; nginx는 &lt;code&gt;FastCGI&lt;/code&gt; 핸들러를 사용하여 &lt;code&gt;/srv/www/example.com/public_html/tasks.pl&lt;/code&gt;에 존재하는 파일을 실행하고 결과를 응답한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;요청(Request):&lt;/b&gt; &lt;code&gt;http://example.com/username/roster.pl&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;응답(Response):&lt;/b&gt; nginx는 &lt;code&gt;FastCGI&lt;/code&gt; 핸들러를 사용하여 &lt;code&gt;/srv/www/example.com/public_html/username/roster.pl&lt;/code&gt;에 존재하는 파일을 실행하고 결과를 응답한다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;i&gt;Linode. (2019, Apr 5). &lt;a href=&quot;https://www.linode.com/docs/web-servers/nginx/how-to-configure-nginx&quot;&gt;How to Configure NGINX&lt;/a&gt; [Web Document]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;[2] &lt;i&gt;Nginx. (?). &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_core_module.html&quot;&gt;Module ngx_http_core_module&lt;/a&gt; [Web Document]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;[3] &lt;i&gt;Perl. (?). &lt;a href=&quot;https://perldoc.perl.org/perlre.html&quot;&gt;Perl Compatible Regular Expressions(PCRE)&lt;/a&gt; [Web Document]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#엔진엑스&lt;/code&gt; &lt;code&gt;#엔진엑스 설정&lt;/code&gt; &lt;code&gt;#엔진엑스 가이드&lt;/code&gt; &lt;code&gt;#엔진엑스 매뉴얼&lt;/code&gt; &lt;code&gt;#엔진엑스 설정&lt;/code&gt; &lt;code&gt;#엔진엑스 컨피겨레이션&lt;/code&gt; &lt;code&gt;#Nginx 설정&lt;/code&gt; &lt;code&gt;#nginx 서버&lt;/code&gt; &lt;code&gt;#nginx 가이드&lt;/code&gt; &lt;code&gt;#nginx 매뉴얼&lt;/code&gt; &lt;code&gt;#Nginx&lt;/code&gt; &lt;code&gt;#Nginx Guide&lt;/code&gt; &lt;code&gt;#Nginx Manual&lt;/code&gt; &lt;code&gt;#nginx.conf&lt;/code&gt; &lt;code&gt;#Nginx Config&lt;/code&gt; &lt;code&gt;#Nginx Configuration&lt;/code&gt; &lt;code&gt;#Nginx Directives&lt;/code&gt; &lt;code&gt;#Nginx Blocks&lt;/code&gt; &lt;code&gt;Nginx Contexts&lt;/code&gt; &lt;code&gt;#nginx http&lt;/code&gt; &lt;code&gt;#nginx server&lt;/code&gt; &lt;code&gt;#nginx location&lt;/code&gt; &lt;code&gt;#nginx proxy server&lt;/code&gt; &lt;code&gt;#nginx request&lt;/code&gt; &lt;code&gt;#nginx response&lt;/code&gt; &lt;code&gt;#sites-available&lt;/code&gt; &lt;code&gt;#sites-enabled&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;copy; 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Frameworks/Nginx</category>
      <category>nginx</category>
      <category>nginx block</category>
      <category>nginx directive</category>
      <category>nginx http</category>
      <category>nginx location</category>
      <category>nginx manual</category>
      <category>nginx proxy server</category>
      <category>nginx server</category>
      <category>nginx 설정</category>
      <category>nginx.conf</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/12</guid>
      <comments>https://architectophile.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 19 Mar 2020 09:38:44 +0900</pubDate>
    </item>
    <item>
      <title>[Nginx] 초보자 가이드</title>
      <link>https://architectophile.tistory.com/11</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;nginx.png&quot; data-origin-width=&quot;2494&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deQIEi/btqCO2tqKD6/tyNjWGeqqAKZez0izyKukk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deQIEi/btqCO2tqKD6/tyNjWGeqqAKZez0izyKukk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deQIEi/btqCO2tqKD6/tyNjWGeqqAKZez0izyKukk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeQIEi%2FbtqCO2tqKD6%2FtyNjWGeqqAKZez0izyKukk%2Fimg.png&quot; data-filename=&quot;nginx.png&quot; data-origin-width=&quot;2494&quot; data-origin-height=&quot;1436&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[Nginx] 초보자 가이드&lt;/h1&gt;
&lt;br/&gt;

&lt;h2&gt;1. Nginx 기본 설명&lt;/h2&gt;
&lt;p&gt;nginx는 &lt;strong&gt;&lt;em&gt;1개의 마스터 프로세스(master process)&lt;/em&gt;&lt;/strong&gt;와 &lt;strong&gt;&lt;em&gt;여러개의 워커 프로세스들(worker processes)&lt;/em&gt;&lt;/strong&gt;을 가지고 있다.&lt;br&gt;마스터 프로세스의 주요 역할은 설정(configuration)을 읽고 평가하고, 워커 프로세스들을 관리하는 것이다.&lt;br&gt;반면 워커 프로세스들의 역할은 실질적인 요청들(requests)을 처리하는 것이다. nginx는 이벤트 기반 모델(event-based model) 사용하며, 운영체제 의존적인(OS-dependent) 메커니즘을 활용하여 워커 프로세스들 사이의 요청을 효율적으로 분배한다.&lt;br&gt;워커 프로세스의 개수는 컨피겨레이션 파일 안에 정의되어 있는데, 설정에 따라 절대적인 개수로 고정될 수도 있고 또는 가용한 CPU 코어의 개수에 따라 자동으로 조절될 수도 있다.&lt;/p&gt;
&lt;p&gt;nginx와 그 모듈들이 동작하는 방식은 컨피겨레이션 파일 안에서 결정된다. 디폴트로는 그 컨피겨레이션 파일 이름은 &lt;code&gt;nginx.conf&lt;/code&gt;로 되어 있으며, 그 파일이 위치하는 디렉토리는 &lt;code&gt;/usr/local/nginx/conf&lt;/code&gt;, &lt;code&gt;/etc/nginx&lt;/code&gt;, 또는 &lt;code&gt;/usr/local/etc/nginx&lt;/code&gt;이다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. Nginx 시작, 중지 그리고 리로딩 설정 방법&lt;/h2&gt;
&lt;p&gt;nginx를 시작하기 위해서는 우선 실행파일(executable file)을 실행한다. nginx가 시작되면 실행파일을 &lt;code&gt;-s&lt;/code&gt; 파라미터와 함께 실행하여 컨트롤할 수 있다. 사용 문법은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nginx -s [signal]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;signal&lt;/code&gt;의 종류는 다음과 같이 4가지가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stop : 빠른 종료(fast shutdown)&lt;/li&gt;
&lt;li&gt;quit : 우아한 종료(graceful shutdown)&lt;/li&gt;
&lt;li&gt;reload : 컨피겨레이션 파일(configuration file) 다시 로딩하기&lt;/li&gt;
&lt;li&gt;reopen : 로그 파일 다시 열기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어, nginx를 종료할 때 워커 프로세스들이 현재 처리하고 있는 요청들을 모두 완료할 때까지 기다리고 싶을 때는 다음과 같은 명령을 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ nginx -s quit&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;위 명령은 nginx를 실행시킨 유저와 동일한 유저에 의해 실행되어야 한다. 만약 nginx를 실행한 유저가 root 유저일 경우 권한 획득을 위해 &lt;code&gt;sudo&lt;/code&gt;와 같은 명령을 함께 사용해야 한다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;컨피겨레이션 파일이 변경되었을 경우, nginx에 &lt;code&gt;reload&lt;/code&gt; 명령을 보내거나 nginx를 재시작하기 전까지는 변경된 설정은 적용되지 않는다. 컨피겨레이션을 리로드하기 위해서는 다음과 같은 명령을 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마스터 프로세스가 &lt;code&gt;reload&lt;/code&gt; 시그널을 전달받은 경우, 그것은 우선 새로운 컨피겨레이션 파일의 문법 유효성 검사를 마친 뒤, 새로운 설정을 적용한다. 만약 문법 검사에서 이상이 없을 경우, 마스터 프로세스는 새로운 워커 프로세스들을 실행시키고, 예전 워커 프로세스들에게는 메시지를 보내 종료 요청을 한다. 예전 워커 프로세스들은 종료 요청을 받으면 더 이상 새로운 요청은 거부하고 기존에 처리하던 요청들을 마무리한 뒤 종료한다.&lt;/p&gt;
&lt;p&gt;하지만 만약 문법 검사에 이상이 있거나 오류가 발생했을 경우에는 마스터 프로세스는 변경 내용을 롤백(roll back)하고 이전 설정(configuration)으로 계속 작업한다.&lt;/p&gt;
&lt;p&gt;signal은 &lt;code&gt;kill&lt;/code&gt;과 같은 Unix 툴을 사용하여 nginx 프로세스에게 전달될 수 있다. 이 경우에는 signal은 &lt;code&gt;프로세스 ID&lt;/code&gt;를 이용해 프로세스에게 직접 전달된다. 그 nginx 마스터 프로세스의 &lt;code&gt;프로세스 ID&lt;/code&gt;는 기본적으로 &lt;code&gt;/usr/local/nginx/logs&lt;/code&gt; 또는 &lt;code&gt;/var/run&lt;/code&gt; 디렉토리에 있는 &lt;code&gt;nginx.pid&lt;/code&gt; 파일에 쓰여있다. 만약 마스터 프로세스의 프로세스 ID가 1628일 경우, nginx에게 우아한 종료(graceful shutdown) 요청을 보내기 위해서는 다음과 같이 명령을 사용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ kill -s QUIT 1628&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행 중인 모든 nginx 프로세스들의 목록을 얻기 위해서는 &lt;code&gt;ps&lt;/code&gt; 유틸리티를 사용하여 다음과 같이 명령을 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ ps -ax | grep nginx&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;3. 컨피겨레이션 파일(configuration file) 구조&lt;/h2&gt;
&lt;p&gt;nginx는 여러 개의 모듈들로 구성되어 있는데, 그것들은 컨피겨레이션 파일 안의 &lt;code&gt;디렉티브(directives)&lt;/code&gt;에 의해 제어된다. 디렉티브들은 &lt;code&gt;심플 디렉티브(simple directives)&lt;/code&gt;와 &lt;code&gt;블록 디렉티브(block directives)&lt;/code&gt;로 나뉜다. &lt;code&gt;심플 디렉티브&lt;/code&gt;의 이름과 파라미터는 스페이스(spaces)에 의해 구분되고 &lt;code&gt;세미콜론(;)&lt;/code&gt;으로 끝난다. &lt;code&gt;블록 디렉티브&lt;/code&gt;는 심플 디렉티브와 기본적으로는 동일한 구조를 가지는데, 하지만 세미콜론 대신에 그것은 &lt;code&gt;중괄호(braces)&lt;/code&gt;로 둘러싸인 추가적인 명령들의 집합이 뒤에 붙는다. 그리고 만약 블록 디렉티브가 중괄호 안에 다른 디렉티브를 가질 수 있으면 그것은 &lt;code&gt;컨텍스트(context)&lt;/code&gt;라고 불린다(e.g. &lt;code&gt;events&lt;/code&gt;, &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt; 그리고 &lt;code&gt;location&lt;/code&gt; 등).&lt;/p&gt;
&lt;p&gt;컨피겨레이션 파일 안에서 다른 컨텍스트 밖에 있는 디렉티브들은 모두 &lt;code&gt;메인 컨텍스트(main context)&lt;/code&gt; 안에 있는 것으로 본다.&lt;br&gt;&lt;code&gt;events&lt;/code&gt;와 &lt;code&gt;http&lt;/code&gt; 디렉티브는 &lt;code&gt;메인&lt;/code&gt; 컨텍스트 안에 있는 것이고, &lt;code&gt;server&lt;/code&gt; 디렉티브는 &lt;code&gt;http&lt;/code&gt; 컨텍스트 안에, 그리고 &lt;code&gt;location&lt;/code&gt; 디렉티브는 &lt;code&gt;server&lt;/code&gt; 컨텍스트 안에 있는 것이다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;code&gt;#&lt;/code&gt; 기호 이후에 오는 모든 줄은 주석으로 간주된다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;4. 정적 컨텐트(static content) 서빙&lt;/h2&gt;
&lt;p&gt;웹 서버의 중요한 업무는 파일들(e.g. images 또는 정적 HTML pages 등)을 서빙하는 것이다. 이제 우리는 서버에 들어오는 요청에 따라서 서로 다른 로컬 디렉토리들(&lt;code&gt;/data/www&lt;/code&gt;(HTML 파일 디렉토리) 그리고 &lt;code&gt;/data/images&lt;/code&gt;(이미지 파일 디렉토리))에 있는 파일들이 서빙되는 예제를 구현할 것이다. 이를 위해서 컨피겨레이션 파일을 수정해야 하는데, &lt;code&gt;http&lt;/code&gt; 블록 안에 &lt;code&gt;server&lt;/code&gt; 블록을 설정하고, 그 &lt;code&gt;server&lt;/code&gt; 블록 안에는 두 개의 &lt;code&gt;location&lt;/code&gt; 블록들을 설정할 것이다.&lt;/p&gt;
&lt;p&gt;우선, &lt;code&gt;/data/www&lt;/code&gt; 디렉토리를 생성하고 그 안에 &lt;code&gt;index.html&lt;/code&gt; 파일을 만들고 아무런 텍스트 내요을 작성한다. 그리고 &lt;code&gt;/data/images/&lt;/code&gt; 디렉토리를 만들고 그 안에 몇 장의 이미지 파일들을 넣는다.&lt;/p&gt;
&lt;p&gt;그 다음 컨피겨레이션 파일을 연다. 디폴트 컨피겨레이션 파일 안에는 이미 몇 개의 &lt;code&gt;server&lt;/code&gt; 블록에 대한 예제들이 포함되어 있는데, 대부분은 주석으로 처리되어있다. 이제 다른 모든 블록들을 주석으로 처리하고 새로운 &lt;code&gt;server&lt;/code&gt; 블록을 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;http {
    server {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일반적으로 컨피겨레이션 파일 안에는 여러개의 &lt;code&gt;server&lt;/code&gt; 블록들을 포함할 수 있는데, 그것들은 사용하는 &lt;code&gt;포트번호&lt;/code&gt; 또는 &lt;code&gt;서버이름&lt;/code&gt;에 따라 나뉘어져있다. 어떤 요청이 들어오면 nginx는 우선 어떤 &lt;code&gt;server&lt;/code&gt; 블록이 해당 요청을 처리할지 결정하고, 그 다음은 요청 헤더(header)에 있는 &lt;code&gt;URI&lt;/code&gt;를 server 블록 안의 &lt;code&gt;location&lt;/code&gt; 디렉티브들의 &lt;code&gt;파라미터(parameters)&lt;/code&gt;와 대조하여 검사한다.&lt;/p&gt;
&lt;p&gt;이제 &lt;code&gt;server&lt;/code&gt; 블록 안에 다음의 &lt;code&gt;location&lt;/code&gt; 블록을 추가한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;location / {
    root /data/www;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 &lt;code&gt;location&lt;/code&gt; 블록은 요청에서 URI와 비교하여 &amp;quot;/&amp;quot; 프리픽스(prefix)를 나타낸다. 매칭되는 요청에 대해서 &lt;code&gt;root&lt;/code&gt; 디렉티브에 설정된 경로(path)에 해당 URI가 붙여진다. 즉, &lt;code&gt;/data/www&lt;/code&gt;에 붙여져서 로컬 파일 시스템에 있는 요청된 파일에 대한 경로를 형성한다.&lt;br&gt;만약 &lt;code&gt;location&lt;/code&gt; 블록들 중에 매칭되는 것이 여러개 있다면, nginx는 그 중 가장 긴 프리픽스(prefix)를 가진 것을 선택한다. 위의 &lt;code&gt;location&lt;/code&gt; 블록은 길이 1의 가장 짧은 프리픽스를 제공한다. 따라서 다른 모든 &lt;code&gt;location&lt;/code&gt; 블록들이 매칭에 실패할 경우 이 블록이 사용될 것이다.&lt;/p&gt;
&lt;p&gt;이제 아래의 두 번째 &lt;code&gt;location&lt;/code&gt; 블록을 추가한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;location /images/ {
    root /data;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 location 블록은 &lt;code&gt;/images/&lt;/code&gt;로 시작하는 요청에 대해 매치가될 것이다(&lt;code&gt;location /&lt;/code&gt; 블록에도 역시 매치될 것이지만 이것은 가장 짧은 길이의 프리픽스이다.)&lt;/p&gt;
&lt;p&gt;현재까지 작성된 &lt;code&gt;server&lt;/code&gt; 블록의 모습은 아래와 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    location / {
        root /data/www;
    }

    location /images/ {
        root /data;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것은 이미 포트 80에서 듣고 있는 서버로 동작하는 컨피겨레이션이며, 그것은 로컬 머신에서 &lt;code&gt;http://localhost/&lt;/code&gt;로 접속이 가능하다.&lt;br&gt;그리고 &lt;code&gt;/images/&lt;/code&gt;로 시작하는 URI를 가진 요청에 대해서는 서버는 &lt;code&gt;/data/images&lt;/code&gt; 디렉토리에서 파일을 전송할 것이다.&lt;br&gt;예를 들어, &lt;code&gt;http://localhost/images/example.png&lt;/code&gt; 요청에 대한 응답으로 nginx는 &lt;code&gt;/data/images/example.png&lt;/code&gt; 파일을 전송할 것이다. 만약 그 파일이 존재하지 않을 경우 nginx는 &lt;code&gt;404 오류(Not Found)&lt;/code&gt;를 전송할 것이다.&lt;br&gt;그리고 &lt;code&gt;/images/&lt;/code&gt;로 시작하지 않는 URI를 가진 요청은 &lt;code&gt;/data/www&lt;/code&gt; 디렉토리로 맵핑될 것이다. 예를 들어, &lt;code&gt;http://localhost/some/example.html&lt;/code&gt; 요청에 대한 응답으로 nginx는 &lt;code&gt;/data/www/some/example.html&lt;/code&gt; 파일을 전송할 것이다.&lt;/p&gt;
&lt;p&gt;새로운 컨피겨레이션을 적용하기 위해서, 아직 nginx를 실행하지 않았으면 nginx를 시작시키고, 이미 샐행 중인 경우 다음과 같이 &lt;code&gt;reload&lt;/code&gt; 시그널을 마스터 프로세스에 전송한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;만약 어떤 것이 예상한대로 동작하지 않을 경우, &lt;code&gt;/usr/local/nginx/logs&lt;/code&gt; 또는 &lt;code&gt;/var/log/nginx&lt;/code&gt; 디렉토리 안에 있는 &lt;code&gt;access.log&lt;/code&gt; 또는 &lt;code&gt;error.log&lt;/code&gt; 파일을 확인해 볼 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;h2&gt;5. 간단한 프록시 서버(proxy server) 세팅&lt;/h2&gt;
&lt;p&gt;nginx의 가장 흔한 사용 중 하나는 그것을 프록시 서버로 사용하는 것이다. 따라서 프로시 서버는 요청을 받으면 프록시된 다른 서버에게 요청을 전달하고, 그들에게 받은 응답을 결과적으로 클라이언트에게 전송한다.&lt;/p&gt;
&lt;p&gt;우리는 기본적인 프록시 서버를 하나 설정할 것인데, 그것은 로컬 디렉토리에 있는 이미지 파일에 대한 요청을 처리하고, 나머지 모든 요청들은 프록시된 서버에게 전달한다. 이 예제에서, 두 서버 모두 하나의 nginx 인스턴스에서 정의된다.&lt;/p&gt;
&lt;p&gt;우선, 프록시된 서버를 하나 정의할 것인데, 다음과 같이 컨피겨레이션 파일에 하나의 &lt;code&gt;server&lt;/code&gt; 블록을 추가한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 8080;
    root /data/up1;

    location / {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것은 포트 8080을 듣고 있는 간단한 서버가 될 것인데(이전에는 &lt;code&gt;listen&lt;/code&gt; 디렉티브를 생략했었는데, 왜냐하면 표준 포트인 80번이 사용되었기 때문이다.) 이 서버는 모든 요청을 로컬 파일 시스템의 &lt;code&gt;/data/up1&lt;/code&gt; 디렉토리로 맵핑할 것이다. 따라서 &lt;code&gt;/data/up1&lt;/code&gt; 디렉토리를 생성하고 이 안에 새로운 &lt;code&gt;index.html&lt;/code&gt; 파일을 만들어 넣도록한다. 이 때 잘 보아야할 것은 &lt;code&gt;root&lt;/code&gt; 디렉티브가 &lt;code&gt;server&lt;/code&gt; 컨텍스트 안에 위치한다는 점이다. 그러한 &lt;code&gt;root&lt;/code&gt; 디렉티브는 어떤 요청에 대해 선택된 &lt;code&gt;location&lt;/code&gt; 블록이 자체적인 &lt;code&gt;root&lt;/code&gt; 디렉티브를 갖고 있지 않을 때 사용된다.&lt;/p&gt;
&lt;p&gt;다음으로, 이전에 작성했던 &lt;code&gt;server&lt;/code&gt; 컨피겨레이션을 수정하여 프록시 컨피겨레이션으로 만들어보자. 첫 번째 &lt;code&gt;location&lt;/code&gt; 블록에는 &lt;strong&gt;&lt;code&gt;proxy_pass&lt;/code&gt;&lt;/strong&gt; 디렉티브와 함께 프록시된 서버의 프로토콜, 이름, 포트 번호를 파라미터에 명시된대로 넣어준다(이 경우에는 &lt;code&gt;http://localhost:8080&lt;/code&gt;가 된다.)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    location / {
        proxy_pass http://localhost:8080;
    }

    location /images/ {
        root /data;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 두 번째 &lt;code&gt;location&lt;/code&gt; 블록을 수정하여 특정 파일 확장자를 가진 이미지 요청에 대해 매치되도록 만든다. 변경된 &lt;code&gt;location&lt;/code&gt; 블록은 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;location ~ \.(gif|jpg|png)$ {
    root /data/images;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 파라미터는 &lt;code&gt;레귤러 익스프레션(regular expression)&lt;/code&gt;이며 이것은 &lt;code&gt;.gif&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt;, 또는 &lt;code&gt;.png&lt;/code&gt;로 끝나는 모든 URI와 매치된다. &lt;strong&gt;&lt;em&gt;레귤러 익스프레션은 반드시 &lt;code&gt;~&lt;/code&gt; 심볼을 앞에 붙여야한다.&lt;/em&gt;&lt;/strong&gt; 이에 해당하는 요청들은 모두 &lt;code&gt;/data/images&lt;/code&gt; 디렉토리로 맵핑될 것이다.&lt;/p&gt;
&lt;p&gt;nginx가 요청을 처리하기 위해 어떤 &lt;code&gt;location&lt;/code&gt; 블록을 선택할 때는, 우선 각 &lt;code&gt;location&lt;/code&gt; 디렉티브의 프리픽스(prefix)를 검사하고 매치되는 가장 긴 프리픽스를 가진 하나의 &lt;code&gt;location&lt;/code&gt; 블록을 기억해둔다. 그 다음으로 레귤러 익스프레션을 검사한다. 만약 매치되는 레귤러 익스프레션이 있을 경우 그 &lt;code&gt;location&lt;/code&gt;을 선택하게 되고, 매치되는 레귤러 익스프레션이 없을 경우에는 이전에 기억해두었던 &lt;code&gt;location&lt;/code&gt; 블록을 선택한다.&lt;/p&gt;
&lt;p&gt;지금까지 완성된 프록시 서버의 컨피겨레이션은 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    location / {
        proxy_pass http://localhost:8080/;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 서버는 .gif, .jpg, 또는 .png로 끝나는 모든 요청을 필터링하여 &lt;code&gt;/data/images&lt;/code&gt; 디렉토리로 맵핑할 것이다(해당 &lt;code&gt;root&lt;/code&gt; 디렉티브의 파라미터에 요청의 URI를 덧붙인다.) 그리고 다른 모든 요청들은 위에 설정한 프록시된 서버로 전달될 것이다.&lt;/p&gt;
&lt;p&gt;새로운 컨피겨레이션을 적용하기 위해서 이전처럼 &lt;code&gt;reload&lt;/code&gt; 시그널을 마스터 프로세스에 전송한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;foo@bar:~$ nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;프록시 커넥션을 설정하기 위한 많은 다양한 &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_proxy_module.html&quot;&gt;디렉티브(directives)&lt;/a&gt;가 존재한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;h2&gt;6. FastCGI 프록시 세팅&lt;/h2&gt;
&lt;p&gt;nginx는 요청들을 다양한 프레임워크와 PHP와 같은 프로그래밍 언어로 빌드된 어플리케이션을 실행하는 &lt;code&gt;FastCGI&lt;/code&gt; 서버들로 라우트(route)하기 위해 사용될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FastCGI&lt;/code&gt; 서버와 함께 동작하기 위한 가장 기본적인 nginx 컨피겨레이션은 &lt;code&gt;proxy_pass&lt;/code&gt; 대신에 &lt;strong&gt;&lt;code&gt;fastcgi_pass&lt;/code&gt;&lt;/strong&gt; 디렉티브를 사용하는 것이다. 그리고 &lt;code&gt;FastCGI&lt;/code&gt; 서버에 전달되는 파라미터(parameters)를 설정하기 위해서 &lt;code&gt;fastcgi_param&lt;/code&gt; 디렉티브를 사용한다.&lt;br&gt;FastCGI 서버가 &lt;code&gt;localhost:9000&lt;/code&gt;에서 접속가능하다고 가정해보자. 이전의 프록시 설정을 기본으로 해서 &lt;code&gt;proxy_pass&lt;/code&gt; 디렉티브를 &lt;code&gt;fastcgi_pass&lt;/code&gt; 디렉티브로 교체하고 파라미터를 &lt;code&gt;localhost:9000&lt;/code&gt;로 설정한다. PHP에서는 &lt;code&gt;SCRIPT_FILENAME&lt;/code&gt; 파라미터는 스크립트 이름을 결정하기 위해 사용되며, &lt;code&gt;QUERY_PARAMETER&lt;/code&gt;는 요청 파라미터를 전달하기 위해 사용된다. 완성된 컨피겨레이션은 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    location / {
        fastcgi_pass  localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING    $query_string;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 서버는 정적 이미지에 대한 요청을 제외한 모든 요청들을 &lt;code&gt;localhost:9000&lt;/code&gt;에서 동작 중인 프록시된 서버에게 &lt;code&gt;FastCGI 프로토콜&lt;/code&gt;을 사용해 &lt;code&gt;라우트(route)&lt;/code&gt;할 것이다.&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;em&gt;Nginx. (?). &lt;a href=&quot;http://nginx.org/en/docs/beginners_guide.html&quot;&gt;Beginner’s Guide&lt;/a&gt; [Web Document]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[2] &lt;em&gt;Nginx. (?). &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_proxy_module.html&quot;&gt;Module ngx_http_proxy_module&lt;/a&gt; [Web Document]&lt;/em&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#엔진엑스&lt;/code&gt; &lt;code&gt;#엔진엑스 설정&lt;/code&gt; &lt;code&gt;#엔진엑스 가이드&lt;/code&gt; &lt;code&gt;#엔진엑스 매뉴얼&lt;/code&gt; &lt;code&gt;#엔진엑스 설정&lt;/code&gt; &lt;code&gt;#엔진엑스 컨피겨레이션&lt;/code&gt; &lt;code&gt;#디렉티브&lt;/code&gt; &lt;code&gt;#Nginx 설정&lt;/code&gt; &lt;code&gt;#nginx 서버&lt;/code&gt; &lt;code&gt;#nginx 가이드&lt;/code&gt; &lt;code&gt;#nginx 매뉴얼&lt;/code&gt; &lt;code&gt;#Nginx&lt;/code&gt; &lt;code&gt;#nginx block&lt;/code&gt; &lt;code&gt;#nginx load balance&lt;/code&gt; &lt;code&gt;#Nginx Guide&lt;/code&gt; &lt;code&gt;#Nginx Manual&lt;/code&gt; &lt;code&gt;#nginx.conf&lt;/code&gt; &lt;code&gt;#Nginx Config&lt;/code&gt; &lt;code&gt;#Nginx Configuration&lt;/code&gt; &lt;code&gt;#Nginx Directives&lt;/code&gt; &lt;code&gt;#Nginx Blocks&lt;/code&gt; &lt;code&gt;Nginx Contexts&lt;/code&gt; &lt;code&gt;#nginx http&lt;/code&gt; &lt;code&gt;#nginx server&lt;/code&gt; &lt;code&gt;#nginx location&lt;/code&gt; &lt;code&gt;#nginx proxy server&lt;/code&gt; &lt;code&gt;#nginx request&lt;/code&gt; &lt;code&gt;#nginx response&lt;/code&gt; &lt;code&gt;#sites-available&lt;/code&gt; &lt;code&gt;#sites-enabled&lt;/code&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;p&gt;© 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Frameworks/Nginx</category>
      <category>nginx</category>
      <category>nginx block</category>
      <category>nginx directive</category>
      <category>nginx guide</category>
      <category>nginx http</category>
      <category>nginx manual</category>
      <category>nginx server</category>
      <category>nginx 가이드</category>
      <category>nginx 매뉴얼</category>
      <category>nginx 설정</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/11</guid>
      <comments>https://architectophile.tistory.com/11#entry11comment</comments>
      <pubDate>Thu, 19 Mar 2020 09:30:54 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스] 명령 실행 원리 3 : I/O 리다이렉션</title>
      <link>https://architectophile.tistory.com/10</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;linux2.jpg&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/11QZL/btqCO0IVMoN/5ECIRuzvgb2I9R0bxVJII1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/11QZL/btqCO0IVMoN/5ECIRuzvgb2I9R0bxVJII1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/11QZL/btqCO0IVMoN/5ECIRuzvgb2I9R0bxVJII1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F11QZL%2FbtqCO0IVMoN%2F5ECIRuzvgb2I9R0bxVJII1%2Fimg.jpg&quot; data-filename=&quot;linux2.jpg&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;742&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[리눅스] 명령 실행 원리 3 : I/O 리다이렉션(Redirection)&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. I/O 리다이렉션 소개&lt;/h2&gt;
&lt;p&gt;이번 리눅스 명령 실행 원리 포스트에서 마지막으로 다룰 주제는 바로 &lt;code&gt;리다이렉션(redirection)&lt;/code&gt;이다. 앞서 보았던 포스트들을 통해 우리는 이제 디폴트 스트림들(default streams)이 3개의 디폴트 파일 디스크립터(file descriptors)를 사용하여 어떻게 동작하는지 알게 되었다. 디폴트 스트림들은 키보드를 통해 데이터를 입력(input)받고 터미널을 통해 데이터를 출력(output)한다. 우리는 또한 파이프라인 안에서 프로세스들 사이에서 어떻게 데이터가 전달되는지도 보았다.&lt;/p&gt;
&lt;p&gt;하지만 만약 우리가 파이프라인의 첫 번째 명령에게 입력을 전달할 때 키보드를 사용하지 않고 기존의 파일을 입력으로 전달하고 싶다면 어떻게 해야 할까? 또는 파이프라인의 마지막 명령이 결과를 출력할 때 터미널이 아니라 어떤 파일로 출력하고 싶다면 어떻게 할까?&lt;br /&gt;이런 경우 I/O 리다이렉션(redirection)을 사용할 수 있다. 커맨드 라인(command line)에서 &lt;b&gt;&lt;code&gt;&quot;&amp;lt;&quot;&lt;/code&gt;&lt;/b&gt; 문자는 &lt;code&gt;입력 리다이렉션(input redirection)&lt;/code&gt;을 위해 사용되며, &lt;b&gt;&lt;code&gt;&quot;&amp;gt;&quot;&lt;/code&gt;&lt;/b&gt; 문자는 &lt;code&gt;출력 리다이렉션(output redirection)&lt;/code&gt;을 위해 사용된다. 그리고 출력된 파일은 &lt;b&gt;&lt;i&gt;존재하지 않을 경우 자동으로 생성&lt;/i&gt;&lt;/b&gt;되며, 만약 &lt;b&gt;&lt;i&gt;이미 존재할 경우에는 기존 파일에 덮어쓰게(overwrite) 된다.&lt;/i&gt;&lt;/b&gt; 그리고 만약 리다이렉트된 출력 데이터가 파일에 덮어씌워지는(overwritten) 것이 아니라 &lt;b&gt;&lt;i&gt;기존 컨텐츠에 출력 데이터가 추가(append)되는 것을 원할 경우&lt;/i&gt;&lt;/b&gt;에는 &lt;b&gt;&lt;code&gt;&quot;&amp;gt;&amp;gt;&quot;&lt;/code&gt;&lt;/b&gt; 문자를 사용할 수 있다.&lt;br /&gt;이제 입력, 출력 리다이렉션을 모두 사용하는 한 예제를 살펴보자. 우리는 다음과 같은 컨텐츠를 담고 있는 &lt;code&gt;words.txt&lt;/code&gt; 파일이 있다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;$ cat words.txt
pear
peach
apple&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;우리는 이 파일을 입력(input)으로 사용하여 &lt;code&gt;sort&lt;/code&gt; 명령에 전달한 뒤, 그 결과를 또 다른 파일로 출력(output)할 수 있다(또는 원할 경우 입력과 동일한 파일에 출력할 수도 있다.)&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;$ &amp;lt; words.txt sort &amp;gt; words2.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 명령을 입력하면 &lt;code&gt;words.txt&lt;/code&gt; 파일이 리다이렉트되어 &lt;code&gt;sort&lt;/code&gt; 명령의 입력으로 전달되고, 그 결과는 터미널이 아닌 &lt;code&gt;words2.txt&lt;/code&gt; 파일에 출력된다. 우리는 동일한 명령을 다음과 같이 사용할 수도 있다: &lt;code&gt;sort &amp;lt; words.txt &amp;gt; words2.txt&lt;/code&gt;&lt;br /&gt;&lt;code&gt;cat&lt;/code&gt; 명령을 통해 결과를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ cat words2.txt
apple
peach
pear&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 I/O 리다이렉션을 구현하는 것은 매우 상대적으로 간단하다. 우리는 그저 앞에서 사용했던 &lt;code&gt;dup2()&lt;/code&gt; 함수의 마법을 사용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// if first command in pipeline has input redirection
if (hasInputFile &amp;amp;&amp;amp; is1stCommand) { 
  int fdin = open(inputFile, O_RDONLY, 0644);
  dup2(fdin, STDIN_FILENO);
  close(fdin);
}

// if last command in pipeline has output redirection
if (hasOutputFile &amp;amp;&amp;amp; isLastCommand) { 
  int fdout = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  dup2(fdout, STDOUT_FILENO);
  close(fdout);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 매우 직관적으로 보일 것이라 생각한다. 만약 입력 리다이렉션(input redirection)이 있다면(입력 리다이렉션을 발견하는 함수는 위 코드에서는 생략되어 있다.), 프로세스는 &lt;code&gt;open()&lt;/code&gt; 함수를 호출하여 그 입력 파일을 열고(open) 해당 데이터 스트림(data stream)을 &lt;code&gt;open()&lt;/code&gt; 함수가 사용하는 &lt;code&gt;파일 디스크립터(file descriptor)&lt;/code&gt;에게 할당한다. 그런 다음, 이전 포스트에서처럼 &lt;code&gt;dup2()&lt;/code&gt; 함수를 사용하여 해당 파일 디스크립터를 &lt;code&gt;stdin&lt;/code&gt;과 연결시킨다.&lt;br /&gt;이와 비슷하게 만약 파이프라인의 마지막 명령에서 출력 리다이렉션(input redirection)이 있을 경우, &lt;code&gt;dup2()&lt;/code&gt; 함수를 사용하여 해당 파일 디스크립터를 &lt;code&gt;stdout&lt;/code&gt;과 연결시킨다.&lt;/p&gt;
&lt;p&gt;아래에 이를 나타내는 다이어그램이 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-3-io-redirection-1.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;당신은 아마도 왜 파일 디스크립터들이 입력과 출력에서 모두 3으로 시작하는지 궁금할 것이다. 위의 코드를 살펴보면, 우선 입력 리다이렉션이 있는지 검사한 후에 만약에 있을 경우, 프로세스는 &lt;code&gt;open()&lt;/code&gt; 함수를 호출하여 파일을 열고 해당 데이터 스트림을 &lt;code&gt;파일 디스크립터 3&lt;/code&gt;에게 할당(assign)한다. 그리고 &lt;code&gt;dup2()&lt;/code&gt; 함수를 호출하여 파일 디스크립터 3이 처리하는 데이터를 &lt;code&gt;stdin&lt;/code&gt;이 처리하도록 리다이렉트한다. 그 다음 더 이상 필요없는 &lt;b&gt;&lt;i&gt;파일 디스크립터 3을 닫고(close) 나면&lt;/i&gt;&lt;/b&gt;, 이제 &lt;code&gt;파일 디스크립터 3&lt;/code&gt;은 출력 리다이렉션을 처리하기 위해 &lt;b&gt;&lt;i&gt;사용가능한(available) 상태로 다시 바뀌게 된다.&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;따라서 입력과 출력 파일에 대해 모두 &lt;code&gt;파일 디스크립터 3&lt;/code&gt;을 사용한 다음, 적절하게 데이터를 처리하기 위한 스트림들(stdin과 stdout)에게 리다이렉트되고 나면, 더 이상 필요없는 파일 디스크립터 3을 닫는다(close).&lt;/p&gt;
&lt;p&gt;이것을 통해 우리는 Unix의 강력한 힘을 엿볼 수 있다. 우리는 파이프를 이용하여 작은 프로그램들을 연결(chain)하여 더 큰 프로그램을 만들 수 있을 뿐만 아니라, I/O 리다이렉션을 이용해 파일로 된 데이터를 파이프라인으로 입력하고, 또한 처리된 결과 데이터를 파일로 출력하여 저장할 수 있는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. 최종 정리&lt;/h2&gt;
&lt;p&gt;우리가 이번 리눅스 명령 실행 원리 시리즈 3편의 포스트를 통해 배운 것을 정리하기 위해서 마지막으로 아래의 예제와 다이어그램을 살펴보도록 하자.&lt;/p&gt;
&lt;p&gt;당신은 어떤 그룹의 사람들을 대상으로 그들이 가장 좋아하는 색깔이 무엇인지를 알아보고자 한다고 가정해보자. 우선 당신은 사람들이 가장 좋아하는 색깔이 무엇인지를 조사하여 파일에 적어 저장한다. 따라서 파일의 한 줄마다 사람들이 좋아하는 색깔이 하나씩 적혀있다. 이제 당신은 그 파일에 대해 여러 명령들을 실행하여 최종적으로 가장 인기있는 3개의 색깔이 무엇인지 찾아내고 각 색깔마다 몇명의 투표를 받았는지에 대한 결과를 하나의 파일로 출력하고자 한다. 그리고 이 모든 명령들은 단 하나의 파이프라인(pipeline)에서 처리하고자 한다.&lt;/p&gt;
&lt;p&gt;이를 처리하기 위한 명령은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;$ &amp;lt; colors.txt sort | uniq -c | sort -r | head -3 &amp;gt; favcolors.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다음은 위의 명령을 처리되는 과정을 나타내는 다이어그램이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-3-io-redirection-2.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;840&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 명령은 다음과 같이 처리된다: 우선 &lt;code&gt;colors.txt&lt;/code&gt; 파일은 사람들이 좋아하는 색깔들이 무작위 순서로 저장되어 있다. &lt;code&gt;uniq&lt;/code&gt; 명령은 이전 라인과 동일한 중복되는 모든 라인을 삭제한다. 그리고 위해서 우선 &lt;code&gt;sort&lt;/code&gt; 명령을 사용해 동일한 색깔들이 연속으로 나타나도록 만든다. 그리고 &lt;code&gt;uniq -c&lt;/code&gt;를 호출하는데, 이 때 &lt;code&gt;-c&lt;/code&gt; 옵션은 중복되는 라인을 삭제한 다음 결과를 출력할 때 왼쪽에 각 중복된 라인의 숫자(count)를 함께 출력한다. 그 다음 우리는 다시 그 처리된 결과에 대해 내림차순(descending order)으로 정렬하기 위해 &lt;code&gt;sort -r&lt;/code&gt; 명령을 사용한다. 그리고 마지막으로 &lt;code&gt;head -3&lt;/code&gt; 명령을 실행하여 가장 위에 있는 3줄만 출력하는데 이 때 리다이렉션을 통해 &lt;code&gt;favcolors.txt&lt;/code&gt; 파일에 출력하여 저장한다. 결과적으로 &lt;code&gt;favcolors.txt&lt;/code&gt; 파일에는 다음과 같은 데이터가 저장된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ cat favcolors.txt 
   4 red
   3 blue
   2 green&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 예제는 지금까지 다루었던 예제들 보다 더욱 복잡하다. 그리고 위 다이어그램의 파일 디스크립터들 역시 매우 복잡하다.&lt;br /&gt;나의 쉘 프로그램은 I/O 리다이렉션을 검사하기 전에 pipe() 함수를 먼저 호출한다. 따라서 첫 번째 파이프가 파일 디스크립터 3, 4를 사용하게 된다. 그리고 입력과 출력을 위한 파일 디스크립터는 각각 5, 6이 할당된다. 그리고 파일 디스크립터 5, 6이 각각 stdin과 stdout으로 리다이렉트된 이후에는 닫히게(close) 되고, 위의 다이어그램의 양쪽 끝을 보면 이를 알 수 있다.&lt;br /&gt;두 번째 파이프가 생성될 때, 이것은 재활용된 파일 디스크립터를 사용할 수 있는데, 그 이후에 다른 모든 파이프에 대해서도 마찬가지이다.&lt;br /&gt;하지만 파일 디스크립터들이 어떻게 재활용되는지에 대해 너무 집중할 필요는 없다. 그것은 나의 쉘 프로그램과 예제 코드에 따라 결정되는 것이기 때문이다.&lt;/p&gt;
&lt;p&gt;이것으로 이번 리눅스 명령 실행 원리 시리즈의 마지막 포스트를 마치도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;i&gt;Roslyn Michelle Cyrus. (2017, Nov 8). &lt;a href=&quot;http://www.rozmichelle.com/pipes-forks-dups&quot;&gt;PIPES, FORKS, &amp;amp; DUPS: UNDERSTANDING COMMAND EXECUTION AND INPUT/OUTPUT DATA FLOW&lt;/a&gt; [Web Blog Post]&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#리눅스&lt;/code&gt; &lt;code&gt;#유닉스&lt;/code&gt; &lt;code&gt;#리다이렉트``#리눅스 리다이렉션&lt;/code&gt; &lt;code&gt;#리눅스 리다이렉트&lt;/code&gt; &lt;code&gt;#리눅스 리디렉션 설명&lt;/code&gt; &lt;code&gt;#유닉스 리다이렉트&lt;/code&gt; &lt;code&gt;#리눅스 리다이렉션 설명&lt;/code&gt; &lt;code&gt;#IO 리다이렉트&lt;/code&gt; &lt;code&gt;#IO 리다이렉션&lt;/code&gt; &lt;code&gt;#IO 리디렉션&lt;/code&gt; &lt;code&gt;#리눅스 명령어&lt;/code&gt; &lt;code&gt;#리눅스 프로세스&lt;/code&gt; &lt;code&gt;#리눅스 파일 디스크립터&lt;/code&gt; &lt;code&gt;#리눅스 데이터 흐름&lt;/code&gt; &lt;code&gt;#리눅스 명령 실행&lt;/code&gt; &lt;code&gt;#표준 입력&lt;/code&gt; &lt;code&gt;#표준 출력&lt;/code&gt; &lt;code&gt;#표준 오류&lt;/code&gt; &lt;code&gt;#표준 입출력&lt;/code&gt; &lt;code&gt;#linux&lt;/code&gt; &lt;code&gt;#unix&lt;/code&gt; &lt;code&gt;#linux redirection&lt;/code&gt; &lt;code&gt;#linux IO redirection&lt;/code&gt; &lt;code&gt;#I/O Redirection&lt;/code&gt; &lt;code&gt;#IO Redirection&lt;/code&gt; &lt;code&gt;#unix redirect&lt;/code&gt; &lt;code&gt;#what is redirection in linux?&lt;/code&gt; &lt;code&gt;#what is linux redirection?&lt;/code&gt; &lt;code&gt;#linux redirect&lt;/code&gt; &lt;code&gt;#linux redirect commands&lt;/code&gt; &lt;code&gt;#linux file descriptor&lt;/code&gt; &lt;code&gt;#linux commands&lt;/code&gt; &lt;code&gt;#linux command execution&lt;/code&gt; &lt;code&gt;#linux file&lt;/code&gt; &lt;code&gt;#linux data flow&lt;/code&gt; &lt;code&gt;#stdin&lt;/code&gt; &lt;code&gt;#stdout&lt;/code&gt; &lt;code&gt;#stderr&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;copy; 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Linux/Concepts</category>
      <category>IO Redirection</category>
      <category>IO 리다이렉션</category>
      <category>Linux</category>
      <category>linux redirection</category>
      <category>unix redirection</category>
      <category>리눅스</category>
      <category>리눅스 리다이렉션</category>
      <category>리눅스 리다이렉트</category>
      <category>리눅스 리디렉션</category>
      <category>유닉스</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/10</guid>
      <comments>https://architectophile.tistory.com/10#entry10comment</comments>
      <pubDate>Wed, 18 Mar 2020 18:47:49 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스] 명령 실행 원리 2 : 파이프</title>
      <link>https://architectophile.tistory.com/9</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;linux2.jpg&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3rqVm/btqCOf6Rugx/BubSV7U3uR74kKP4U9629k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3rqVm/btqCOf6Rugx/BubSV7U3uR74kKP4U9629k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3rqVm/btqCOf6Rugx/BubSV7U3uR74kKP4U9629k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3rqVm%2FbtqCOf6Rugx%2FBubSV7U3uR74kKP4U9629k%2Fimg.jpg&quot; data-filename=&quot;linux2.jpg&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;742&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;[리눅스] 명령 실행 원리 2 : 파이프(Pipe(&amp;#39;|&amp;#39;))&lt;/h1&gt;
&lt;br/&gt;

&lt;h2&gt;1. 파이프(Pipe) 소개&lt;/h2&gt;
&lt;p&gt;유닉스(Unix)는 단순하지만 매우 가치있는 디자인 철학을 갖고 있는데, 유닉스 파이프(Pipe)의 창시자인 &lt;code&gt;Doug McIlroy&lt;/code&gt;는 다음과 같이 말했다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“한 가지 일만 아주 잘하는 프로그램들을 작성하라. 프로그램들이 다른 프로그램들과 함께 일할 수 있도록 작성하라. 프로그램들이 텍스트 스트림을 처리할 수 있도록 작성하라. 왜냐하면 그것은 보편적인 인터페이스이기 때문이다.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;원문 :&lt;br&gt;&lt;em&gt;“Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;파이프의 개념은 매우 강력하다. 파이프는 데이터가 한 프로세스에서 다른 프로세스로 전달되도록 하는데(일방향(unidirectional)의 데이터 흐름을 통해서), 그로인해 프로세스들의 명령들이 스트림에 의해 서로 연결된다(chained). 이것은 여러 명령들이 함께 동작하여 더 큰 목적을 달성할 수 있도록 해준다. 이러한 프로세스들의 체이닝(chaining)은 &lt;code&gt;파이프라인(pipeline)&lt;/code&gt;으로 표현될 수 있다: 한 파이프라인 안에 있는 &lt;code&gt;명령들(commands)&lt;/code&gt;은 서로 &lt;strong&gt;&lt;em&gt;&lt;code&gt;파이프(pipes)&lt;/code&gt;에 의해서 연결되며&lt;/em&gt;&lt;/strong&gt;, 파이프의 한 쪽 끝에서 다른 쪽 끝으로 데이터가 흐르면서 두 프로세스 사이에 데이터가 공유된다. 이 때 파이프라인 안에 있는 각 명령들은 각자 독립적인 프로세스 안에서 실행되며, 각자 독립적인 메모리 공간에서 실행된다. 그러므로 우리는 각 프로세스들이 서로 통신할 수 있는 방법이 필요하게 되는데, 바로 &lt;code&gt;pipe()&lt;/code&gt; 시스템 호출이 그 방법을 제공하는 것이다.&lt;/p&gt;
&lt;p&gt;구현에 있어서, 사실 파이프는 그저 &lt;code&gt;버퍼된 스트림(buffered stream)&lt;/code&gt;에 불과하며, 그 스트림은 2개의 파일 디스크립터(file descriptors)와 연결되어 있는데, 첫 번째는 데이터를 &lt;code&gt;읽기&lt;/code&gt; 위한 것이고, 두 번째는 데이터에 &lt;code&gt;쓰기&lt;/code&gt; 위한 것이다. 더욱 구체적으로, 파이프라인의 명령의 실행을 처리하는 코드를 살펴보면, 2개의 정수값을 저장하는 배열(array)이 생성되고, &lt;code&gt;pipe()&lt;/code&gt; 호출은 그 배열에 사용 가능한 2개의 파일 디스크립터 값을(일반적으로 사용가능한 가장 낮은 숫자의 2개의 파일 디스크립터를 사용한다.) 채운다(populate).&lt;/p&gt;
&lt;p&gt;실제 물리적인 파이프는 지금 설명하는 유닉스 파이프의 추상적인 개념을 설명하는데 매우 좋은 비유이다. 우리는 한 프로세스에서 시작되는 &lt;code&gt;데이터 스트림&lt;/code&gt;을 독립된 공간에 들어있는 &lt;code&gt;물&lt;/code&gt;이라고 생각할 수 있다. 그리고 그 물이 다음 프로세스의 공간으로 흐를 수 있는 유일한 방법은 각 공간을 파이프(pipe)로 연결하는 것이다. 이러한 방식으로, 그 &lt;code&gt;물(데이터)&lt;/code&gt;은 &lt;code&gt;첫 번째 공간(프로세스)&lt;/code&gt;에서 &lt;code&gt;파이프&lt;/code&gt;로 흘러들어가고, 파이프 안에 물이 가득차면, 다시 그 &lt;code&gt;파이프&lt;/code&gt;에서 &lt;code&gt;다음 공간(프로세스)&lt;/code&gt;으로 &lt;code&gt;물(데이터)&lt;/code&gt;을 흘려보낸다. 아래의 다이어그램은 이러한 데이터 흐름(data flow)을 표현한 예이다(&lt;code&gt;sort | grep ea&lt;/code&gt;).&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-1.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;br/&gt;

&lt;p&gt;이제 위의 예제를 하나씩 파헤쳐보도록 하자. 기본적으로 &lt;code&gt;sort&lt;/code&gt; 명령은 사용자가 &lt;code&gt;stdin(file descriptor 0)&lt;/code&gt;을 통해서 &lt;code&gt;입력(input)&lt;/code&gt;을 전달할 때까지 기다린다. 그 다음, 입력받은 스트링들은 알파벳 순서대로 정렬되고, 그 정렬된 결과가 &lt;code&gt;stdout&lt;/code&gt;을 통해서 &lt;code&gt;파이프(pipe)&lt;/code&gt;로 전달된다. 이것은 &lt;code&gt;stdout&lt;/code&gt;으로 하여금 출력된 데이터를 터미널 디스플레이(file descriptor 1)가 아니라 &lt;code&gt;파이프의 왼쪽 끝(file descriptor 4)&lt;/code&gt;으로 입력(feed)하도록 만듦으로써 가능해진다.&lt;/p&gt;
&lt;p&gt;위 예제에서 프로세스의 실행 과정에 대한 더욱 자세한 내용은 추후에 다시 설명하도록 하겠다.&lt;/p&gt;
&lt;p&gt;계속 진행하기 전에, 다시 한 번 떠올려야 할 중요한 점이 있는데, 바로 각 프로세스는 자신 만의 &lt;code&gt;파일 디스크립터 테이블(own file descriptor table)&lt;/code&gt;을 갖는다는 것이다. 파이프라인의 각 명령은 각자 독립적인 프로세스 안에서 실행되기 때문에, 각 명령은 자신만의 버전의 파일 디스크립터를 갖고 있으며, 자신만의 stdin, stdout, 그리고 stderr를 갖고 있다. 이것이 의미하는 것은 위의 다이어그램에서 왼쪽 끝에 있는 &lt;code&gt;0&lt;/code&gt;은 &lt;code&gt;sort&lt;/code&gt; 명령을 실행하는 프로세스에 속하는 것이고, 따라서 이것은 다이어그램의 오른쪽 끝에 있는 &lt;code&gt;1&lt;/code&gt;과는 다른 파일 디스크립터 테이블 안에 있는 것이고, 그것은 &lt;code&gt;grep&lt;/code&gt; 명령을 실행하는 프로세스에 속하는 것이다. 하지만 스트림들은 프로세스 바운더리(process boundaries)를 건너 데이터를 전송하도록 설정되어있기 때문에, 데이터가 파이프라인을 타고 잘 전달된다면 결과적으로 데이터는 마지막 프로세스로 전달될 것이다.&lt;/p&gt;
&lt;p&gt;다시 처음부터 살펴보면, &lt;code&gt;sort&lt;/code&gt; 명령은 정렬된 스트링 리스트를 출력(output)으로 갖게되고, 그 출력 데이터를 결과적으로 다음 프로세스(grep)에게 전달하기 위해서, 생성된 파이프(pipe)에게 출력 데이터를 전달해야 한다. 우선 잠시 동안은 파일 디스크립터 3, 4에 대해서는 잊도록 하고, 다어이그램에 쓰여 있는 &lt;strong&gt;&amp;#39;in&amp;#39;&lt;/strong&gt;과 &lt;strong&gt;&amp;#39;out&amp;#39;&lt;/strong&gt; 단어들을 살펴보자. 데이터는 &lt;code&gt;sort 프로세스&lt;/code&gt;로부터(&lt;strong&gt;out of&lt;/strong&gt;) &lt;code&gt;파이프&lt;/code&gt;로(&lt;strong&gt;into&lt;/strong&gt;) 전달되고, 그것은 다시 &lt;code&gt;파이프&lt;/code&gt;로부터(&lt;strong&gt;out of&lt;/strong&gt;) &lt;code&gt;grep 프로세스&lt;/code&gt;로(&lt;strong&gt;into&lt;/strong&gt;) 전달된다.&lt;/p&gt;
&lt;p&gt;이제 &lt;code&gt;pipe()&lt;/code&gt; 호출에 의해 전달되는 파일 디스크립터에 대해서 알아보도록 하자. 파이프라인에서 명령들을 실행하는 코드 안에서, &lt;code&gt;pipe()&lt;/code&gt; 호출은 파일 디스크립터 배열 {3, 4}를 채우게(populate) 되고, 따라서 &lt;code&gt;파일 디스크립터 4&lt;/code&gt;에 &lt;strong&gt;쓰여진(written)&lt;/strong&gt; 데이터가 &lt;code&gt;파일 디스크립터 3&lt;/code&gt;으로부터 &lt;strong&gt;읽히도록(read)&lt;/strong&gt; 만든다. 사실 이 번호들이 숫자가 무엇인지는 별로 중요하지 않다. 배열 속에 들어가는 값들은 오직 프로세스에게만 중요하다. 하지만 &lt;strong&gt;&lt;em&gt;각 파일 디스크립터의 용도(purpose)는 데이터에게 있어 매우 중요하다.&lt;/em&gt;&lt;/strong&gt; 그리고 각 파일 디스크립터의 용도는 배열(array) 안에서 몇 번째 인덱스(index)에 위치하느냐에 따라서 결정된다.&lt;/p&gt;
&lt;p&gt;그리고 여기에서 알아야 할 매우 중요한 개념이 있는데, 나는 이것을 이해하기 위해 위의 다이어그램들을 그려보며 생각해야 했다. 내가 처음에 얘기했던 것처럼 위의 다이어그램들은 데이터가 왼쪽에서 오른쪽으로 흐르는 모델이라는 것을 잘 생각해보자. 파일 디스크립터는 배열 안에서 설정되어 4에 쓰여진 데이터가 3에서 읽히도록 된다. 하지만 당신은 왜 4가 왼쪽으로 가고 3이 오른쪽으로 가는지 궁금해할 수 있다.&lt;br&gt;여기서 핵심적인 내용은 바로 &lt;code&gt;pipe()&lt;/code&gt; 호출에 의해 설정되는 &lt;code&gt;읽기(read)&lt;/code&gt;, &lt;code&gt;쓰기(write)&lt;/code&gt; 액션은 바로 파이프를 사용하는 &lt;strong&gt;&lt;em&gt;양쪽의 2개의 &lt;code&gt;프로세스&lt;/code&gt;들의 관점에서 정의된다는 것이다.&lt;/em&gt;&lt;/strong&gt; 따라서 &lt;code&gt;pipe()&lt;/code&gt; 호출에 &lt;strong&gt;&lt;em&gt;4를 &lt;code&gt;writable end&lt;/code&gt;로 설정하면&lt;/em&gt;&lt;/strong&gt;, 그것은 첫 번째 명령(&lt;code&gt;sort&lt;/code&gt;)의 프로세스가 &lt;strong&gt;&lt;em&gt;출력(output)을 &lt;code&gt;쓰기(write)&lt;/code&gt;하는&lt;/em&gt;&lt;/strong&gt; 파이프의 입력(input)을 전달받는 파이프의 왼쪽 파일 디스크립터가 되는 것이다. 반대로 &lt;code&gt;pipe()&lt;/code&gt; 호출에 &lt;strong&gt;&lt;em&gt;3을 &lt;code&gt;readable end&lt;/code&gt;로 설정하면&lt;/em&gt;&lt;/strong&gt;, 그것은 두 번째 명령(&lt;code&gt;grep&lt;/code&gt;)의 프로세스가 &lt;strong&gt;&lt;em&gt;입력(input)을 &lt;code&gt;읽기(read)&lt;/code&gt;하는&lt;/em&gt;&lt;/strong&gt; 파이프의 출력(output)을 전달하는 파이프의 오른쪽 파일 디스크립터가 되는 것이다.&lt;/p&gt;
&lt;p&gt;따라서 데이터는 첫 번째 프로세스(&lt;code&gt;sort&lt;/code&gt;)에서 파이프로 전달되고, 파이프는 모든 데이터가 전달될 때까지 기다렸다가 모든 데이터가 전달되면, 파이프는 그 다음 프로세스(&lt;code&gt;grep&lt;/code&gt;)로 그 데이터를 내보낸다. 그리고 마지막으로 파이프로부터 데이터를 전달받은 &lt;code&gt;grep&lt;/code&gt; 명령을 실행하는 프로세스는 그 입력 데이터 중에서 &amp;quot;ea&amp;quot;가 포함된 라인을 stdout으로 출력하여 터미널로 내보내게 된다.&lt;/p&gt;
&lt;p&gt;다음으로 우리는 이러한 프로세스들을 처리하는 코드들에 대해서 더욱 자세히 알아보도록 할 것이다. 지금까지 알아본 &lt;code&gt;pipe()&lt;/code&gt; 호출을 이해했다면 우리는 전체 중에서 절반 정도 왔다고 할 수 있다. 나머지 절반은 바로 &lt;code&gt;fork()&lt;/code&gt;와 &lt;code&gt;dup2()&lt;/code&gt;를 이해하는 것이다. 이제 이 함수들이 어떻게 동작하는지 알아보도록 하자!&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;2. 파이프라인(pipeline)에서 여러 명령들 실행하기&lt;/h2&gt;
&lt;p&gt;우리는 지금까지 파이프(pipes)를 이용하여 어떻게 한 프로세스의 데이터가 다른 프로세스로 전달되는지를 살펴보았다. 하지만 우리는 아직 그 명령들을 실행하는 프로세스들의 &lt;code&gt;계층(hierarchy)&lt;/code&gt;에 대해서는 아직 다루지 않았다. 수업시간에 우리는 모든 명령들이 &lt;strong&gt;&lt;code&gt;자식 프로세스(child process)&lt;/code&gt;&lt;/strong&gt;에서 실행되도록 하거나 또는 &lt;strong&gt;&lt;code&gt;부모 프로세스(parent process(calling process))&lt;/code&gt;&lt;/strong&gt;에서 실행되도록 하는 것을 배웠다. 일반적으로 &lt;code&gt;부모 프로세스&lt;/code&gt;는 기본적으로 필요한 설정(setup) 작업을 하고, &lt;code&gt;fork()&lt;/code&gt; 호출을 통해 &lt;code&gt;자식 프로세스&lt;/code&gt;를 생성하여 부모 프로세스의 메모리 상태(memory state)와 파일 디스크립터(file descriptors)를 복제하도록 한다. 따라서 &lt;code&gt;자식 프로세스&lt;/code&gt;는 &lt;code&gt;fork()&lt;/code&gt; 호출이 실행될 때의 부모 프로세스에 존재했던 동일한 변수(variables)와 파일 디스크립터(file descriptors)를 갖는 독립적인 복사본을 갖게 된다. 그리고 &lt;code&gt;fork()&lt;/code&gt; 호출이 끝난 이후에는 자식 프로세스는 더 이상 부모 프로세스의 변화에 대해서 알지 못하고, 반대로 부모 프로세스 역시 자식 프로세스의 변화에 대해서 알지 못한다.&lt;/p&gt;
&lt;p&gt;이러한 자식 프로세스 명령 실행 패턴(the children-execute commands pattern)은 그저 싱글 명령(single command)을 처리하는 경우에는 불필요해 보인다. 왜냐하면 굳이 자식 프로세스를 생성하지 않고도 부모 프로세스에서 그 명령을 실행하면 되기 때문이다.&lt;br&gt;하지만 싱글 명령(single command)이나 또는 파이프라인에서 멀티플 명령들(multiple commands) 모두를 처리할 수 있는 제네릭(generic) 코드를 작성한다고 생각한다면, 이 경우에는 항상 다른 자식 프로세스가 각각 다른 명령을 수행하도록 하는 것이 당연할 것이다.&lt;br&gt;물론 이 규칙이 적용되지 않는 예외적인 경우도 있지만, 여기서 우리는 모든 명령들이 각기 다른 자식 프로세스에서 실행된다고 가정할 것이다.&lt;/p&gt;
&lt;p&gt;이제 다음과 같은 &lt;code&gt;sort&lt;/code&gt; 명령을 실행하는 C 코드 예제를 살펴보도록 하자. 이 예제에서 입력(input)은 &lt;code&gt;dprintf()&lt;/code&gt; 함수를 사용하여 직접적으로 파일 디스크립터(file descriptor)로 출력되는데, 이것은 파이프를 사용하여 부모 프로세스로부터 자식 프로세스로 데이터가 전달되는 경우를 보여주기 위함이다.&lt;/p&gt;
&lt;p&gt;다음의 예제는 우리 수업에서 제공된 샘플 코드를 간략화한 버전이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main(int argc, char *argv[]) {
  int fds[2];                      // an array that will hold two file descriptors
  pipe(fds);                       // populates fds with two file descriptors
  pid_t pid = fork();              // create child process that is a clone of the parent

  if (pid == 0) {                  // if pid == 0, then this is the child process
    dup2(fds[0], STDIN_FILENO);    // fds[0] (the read end of pipe) donates its data to file descriptor 0
    close(fds[0]);                 // file descriptor no longer needed in child since stdin is a copy
    close(fds[1]);                 // file descriptor unused in child
    char *argv[] = {(char *)&amp;quot;sort&amp;quot;, NULL};   // create argument vector
    if (execvp(argv[0], argv) &amp;lt; 0) exit(0);  // run sort command (exit if something went wrong)
  } 

  // if we reach here, we are in parent process
  close(fds[0]);                 // file descriptor unused in parent
  const char *words[] = {&amp;quot;pear&amp;quot;, &amp;quot;peach&amp;quot;, &amp;quot;apple&amp;quot;};
  // write input to the writable file descriptor so it can be read in from child:
  size_t numwords = sizeof(words)/sizeof(words[0]);
  for (size_t i = 0; i &amp;lt; numwords; i++) {
    dprintf(fds[1], &amp;quot;%s\n&amp;quot;, words[i]); 
  }

  // send EOF so child can continue (child blocks until all input has been processed):
  close(fds[1]); 

  int status;
  pid_t wpid = waitpid(pid, &amp;amp;status, 0); // wait for child to finish before exiting
  return wpid == pid &amp;amp;&amp;amp; WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 프로그램은 한 가지 구체적인 명령(&lt;code&gt;sort&lt;/code&gt;)을 실행하도록 쓰여졌다. 코드가 동작하는 과정은 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;우선 &lt;code&gt;부모 프로세스(parent process)&lt;/code&gt;에서 2개의 &lt;code&gt;파일 디스크립터(file descriptors)&lt;/code&gt;를 저장하기 위한 &lt;strong&gt;&lt;em&gt;배열(array)이 한 개 생성된다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pipe()&lt;/code&gt; 호출 이후에 그 배열은 연결된 2개의 파일 디스크립터로 채워지게(populate) 된다. 이 때 배열의 &lt;strong&gt;&lt;em&gt;첫 번째 요소는&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;자식 프로세스&lt;/code&gt;에서 &lt;code&gt;입력(input)&lt;/code&gt;을 &lt;strong&gt;&lt;em&gt;읽는데(read) 사용되고&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;두 번째 요소는&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;부모 프로세스&lt;/code&gt;로부터 &lt;code&gt;출력(output)&lt;/code&gt;을 &lt;strong&gt;&lt;em&gt;쓰는데(write) 사용된다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;fork()&lt;/code&gt; 함수가 호출되어 &lt;code&gt;자식 프로세스&lt;/code&gt;가 생성되고, 그것은 &lt;code&gt;부모 프로세스&lt;/code&gt;의 &lt;strong&gt;&lt;em&gt;파일 디스크립터(file descriptors)와 메모리(memory)를 복제한다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제 현재 실행 중인 &lt;code&gt;프로세스 아이디(process id)&lt;/code&gt;가 &lt;strong&gt;&lt;em&gt;자식 프로세스인지(pid == 0) 검사한다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;

&lt;h3&gt;(1) 자식 프로세스(child process)인 경우&lt;/h3&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dup2()&lt;/code&gt; 함수를 호출하여 자신의 &lt;code&gt;stdin&lt;/code&gt;을 &lt;code&gt;파이프(pipe)&lt;/code&gt;의 &lt;strong&gt;&lt;em&gt;읽기 엔드(read end)와 연결시킨다.&lt;/em&gt;&lt;/strong&gt; 그리고 이것은 &lt;code&gt;fds[0]&lt;/code&gt;에 해당한다. 이 때 알아야 할 점은 &lt;code&gt;dup2()&lt;/code&gt; 함수가 동작할 때, 두 번째 파라미터의 파일 디스크립터를 먼저 닫는다는(close) 것이다. 따라서 위 예제에서는 &lt;code&gt;stdin&lt;/code&gt;(디폴트로 열려 있는 표준 입력)이 먼저 닫히면서, 디폴트 키보드 파일에 대한 레퍼런스가 삭제된다. 그 이후에는 해당 자식 프로세스의 &lt;code&gt;stdin&lt;/code&gt;은 &lt;strong&gt;&lt;em&gt;더 이상 키보드가 아니라 fds[0]에서 데이터를 입력받게 된다.&lt;/em&gt;&lt;/strong&gt; 이것이 바로 &lt;code&gt;dup2()&lt;/code&gt; 함수에서 일어나는 마법이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제 자식 프로세스의 &lt;code&gt;stdin&lt;/code&gt;은 데이터를 읽어들일 준비가 되었으며, &lt;code&gt;pipe()&lt;/code&gt; 호출에 의해 생성된 파일 디스크립터는 이제 더 이상 필요 없으므로 파일 디스크립터를 닫는다(close): &lt;code&gt;close(fds[0]);&lt;/code&gt; &lt;code&gt;close(fds[1]);&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이제 자식 프로세스는 &lt;code&gt;execvp()&lt;/code&gt; 함수를 호출하여 &lt;code&gt;sort&lt;/code&gt; 명령을 실행하고, 부모 프로세스의 모든 데이터가 파이프의 &lt;code&gt;읽기 엔드(readable end)&lt;/code&gt;로 쓰여질 때가지 대기한다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;부모 프로세스로부터 모든 데이터가 파이프의 입력 엔드를 통해 자식 프로세스로 전달되면 &lt;code&gt;sort&lt;/code&gt; 명령을 통해 데이터를 정렬하고, 그 결과를 stdout을 통해 터미널 디스플레이로 출력한 다음, execvp() 호출이 리턴된 이후, 자식 프로세는 디폴트 파일 디스크립터 0, 1, 그리고 2를 자동으로 닫는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;

&lt;h3&gt;(2) 부모 프로세스(parent process)인 경우&lt;/h3&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;부모 프로세스&lt;/code&gt;는 &lt;code&gt;fork()&lt;/code&gt; 함수를 호출한 이후에 파일 디스크립터 &lt;code&gt;fds[0]&lt;/code&gt;를 닫는데, 왜냐하면 &lt;code&gt;부모 프로세스&lt;/code&gt;는 데이터를 읽기(read)는 사용하지 않고 데이터를 &lt;strong&gt;&lt;em&gt;쓰기(write)만 사용&lt;/em&gt;&lt;/strong&gt;하기 때문이다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dprintf()&lt;/code&gt; 함수를 이용해서 &lt;code&gt;자식 프로세스&lt;/code&gt;에게 전달하고자 하는 배열(array) 안에 담긴 단어들(words)을 파이프의 &lt;code&gt;쓰기 엔드(writable end)&lt;/code&gt;인 &lt;code&gt;fds[1]&lt;/code&gt;에 쓴다. 이 때 각 단어 끝에는 줄바꿈 문자(a new line character(&amp;#39;\n&amp;#39;))를 붙여 &lt;code&gt;sort&lt;/code&gt; 명령이 한 줄당 한 단어씩 받을 수 있도록 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;모든 단어들을 전송한 후에 &lt;code&gt;fds[1]&lt;/code&gt;을 닫는다: &lt;code&gt;close(fds[1]);&lt;/code&gt;. 이 때 자동으로 &lt;code&gt;EOF(end-of-file)&lt;/code&gt;이 전송되고, 그 뒤에 자식 프로세스는 &lt;code&gt;sort&lt;/code&gt; 명령을 수행한다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;waitpid()&lt;/code&gt; 함수를 호출하여 &lt;strong&gt;&lt;em&gt;자식 프로세스가 종료될 때가지 기다린다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;자식 프로세스가 종료되고 나면 부모 프로세스도 종료된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;

&lt;p&gt;아래는 위의 모든 과정에 대한 데이터 흐름 다이어그램이다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.1.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;위 예제 코드의 최종 데이터 흐름(data flow)이다. &lt;code&gt;파일 디스크립터 3&lt;/code&gt;은 자식 프로세스에서 &lt;code&gt;stdin&lt;/code&gt;으로 복사된 뒤에 종료되었다. 그리고 오직 &lt;code&gt;자식 프로세스&lt;/code&gt;의 &lt;code&gt;stdin&lt;/code&gt;만 데이터를 읽기 위해 사용되었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;p&gt;이 예제는 파이프가 어떻게 동작하는지를 보여주기 위한 것이라는 점을 명심하길 바란다. 사실 위 예제에서 파이프가 꼭 필요한 것은 아니다. 왜냐하면 자식 프로세스는 부모 프로세스의 도움 없이 파이프를 사용하지 않고 그저 디폴트 stdin을 이용해 데이터에 접근할 수도 있다. 하지만 위 예제의 목적은 파이프를 이용해 한 프로세스에서 다른 프로세스로 데이터를 전달하는 것을 보여주기 위함이다. 그리고 이것은 파이프라인에서 1개 이상의 명령들을 처리할 때 매우 중요한 패턴이다.&lt;/p&gt;
&lt;p&gt;위 예제 코드에서 어떤 일들이 일어나는지 알아보기 위해 다음의 다이어그램들을 살펴보자. 아래 그림들에서 선(lines)은 파일 디스크립터와 그것이 가진 포인터가 가리키는 오픈 파일 사이의 관계를 나타낸다. 그리고 이 때 화살표의 방향은 데이터 흐름의 방향을 나타낸다. 이러한 다이어그램은 부모와 자식 프로세스에게 어떤 파일 디스크립터가 필요한지를 분명하게 보여주고, 또한 메모리 누수(leak)를 막기 위해 언제 파일 디스크립터를 닫는 것이 적절한지를 알 수 있게 한다.&lt;br&gt;또한 명심해야 할 점은 부모 프로세스에서 실행되는 명령어들(instructions)이 자식 프로세스의 명령어들(instructions)보다 이전에 실행된다는 보장이 없기 때문에 아래의 몇몇 과정들은 서로 다른 시간에 발생할 수 있다. 아래의 다이어그램들은 명령 실행 과정에서 어떤 일들이 일어나는지에 대한 개념을 제공하기 위한 것이며, 몇몇 과정은 실행 중에 순서가 뒤바뀔 수도 있다.&lt;/p&gt;
&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.1.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;프로그램이 시작될 때, &lt;code&gt;부모 프로세스&lt;/code&gt;가 생성되고 디폴트 스트림들(default streams)이 그것의 파일 디스크립터 테이블(file descriptor table) 안에 설정된다. 화살표의 방향은 데이터의 흐름 방향을 나타낸다: &lt;code&gt;stdin&lt;/code&gt;은 키보드로부터 &lt;strong&gt;입력(input)&lt;/strong&gt;을 받고, &lt;code&gt;stdout&lt;/code&gt;과 &lt;code&gt;stderr&lt;/code&gt;는 &lt;strong&gt;출력(output)&lt;/strong&gt;을 터미널로 전송한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.2.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;pipe()&lt;/code&gt; 함수 호출은 다음으로 사용가능한 파일 디스크립터 2개를 찾고 그것들을 생성된 &lt;code&gt;파이프(pipe)&lt;/code&gt;의 &lt;strong&gt;읽기 엔드(readable end)&lt;/strong&gt;와 &lt;strong&gt;쓰기 엔드(writable end)&lt;/strong&gt;와 각각 연결시킨다. 이 다이어그램에서는 프로세스는 3을 통해 읽고, 4를 통해 쓸 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.3.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;fork()&lt;/code&gt; 함수 호출은 &lt;code&gt;자식 프로세스&lt;/code&gt;를 생성하는데, 그것은 함수 호출 당시의 부모 프로세스의 메모리와 파일 디스크립터 테이블을 복사한다. 이 때 &lt;code&gt;부모 프로세스&lt;/code&gt;의 파일 디스크립터가 연결된 파일들(files)이 무엇이든지 간에 &lt;code&gt;자식 프로세스&lt;/code&gt;의 파일 디스크립터 역시 동일한 그 파일들(files)과 연결된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.4.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;부모 프로세스&lt;/code&gt;는 파이프의 읽기 엔드가 필요없기 때문에 &lt;code&gt;fds[0]&lt;/code&gt;을 닫는다(close). &lt;code&gt;자식 프로세스&lt;/code&gt;는 &lt;code&gt;dup2()&lt;/code&gt; 함수를 호출하여 디폴트로 &lt;code&gt;stdin&lt;/code&gt;과 연결되어 있던 &lt;strong&gt;&lt;em&gt;파일 디스크립터 0을 먼저 닫은 후에,&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;fds[0]&lt;/code&gt;을 복사하여 &lt;code&gt;stdin&lt;/code&gt;에 연결시킨다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.5.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;부모 프로세스&lt;/code&gt;는 &lt;code&gt;파이프&lt;/code&gt;의 &lt;strong&gt;쓰기 엔드(writable end)&lt;/strong&gt;에 데이터를 쓴다. &lt;code&gt;자식 프로세스&lt;/code&gt;는 더 이상 필요없는 &lt;code&gt;파이프&lt;/code&gt;의 &lt;code&gt;파일 디스크립터(fds[0]과 fds[1])&lt;/code&gt;를 닫는다(close).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.6.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;부모 프로세스&lt;/code&gt;는 모든 데이터를 다 쓰고난 뒤에 &lt;code&gt;fds[1]&lt;/code&gt;을 닫는다(close). 그러면 자동으로 &lt;code&gt;EOF&lt;/code&gt;가 전송되고 &lt;code&gt;자식 프로세스&lt;/code&gt;에게 모든 데이터가 전송되었음을 알리게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.7.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;자식 프로세스&lt;/code&gt;는 받은 &lt;code&gt;입력(input)&lt;/code&gt;에 대해서 &lt;code&gt;sort&lt;/code&gt; 명령을 수행한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.8.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;자식 프로세스&lt;/code&gt;에서 &lt;code&gt;sort&lt;/code&gt; 명령에 의해 생성된 &lt;strong&gt;출력(output)&lt;/strong&gt;은 터미널로 전송된다. 그리고 &lt;code&gt;자식 프로세스&lt;/code&gt;가 종료되면서 신호(signal)을 전송하고, 이것은 &lt;strong&gt;&lt;em&gt;자식 프로세스의 종료를 기다리고 있는&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;부모 프로세스&lt;/code&gt;에게 전달된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;img src=&quot;https://github.com/architectophile/blog/blob/master/linux/concepts/images/linux-concepts-cmd-execution-2-pipe-2.2.9.png?raw=true&quot; alt=&quot;drawing&quot; width=&quot;480&quot;/&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;자식 프로세스&lt;/code&gt;는 종료되면서 자신의 디폴트 파일 디스크립터 0, 1, 그리고 2를 모두 닫는다(close). &lt;strong&gt;&lt;em&gt;자식 프로세스가 종료되고 나면&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;부모 프로세스&lt;/code&gt; 또한 종료되면서 자신의 디폴트 파일 디스크립터 0, 1, 그리고 2를 모두 닫는다(close). 그러면 프로세스 실행 중에 추가적으로 사용되었던 모든 파일 디스크립터들은 닫히게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;

&lt;p&gt;특히 위의 다이어그램들 중에서 &lt;strong&gt;&lt;em&gt;파란색 선들(blue lines)을 잘 보아라&lt;/em&gt;&lt;/strong&gt;. 그것들은 당시 과정에서 &lt;strong&gt;&lt;em&gt;데이터의 흐름(the flow of data)을 나타낸다.&lt;/em&gt;&lt;/strong&gt; 위 다이어그램들을 연결하면 다음과 같은 데이터 흐름을 얻는다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;부모 프로세스&lt;/code&gt;는 &lt;code&gt;파이프&lt;/code&gt;의 &lt;code&gt;쓰기 엔드&lt;/code&gt;를 통해 데이터를 작성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;자식 프로세스&lt;/code&gt;는 &lt;code&gt;파이프&lt;/code&gt;의 &lt;code&gt;읽기 엔드&lt;/code&gt;를 통해 &lt;code&gt;stdin&lt;/code&gt;에서 데이터를 읽어온다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;자식 프로세스&lt;/code&gt;는 &lt;code&gt;sort&lt;/code&gt; 명령을 처리하고 결과를 &lt;code&gt;터미널&lt;/code&gt;로 &lt;code&gt;출력&lt;/code&gt;한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 예제를 통해 알 수 있는 것은 &lt;code&gt;파이프&lt;/code&gt;에 의해 생성된 &lt;code&gt;파일 디스크립터들(file descriptors)&lt;/code&gt;은 필요할 경우 다른 스트림(e.g. stdin)으로 &lt;code&gt;리다이렉트(redirect)&lt;/code&gt;될 수 있다는 것이다. 파이프는 프로세스끼리 서로 통신할 수 있는 2개의 파일 디스크립터를 제공하는 편리한 도구이다. 그리고 &lt;strong&gt;&lt;em&gt;파이프(pipe)의 파일 디스크립터(file descriptors)는 데이터가 적절하게 원하는 곳으로 흐를 수 있도록 리다이렉트(redirect)될 수 있다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;위의 예제 코드와 다이어그램들이 프로세스가 파이프를 통해 여러 명령들을 처리하는 과정을 이해하는데 도움이 되었길 바란다. 위 예제를 통해 알아야 할 것은 &lt;code&gt;파일 디스크립터&lt;/code&gt;가 &lt;strong&gt;&lt;em&gt;어떻게(how) 사용되는지를 이해&lt;/em&gt;&lt;/strong&gt;하는 것도 중요하지만 &lt;code&gt;파일 디스크립터&lt;/code&gt;가 &lt;strong&gt;&lt;em&gt;어느 시점에(when) 사용되지 않으며, 그걸 경우 적절하게 닫혀야한다는 것을 이해&lt;/em&gt;&lt;/strong&gt;하는 것도 중요하다는 사실이다. 파일 디스크립터의 힘은 강력하지만 잘못 사용할 경우 문제를 일으킬 수 있고, 쉽게 까먹고 놓칠 수도 있는 부분이다.&lt;/p&gt;
&lt;p&gt;다음 편에서는 이번 리눅스 명령 실행 원리 시리즈의 마지막으로서 &lt;strong&gt;I/O 리다이렉션(Redirection)&lt;/strong&gt;에 알아보도록 하자.&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;[1] &lt;em&gt;Roslyn Michelle Cyrus. (2017, Nov 8). &lt;a href=&quot;http://www.rozmichelle.com/pipes-forks-dups&quot;&gt;PIPES, FORKS, &amp;amp; DUPS: UNDERSTANDING COMMAND EXECUTION AND INPUT/OUTPUT DATA FLOW&lt;/a&gt; [Web Blog Post]&lt;/em&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;hr&gt;
&lt;h3&gt;Hashtags&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;#리눅스&lt;/code&gt; &lt;code&gt;#유닉스&lt;/code&gt; &lt;code&gt;#파이프&lt;/code&gt; &lt;code&gt;#리눅스 파이프&lt;/code&gt; &lt;code&gt;#리눅스 파이프란&lt;/code&gt; &lt;code&gt;#리눅스 파이프 설명&lt;/code&gt; &lt;code&gt;#유닉스 파이프&lt;/code&gt; &lt;code&gt;#리눅스 명령&lt;/code&gt; &lt;code&gt;#리눅스 명령어&lt;/code&gt; &lt;code&gt;#리눅스 명령 프로세스&lt;/code&gt; &lt;code&gt;#리눅스 프로세스&lt;/code&gt; &lt;code&gt;#리눅스 파일 디스크립터&lt;/code&gt; &lt;code&gt;#리눅스 데이터 흐름&lt;/code&gt; &lt;code&gt;#리눅스 리다이렉션&lt;/code&gt; &lt;code&gt;#리눅스 리디렉션&lt;/code&gt; &lt;code&gt;#리눅스 명령 실행&lt;/code&gt; &lt;code&gt;#표준 입력&lt;/code&gt; &lt;code&gt;#표준 출력&lt;/code&gt; &lt;code&gt;#표준 오류&lt;/code&gt; &lt;code&gt;#표준 입출력&lt;/code&gt; &lt;code&gt;#linux&lt;/code&gt; &lt;code&gt;#unix&lt;/code&gt; &lt;code&gt;#linux pipe&lt;/code&gt; &lt;code&gt;#unix pipe&lt;/code&gt; &lt;code&gt;#what is pipe?&lt;/code&gt; &lt;code&gt;#what is linux pipe?&lt;/code&gt; &lt;code&gt;#linux pipe |&lt;/code&gt; &lt;code&gt;#linux | command&lt;/code&gt; &lt;code&gt;#linux |&lt;/code&gt; &lt;code&gt;#pipe(|)&lt;/code&gt; &lt;code&gt;#linux file descriptor&lt;/code&gt; &lt;code&gt;#linux commands&lt;/code&gt; &lt;code&gt;#linux command execution&lt;/code&gt; &lt;code&gt;#linux file&lt;/code&gt; &lt;code&gt;#linux redirection&lt;/code&gt; &lt;code&gt;#linux data flow&lt;/code&gt; &lt;code&gt;#stdin&lt;/code&gt; &lt;code&gt;#stdout&lt;/code&gt; &lt;code&gt;#stderr&lt;/code&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;br/&gt;

&lt;p&gt;© 2020, Byeongcheol Yoo. All rights reserved.&lt;/p&gt;</description>
      <category>Linux/Concepts</category>
      <category>child process</category>
      <category>File Descriptor</category>
      <category>fork</category>
      <category>linux pipe</category>
      <category>pipe</category>
      <category>redirection</category>
      <category>stdin</category>
      <category>stdout</category>
      <category>리눅스 명령</category>
      <category>리눅스 파이프</category>
      <author>아키텍토필</author>
      <guid isPermaLink="true">https://architectophile.tistory.com/9</guid>
      <comments>https://architectophile.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 17 Mar 2020 14:29:36 +0900</pubDate>
    </item>
  </channel>
</rss>