<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>philBaek (백광록) 개발 일기장</title>
    <link>https://phil-baek.tistory.com/</link>
    <description>제가 걸어온, 그리고 앞으로 걸어갈 개발자로서의 발자취를 기록하기 위한 블로그입니다. 그 내용은 개발 프로젝트가 될 수도 있고, 자격증 후기 또는 스터디 내용 정리가 될 것 같습니다. 아직 많이 부족하지만 누군가에겐 제 글이 도움이 되었으면 좋겠네요!</description>
    <language>ko</language>
    <pubDate>Mon, 12 Oct 2026 08:25:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>philBaek (백광록)</managingEditor>
    <image>
      <title>philBaek (백광록) 개발 일기장</title>
      <url>https://tistory1.daumcdn.net/tistory/4330460/attach/46aef9cda71648fd8c93582fd05c8d67</url>
      <link>https://phil-baek.tistory.com</link>
    </image>
    <item>
      <title>[SD] 파이(Linux)에서 microSD 카드를 SPI로 제어해보자! - 1. 준비</title>
      <link>https://phil-baek.tistory.com/entry/SD-Linux%EC%97%90%EC%84%9C-SPI-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-SD-card%EB%A5%BC-%EC%A0%9C%EC%96%B4%ED%95%B4%EB%B3%B4%EC%9E%90-1-Intro</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;CAUTION&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 라즈베리파이는 여러 개의 USB 포트를 제공하기 때문에 굳이 SPI로 SD 카드를 제어할 필요가 없다. (라즈베리파이 zero 제외)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 SPI만을 사용해야 하는 특별한 이유가 없다면 &lt;span style=&quot;color: #006dd7;&quot;&gt;SD to USB 컨버터를 사용하는 것이 더 효율적&lt;/span&gt;이다..!! 그 이유는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1. SPI는 SDIO에 비해 속도가 느리다.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;SD card는 기본적으로 SD 모드로 동작이 되는데, 이때 SD 모드에서는 명령 전송을 위한 데이터 라인과 데이터 전송을 위한 데이터 라인이 분리되어 있다. 하지만 SPI 모드는 한 개의 데이터 라인에서 명령과 데이터를 순차적으로 보내야 하므로 SD 모드에 비해 속도가 느리다.&lt;br /&gt;&lt;br /&gt;또한, SD 모드는 데이터를 보낼 때 1개의 데이터 라인을 사용하는 1-bit 모드와 4개의 데이터 라인을 사용하는 4-bit 모드로 나뉘는데, 4-bit 모드에서는 SPI 모드와의 속도 차이가 더욱 커진다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2. SPI 방식은 호환성이 낮다.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;SD to USB 컨버터를 사용할 경우에는 컨버터 안에 내장되어 있는 컨트롤러에 의해 SD 카드가 &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;/dev/sda&lt;/span&gt; 와 같이 block device로 인식되기 때문에 디스크 장치로 mount도 할 수 있고, &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;dd&lt;/span&gt;&amp;nbsp;커맨드 등을 사용해서 데이터 복사도 손쉽게 할 수 있으며, &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;parted&lt;/span&gt; 등을 사용해서 파티션도 쉽게 나눌 수 있다.&lt;br /&gt;&lt;br /&gt;하지만, SPI 방식으로 연결하는 경우에는 &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;/dev/spidev0.0&lt;/span&gt;과 같이 character device로 인식되기 때문에 block device driver와 호환되지 않으므로 메모리 제어와 관련된 다양한 명령어를 사용할 수 없다.&lt;br /&gt;물론 spi로 연결된 메모리 장치를 block device로 인식시켜주는 디바이스 드라이버인 &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;mmc_spi&lt;/span&gt;가 있지만, 이 드라이버의 경우에는 5.5 이상의 커널 버전이 적용된 raspbian os의 커널에서는 호환이 안된다.  &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;3. 파이에서 SPI로 SD 카드를 제어하는 방법에 대한 레퍼런스가 거의 없다.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;위 1번, 2번의 이유로 인해 파이에서 SPI로 SD 카드를 제어하는 방법은 더 이상 찾아볼 수 없다. 구글을 이 잡듯이 뒤져서 나오는 레퍼런스들도 거의 7~8년 전의 포스트들이라 현재의 raspbian os (buster &amp;amp; bullseye)에서는 동작이 안된다.&lt;br /&gt;&lt;br /&gt;따라서 가능하다면 SD to USB 컨버터를 사용하는 것이 정신 건강에 이롭다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 나열한 단점에도 불구하고 &lt;span style=&quot;color: #006dd7;&quot;&gt;비용적인 측면이나 하드웨어 이슈 등 여러 가지 이유로 인해 SPI 통신을 사용해야 하는 경우&lt;/span&gt;가 있을 수 있다. 따라서 이번 기회에 리눅스에서 SPI로 SD 카드를 제어하는 방법에 대해 시리즈로 소개하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Prerequisite&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라즈베리파이에서 SPI를 사용해 SD 카드를 제어하기 위해 선행해야 할 것들이 몇 가지 있다. 기본적으로 라즈베리파이는 SPI가 비활성화되어 있기 때문에 SPI 드라이버를 먼저 활성화시켜줘야 하며, 그다음 SD to SPI 컨버터를 물리적으로 라즈베리파이의 GPIO 핀에 연결해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;준비물&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  라즈베리파이 (&lt;a href=&quot;https://www.raspberrypi.com/products/raspberry-pi-4-model-b/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Raspberry Pi 4B&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본적으로 라즈베리파이가 필요하다. 이번 예제에서 사용한 버전은 4B 버전인데, 다른 버전의 라즈베리파이에서는 테스트해보지 않았지만, 해당 파이가 raspbian os로 buster 또는 bullseye를 사용한다면 다른 버전의 파이에서도 가능할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pi.png&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdFr7/btrSehH90Qe/FKXgpxuhy84hhYE98boQ00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdFr7/btrSehH90Qe/FKXgpxuhy84hhYE98boQ00/img.png&quot; data-alt=&quot;Raspberry Pi 4B&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdFr7/btrSehH90Qe/FKXgpxuhy84hhYE98boQ00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqdFr7%2FbtrSehH90Qe%2FFKXgpxuhy84hhYE98boQ00%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;280&quot; data-filename=&quot;pi.png&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Raspberry Pi 4B&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;SD to SPI 컨버터 (&lt;a href=&quot;https://www.devicemart.co.kr/goods/view?no=1326951&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SZH-EKBZ-005&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 필요한 것은 SD to SPI 컨버터이다. 기본적으로 6핀(VCC, GND, CS, SCK, MOSI, MISO)을 제공하는 SPI 컨버터를 사용하면 되며, 예제에서 사용하는 제품 외에 다른 제품을 사용해도 무방하다. 대신 컨버터마다 지원하는 SD 카드의 종류가 다를 수 있으므로 사용하고자 하는 SD 카드에 맞는 SPI 컨버터를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;spi.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPvYCI/btrSc9p1TzZ/7F4vfKM5fqpdBaXFluD2Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPvYCI/btrSc9p1TzZ/7F4vfKM5fqpdBaXFluD2Dk/img.png&quot; data-alt=&quot;SD to SPI 컨버터 (SZH-EKBZ-005)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPvYCI/btrSc9p1TzZ/7F4vfKM5fqpdBaXFluD2Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPvYCI%2FbtrSc9p1TzZ%2F7F4vfKM5fqpdBaXFluD2Dk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;245&quot; data-filename=&quot;spi.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SD to SPI 컨버터 (SZH-EKBZ-005)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;nbsp;microSD 카드 (&lt;a href=&quot;https://search.shopping.naver.com/catalog/20540526631?query=sandisk%20microsd&amp;amp;NaPm=ct%3Dlauim59k%7Cci%3D9a64ec70b87ea05639b09d559f923be2af812c6f%7Ctr%3Dslsl%7Csn%3D95694%7Chk%3D5b9f2edb5063e7206b35b1a4ea3567111e68b0de&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SanDisk microSDHC Class10 Ultra 32GB&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 필요한 것은 microSD 카드이다. SD 카드는 사용 가능한 핀의 개수와 크기에 따라 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;SD, miniSD, microSD&lt;/span&gt;로 나뉘며, 그 안에서도 세부적으로 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;SDV1, SDV2, SDHC, SDXC&lt;/span&gt;로 나뉜다. 이번 예제에서는 위에 나열된 모든 종류의 SD 카드를 지원하므로 사용하고자 하는 종류와 버전을 자유롭게 선택하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sd.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO4Zov/btrSa61BxHl/rXXlmn3gucXuHKJGos3n9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO4Zov/btrSa61BxHl/rXXlmn3gucXuHKJGos3n9K/img.png&quot; data-alt=&quot;microSD 카드 (SanDisk microSDHC)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO4Zov/btrSa61BxHl/rXXlmn3gucXuHKJGos3n9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO4Zov%2FbtrSa61BxHl%2FrXXlmn3gucXuHKJGos3n9K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;216&quot; data-filename=&quot;sd.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;microSD 카드 (SanDisk microSDHC)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;spidev&lt;/span&gt; 드라이버 활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;spidev&lt;/span&gt; 드라이버를 활성화하는 데에는 여러 가지 방법이 있다. 간편하게 &lt;span style=&quot;background-color: #333333; color: #ffffff;&quot;&gt;raspi-config&lt;/span&gt;를 사용해서 GUI로 SPI를 활성화하는 방법도 있지만, cli를 활용해서 SPI를 활성화하기 위해서는 아래 과정을 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669476884364&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. sudo apt-get update
2. sudo apt-get upgrade
3. sudo vi /boot/config.txt
4. /boot/config.txt 파일에 dtparam=spi=on 추가
5. sudo reboot
6. lsmod | grep spi 입력 후, spi_bcmXXXX 및 spidev가 출력되는지 확인
7. ls /dev/spi* 입력 후, /dev/spidev0.0 및 /dev/spidev0.1이 출력되는지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SPI Converter 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 CS 0을 사용할 것이라면 아래와 같이 SPI Converter를 연결하면 된다. CS 1을 사용할 경우에는 아래 그림에서 노란색 선을 바로 아래의 SPI0 CS1 (26번 핀)에 연결해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;connection.png&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rXM4V/btrSasqlit7/8KQIpyomIXqtbQ8UxjaPy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rXM4V/btrSasqlit7/8KQIpyomIXqtbQ8UxjaPy0/img.png&quot; data-alt=&quot;SPI Converter 연결도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rXM4V/btrSasqlit7/8KQIpyomIXqtbQ8UxjaPy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrXM4V%2FbtrSasqlit7%2F8KQIpyomIXqtbQ8UxjaPy0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;455&quot; data-filename=&quot;connection.png&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;583&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SPI Converter 연결도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 SPI로 SD 카드를 제어하기 위한 준비가 완료되었다..! 다음 포스트에서는 spidev 드라이버를 간단하게 소개하고, 이를 사용해 SD 카드와 데이터를 주고받을 수 있는 인터페이스를 구현해볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /Linux</category>
      <category>Linux</category>
      <category>mmc_spi</category>
      <category>pi</category>
      <category>Raspberry Pi</category>
      <category>sdcard</category>
      <category>SPI</category>
      <category>spidev</category>
      <category>라즈베리파이</category>
      <category>리눅스</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/42</guid>
      <comments>https://phil-baek.tistory.com/entry/SD-Linux%EC%97%90%EC%84%9C-SPI-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-SD-card%EB%A5%BC-%EC%A0%9C%EC%96%B4%ED%95%B4%EB%B3%B4%EC%9E%90-1-Intro#entry42comment</comments>
      <pubDate>Sun, 27 Nov 2022 00:46:14 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 프레임워크를 직접 구현해보자! - Flint (Numble Node.js 챌린지 X Chekit)</title>
      <link>https://phil-baek.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%A5%BC-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90-Flint-Numble-Nodejs-%EC%B1%8C%EB%A6%B0%EC%A7%80-X-Chekit</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_8773.PNG&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B0l8i/btrMdRXIjL9/yISKt1wL6wxCIYKMCzKL6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B0l8i/btrMdRXIjL9/yISKt1wL6wxCIYKMCzKL6K/img.png&quot; data-alt=&quot;이 APP에 대한 API 서버를 구현하는 것이 이번 과제. 인 줄 알았으나..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B0l8i/btrMdRXIjL9/yISKt1wL6wxCIYKMCzKL6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB0l8i%2FbtrMdRXIjL9%2FyISKt1wL6wxCIYKMCzKL6K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;401&quot; height=&quot;868&quot; data-filename=&quot;edited_IMG_8773.PNG&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이 APP에 대한 API 서버를 구현하는 것이 이번 과제. 인 줄 알았으나..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Numble Node.js 챌린지?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 재직 중인 회사에서 IoT SW 개발자로 근무한 지 약 1년 반이 다되어가고 있다. 회사에서 주로 하는 업무는 펌웨어 개발이지만, 업무 특성상 PoC를 위해 백엔드 개발도 겸해서 수행하는 경우가 종종 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지는 백엔드를 개발해본 적이 한 번도 없었지만, 막상 접해보니 백엔드 분야가 너무 재미있어서 이것저것 찾으며 공부하던 와중에 Numble에서 Node.js 챌린지를 진행한다는 소식을 접하였다. 챌린지 소개글을 훑어보니 Node.js를 활용해서 비대면 성병 검사 서비스인 Chekit App의 API 서버를 구현하면 되는 듯하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 좀 더 다양한 실무 프로젝트를 접해보고 싶었으며, 난이도도 그리 높지 않아 보여서 고민하지 않고 바로 신청하였고, 그렇게 2주간의 Node.js 챌린지가 시작되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 소개글을 너무 대충 읽었던 탓일까,, 9월 2일에 진행된 OT에서 챌린지의 가이드를 듣고 보니, 이 챌린지는 단순히 API 서버를 구현하는 것이 아니라 Nest.js와 같은 백엔드 프레임워크를 직접 개발하고, 자신의 프레임워크를 활용해서 API 서버를 구현하는 극악무도한 난이도를 가진 챌린지였다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 2주간의 Node.js 챌린지 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;지옥편&lt;/span&gt;&lt;/b&gt;이 시작되었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모집 링크는 아래에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1663254751526&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[마감] Node.js with express 기반으로 비대면 진료 앱 클론 서버 만들기&quot; data-og-description=&quot;상세 보기&quot; data-og-host=&quot;www.numble.it&quot; data-og-source-url=&quot;https://www.numble.it/be906bad-abe7-496c-ad2e-445ce2ae5989&quot; data-og-url=&quot;https://www.numble.it/be906bad-abe7-496c-ad2e-445ce2ae5989&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGXaXG/hyPNSKP8Hl/9eqcz6prqcksWAeTFdstd0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/cOpZw8/hyPN0PDiFK/T8kKZTleDy8tCgKTiR7Ak0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627&quot;&gt;&lt;a href=&quot;https://www.numble.it/be906bad-abe7-496c-ad2e-445ce2ae5989&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.numble.it/be906bad-abe7-496c-ad2e-445ce2ae5989&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGXaXG/hyPNSKP8Hl/9eqcz6prqcksWAeTFdstd0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/cOpZw8/hyPN0PDiFK/T8kKZTleDy8tCgKTiR7Ak0/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[마감] Node.js with express 기반으로 비대면 진료 앱 클론 서버 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;상세 보기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.numble.it&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;챌린지 과제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 Node.js 챌린지의 과제는 크게 두 가지로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 백엔드 프레임워크 개발 과제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. API 스펙 개발 과제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드 프레임워크 개발 과제&lt;/b&gt;는 세부적으로 4가지로 나뉘는데 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWb7Vs/btrMd2q3QZG/Ud73G58Bxpm89fqDMQD7K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWb7Vs/btrMd2q3QZG/Ud73G58Bxpm89fqDMQD7K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWb7Vs/btrMd2q3QZG/Ud73G58Bxpm89fqDMQD7K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWb7Vs%2FbtrMd2q3QZG%2FUd73G58Bxpm89fqDMQD7K0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;747&quot; height=&quot;336&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;API 스펙 개발 과제&lt;/b&gt;는 아래 7가지의 API를 구현하는 과제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 API 스펙 문서와 DB에 들어갈 샘플 데이터는 Numble 측에서 준비하였고, 챌린지 참가자는 해당 데이터를 활용해서 API 서버를 구현하고, 이를 테스트용 Chekit 앱에서 정상적으로 프로세스가 진행되는지를 확인하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;299&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYnuNj/btrMbxMku5n/U5FopZbQdxrjbIR2krlxKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYnuNj/btrMbxMku5n/U5FopZbQdxrjbIR2krlxKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYnuNj/btrMbxMku5n/U5FopZbQdxrjbIR2krlxKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYnuNj%2FbtrMbxMku5n%2FU5FopZbQdxrjbIR2krlxKK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;299&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;챌린지 결과부터 먼저 말하자면, API 스펙 개발 과제는 모두 완료하였고, 백엔드 프레임워크 개발 과제의 경우, 오류 Handling 모듈 개발 과제를 제외한 나머지를 완수하였다. 못다 한 부분은 이번 챌린지가 끝나더라도 계속 구현해나갈 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Flint와 CheKit API 서버 구현&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Routing 모듈 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 챌린지를 진행하면서 가장 먼저 구현한 모듈은 Routing 모듈인데, 구현 과정에서 가장 어려웠고, 헷갈렸던 녀석이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Routing 모듈 개발 과제의 목표가 api 스펙 파일을 cli나 특정 모듈의 인자로 전달하면, 이를 파싱 해서 bolier plate처럼 자동으로 controller와 service 파일을 생성해주는 것으로 착각했었다. 그래서 약 4일간 bolier plate 방식으로 Routing 모듈을 만든 후에 실제로 사용을 해보았는데, 당연하게도 생성된 파일에 프레임워크 사용자가 개입할 수 있는 부분이 적어서 오히려 불편하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개발 과제를 다시 분석한 다음 목표를 새로 세웠고, 그 결과 아래와 같이 동작되도록 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Route 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 간단하게 api 스펙 파일만 전달해서 route를 생성하고 싶은 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Router라는 클래스 데코레이터에 api 스펙 파일에 대한 정보를 인자로 전달하면, api 스펙 파일을 파싱 해서 자동으로 route를 생성해주고, 이에 대한 middleware 및 controller도 자동으로 연결해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. api 스펙 파일과는 무관하게, 직접 route 지정도 하고 싶은 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Router 데코레이터로 감싸져 있는 클래스 아래에 Flint에서 제공하는 HTTP 메소드 데코레이터를 활용해서 사용자가 직접 route를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663249715157&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// https://github.com/GwangrokBaek/CheKIT-backend/blob/master/src/routes/api.router.ts

import { Get, Router } from &quot;../../packages/flint/common&quot;

@Router(&quot;&quot;, {
	files: [&quot;api.json&quot;],
	directory: __dirname,
	log: true,
	jwt: &quot;auth.middleware.ts&quot;,
})
export class ApiRouter {
	constructor(private readonly apiController: any) {
		this.apiController = apiController
	}

	@Get(&quot;/test1&quot;)
	testApi1(): any {
		return this.apiController.testApi()
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 확인할 수 있듯이 Router 클래스 데코레이터의 두 번째 인자로 api 스펙 파일에 대한 정보를 전달해주면, 사용자가 route를 직접 설정해주지 않더라도 Flint에서 자동으로 생성해준다. Router 클래스 데코레이터에 대한 내용은 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend/blob/master/packages/flint/common/decorators/core/router.decorator.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 스펙 파일과는 무관하게 직접 route를 설정하고 싶은 경우에는 위에 보이는 testApi1 메소드와 같이 이를 직접 정의해준 다음, 알맞은 HTTP 메소드 데코레이터로 해당 메소드를 감싸주면 된다. HTTP 메소드 데코레이터에 대한 내용은 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend/blob/master/packages/flint/common/decorators/http/method-mapping.decorator.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Middleware 연결&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auth check를 수행하는 middleware를 연결하거나, 입력 파라미터에 대해 validator를 구성하고 싶은 경우가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 validator의 경우, 스펙 파일에 명시되어 있는 headers나 body 등의 type과 required 값을 파싱 해서 Flint가 validator를 자동으로 생성해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1663250482208&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// api 스펙 파일

&quot;signup&quot;: {
		&quot;url&quot;: &quot;/v3/user/reg&quot;,
		&quot;method&quot;: &quot;post&quot;,
		&quot;description&quot;: &quot;email을 고유 아이디로 새로운 사용자 계정을 생성합니다.&quot;,
		&quot;body&quot;: {
			&quot;email&quot;: {
				&quot;type&quot;: &quot;email&quot;,
				&quot;required&quot;: true,
				&quot;description&quot;: &quot;이메일 주소&quot;
			},
			&quot;key&quot;: {
				&quot;type&quot;: &quot;string&quot;,
				&quot;required&quot;: true,
				&quot;description&quot;: &quot;패스워드&quot;
			},
			&quot;name&quot;: {
				&quot;type&quot;: &quot;string&quot;,
				&quot;description&quot;: &quot;사용자의 이름&quot;
			}
		},
		// 생략
	},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스펙 파일에 대한 파싱을 수행하고, 이를 통해 validator를 생성하는 부분은 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend/blob/master/packages/flint/core/builder/router/route-middleware.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 middleware의 경우, 아래와 같이 사용자가 직접 구현한 middleware를 연결할 수 있는데, 시간이 부족하여 api 스펙 파일을 사용하여 route를 생성하는 경우에 대해서만 개발하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663250812488&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// https://github.com/GwangrokBaek/CheKIT-backend/blob/master/src/middlewares/auth.middleware.ts
import * as jwt from &quot;jsonwebtoken&quot;

const authChecker = async (req, res, next) =&amp;gt; {
	try {
		const headers = req.headers
		const token = headers.authorization.split(&quot; &quot;)[1]
		const decoded = await jwt.verify(token, process.env.SECRET)

		if (decoded) {
			req.userId = decoded.id
			next()
		} else {
			res.status(401).json({ error: &quot;Unauthorized&quot; })
		}
	} catch (error) {
		res.status(401).json({ error: &quot;Unauthorized&quot; })
	}
}

export default authChecker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 middleware 파일을 아래와 같이 Router 클래스 데코레이터의 두 번째 인자에 함께 전달해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663289189129&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Router(&quot;&quot;, {
	files: [&quot;api.json&quot;],
	directory: __dirname,
	log: true,
	jwt: &quot;auth.middleware.ts&quot;,
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Logger 모듈 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing 모듈 구현을 완료한 다음에는 Logger 모듈 구현을 진행하였는데, 아래 목표에 초점을 두어 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 로그가 Flint 시스템에서 발생했는지, 사용자 App에서 발생했는지 식별 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 어느 모듈에서 발생한 로그인지 구분 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Timestamp 출력&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Loglevel을 구분하여 사용자가 지정한 level에 대해서만 로그가 출력되게끔 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663251286989&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export class ApiController {
	constructor(
		private readonly logger: any,
	) {
		this.logger = logger
	}

	async test(req, res) {
		this.logger.info(&quot;hello world&quot;)
		return {}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 볼 수 있듯이, 사용자는 logger에 대한 dependency를 주입하여 logger를 각각의 모듈에서 사용할 수 있으며, 결과적으로 command 창에서는 아래와 같은 로그를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-15 오후 9.09.27.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dca9SX/btrMfOebUau/KnSSiHyROO5kEuKkDu0650/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dca9SX/btrMfOebUau/KnSSiHyROO5kEuKkDu0650/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dca9SX/btrMfOebUau/KnSSiHyROO5kEuKkDu0650/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdca9SX%2FbtrMfOebUau%2FKnSSiHyROO5kEuKkDu0650%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;764&quot; height=&quot;363&quot; data-filename=&quot;스크린샷 2022-09-15 오후 9.09.27.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger 모듈을 구현할 때 어려웠던 점은&lt;b&gt; 2. 어느 모듈에서 발생한 로그인지 구분 가능&lt;/b&gt;하게끔 하는 부분이었다. 이를 처리하기 위해서는 logger의 메소드가 호출될 때 콜 스택을 확인하고, 상위 caller에 대해 파싱을 해주어야 하는데.. javascript에서 콜 스택을 정상적으로 얻을 수 있는 방법에 대해 아무리 찾아봐도 찾을 수가 없었다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 편법을 사용해서 아래와 같이 처리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663251588185&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private getModuleName(method: Function): string {
		let obj = {} as any
		Error.captureStackTrace(obj, method)

		let moduleName: string = &quot;&quot;
		let stackArray: string[] = obj.stack.split(&quot;\n&quot;)

		const filteredArray = stackArray.filter(function (val) {
			return !/Error|new|at \/|at Object|at __awaiter|at Generator|at next|at Function.|at Layer.|at Route./g.test(
				val
			)
		})

		let moduleString = filteredArray[0].split(&quot;at &quot;)
		moduleString = moduleString[1].split(&quot; &quot;)
		let module = moduleString[0].split(&quot;.&quot;)

		moduleName = module[0]

		return moduleName
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger 모듈에서는 logger의 메소드가 호출될 경우, getModuleName 메소드를 통해 모듈 이름을 얻어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 getModuleName 메소드에서 Error를 강제로 생성해서 해당 Error가 발생한 지점으로부터의 콜 스택을 획득하고, 정규표현식을 사용해 필요 없는 스택들을 제거한 다음, 남아있는 스택 중 가장 최상위에 위치해 있는 스택을 모듈 이름으로 가져오게끔 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger 모듈에 대한 전체 코드는 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend/blob/master/packages/flint/common/logger/logger.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Flint-Initializer 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flint에서 필요로 하는 필수적인 모듈들에 대한 구현을 대부분 완료하였다. 이제 서버를 생성하고, 사용자의 모듈들이 이 서버 위에서 돌아가게끔 하는 Flint-Initializer를 구현할 차례가 다가왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flint-Initializer의 목표는 아래와 같이 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 사용자는 서버 생성에 관여할 필요가 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 사용자 모듈들의 인스턴스를 Flint-Initializer가 관리하고, 각각에서 필요로하는 의존성을 동적으로 주입시켜준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 각각의 모듈에 대한 metadata를 Flint-Initializer가 취합하고 처리한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663252511494&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// https://github.com/GwangrokBaek/CheKIT-backend/blob/master/src/modules/app.module.ts

import { Module } from &quot;../../packages/flint/common&quot;
import { ApiRouter } from &quot;../routes/api.router&quot;
import { ApiController } from &quot;../controllers/api.controller&quot;
import { Logger } from &quot;../../packages/flint/common&quot;
import { AppDB } from &quot;./app.db&quot;
import { User } from &quot;../models/user.model&quot;
import { Doctor } from &quot;../models/doctor.model&quot;

@Module()
export class AppModule {
	public imports: any[]
	public routers: any[]
	public providers: any[]

	constructor() {
		const logger = new Logger(&quot;error&quot;)
		const appDb = new AppDB(logger)
		const apiController = new ApiController(logger, User, Doctor)
		const apiRouter = new ApiRouter(apiController)

		this.imports = []
		this.routers = [apiRouter]
		this.providers = [apiController, appDb]
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 2번에 대한 구현은 완료하지 못했다  &lt;br /&gt;현재는 위 코드에서 볼 수 있듯이 사용자가 module을 생성할 때에 직접 인스턴스를 생성하고, 필요한 의존성을 주입해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 모듈들은 아래와 같이 Flint의 엔트리 코드인 main.ts에서 Flint-Initializer에 의해 최종적으로 생성되며, Flint-Initializer는 앞서 Routing 모듈에서 설정하였던 Route 관련 metadata를 취합하여 서버의 Router에 등록시켜준다.&lt;/p&gt;
&lt;pre id=&quot;code_1663252621952&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// https://github.com/GwangrokBaek/CheKIT-backend/blob/master/src/main.ts

import { AppModule } from &quot;./modules/app.module&quot;
import { Flint } from &quot;../packages/flint/core&quot;

async function main() {
	const appModule = new AppModule()
	const app = await Flint.create([appModule])
	await app.listen(3000)
}

main()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flint-Initializer와 관련된 부분은 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend/blob/master/packages/flint/core/flint-initializer.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;디자인 패턴 설계과 API 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Flint와 관련된 구현은 모두 완료하였다. 물론 아직 미흡한 부분도 많고, 빠진 부분도 조금씩 있지만 이러한 부분들은 이번 챌린지가 끝나고 시간을 들여 찬찬히 완성해나갈 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 Chekit APP에 대한 API 서버를 구현할 차례이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 API 서버의 디자인 패턴으로는 MVC를 선택하였다. 그 이유는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 여러 디자인 패턴 중 가장 간단한 패턴&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 이번 Chekit API 서버의 경우, View를 따로 구성해 줄 필요가 없기에 MVC 패턴의 단점이 부각되지 않는 점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 패턴을 적용하여 설계한 디렉토리 구조는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5HgAa/btrMeGnv0xk/8zKNPOidbIimKIK4WO4SN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5HgAa/btrMeGnv0xk/8zKNPOidbIimKIK4WO4SN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5HgAa/btrMeGnv0xk/8zKNPOidbIimKIK4WO4SN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5HgAa%2FbtrMeGnv0xk%2F8zKNPOidbIimKIK4WO4SN1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;277&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 여태껏 개발한 Flint 프레임워크의 경우, 우선은 별도 레포지토리가 아닌 아래와 같이 모노 레포로 구성을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 Flint 프레임워크는 packages 디렉토리 아래에 위치하며, 사용자의 API 서버에 대한 코드는 src 디렉토리 아래에 위치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgP4JM/btrMedsj7vo/DJkXj7PYKOTkYPTNQYgeak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgP4JM/btrMedsj7vo/DJkXj7PYKOTkYPTNQYgeak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgP4JM/btrMedsj7vo/DJkXj7PYKOTkYPTNQYgeak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgP4JM%2FbtrMedsj7vo%2FDJkXj7PYKOTkYPTNQYgeak%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;378&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB로는 mongodb를 사용하였으며, javascript에서 이를 편하게 사용하기 위해 mongoose 패키지를 활용하였다. 이때 mongoose Schema 및 CRUD 관련 코드들은 models 디렉토리 아래에 위치시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, router에 의해 호출되는 controller들은 controllers 디렉토리 아래에 위치해 있는데, 아래 코드에서 볼 수 있듯이 여기에서 요청에 대한 로직을 수행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663254273936&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// https://github.com/GwangrokBaek/CheKIT-backend/blob/master/src/controllers/api.controller.ts

import * as jwt from &quot;jsonwebtoken&quot;

export class ApiController {
	constructor(
		private readonly logger: any,
		private readonly userModel: any,
		private readonly doctorModel: any
	) {
		this.logger = logger
		this.userModel = userModel
		this.doctorModel = doctorModel
	}

	async test(req, res) {
		this.logger.info(``)
		return {}
	}

	async signup(req, res) {
		let result

		try {
			const body = req.body

			await this.userModel.create(body.email, body.key, body.name)

			const token = await jwt.sign(
				{ id: body.email },
				process.env.SECRET,
				{
					expiresIn: &quot;1h&quot;,
				}
			)

			result = { status: &quot;ok&quot;, data: { token: token } }
		} catch (error) {
			this.logger.error(error)

			if (error.code === 11000) {
				result = { status: &quot;user_duplicate&quot; }
			} else {
				result = { status: &quot;nok&quot; }
			}
		}

		return result
	}

	async withdrawal(req, res) {
		let result

		try {
			await this.userModel.deleteByEmail(req.userId)

			result = { status: &quot;ok&quot; }
		} catch (error) {
			this.logger.error(error)
			result = { status: &quot;nok&quot; }
		}

		return result
	}
    
    // 생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 각각의 Flint 및 API 서버에 대한 구현을 마무리하였다. 이번 프로젝트의 Github 레포 링크는 &lt;a href=&quot;https://github.com/GwangrokBaek/CheKIT-backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 CheKit 앱을 통해 검사 신청 프로세스 중 가장 마지막 API인 register API에 대한 결과 스크린을 아래와 같이 확인할 수 있다면, API 개발 과제를 성공적으로 수행한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8777.PNG&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsk0pj/btrMfeEflmV/KQSHKnl572Sc7rykCbUnsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsk0pj/btrMfeEflmV/KQSHKnl572Sc7rykCbUnsK/img.png&quot; data-alt=&quot;이번 챌린지에 있어서 최고로 기뻤던 순간 ㅠ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsk0pj/btrMfeEflmV/KQSHKnl572Sc7rykCbUnsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsk0pj%2FbtrMfeEflmV%2FKQSHKnl572Sc7rykCbUnsK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;404&quot; height=&quot;874&quot; data-filename=&quot;IMG_8777.PNG&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1792&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이번 챌린지에 있어서 최고로 기뻤던 순간 ㅠ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;글을&amp;nbsp; 마치며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 개발도 생소한데, 하필 백엔드 프레임워크 개발은 처음이었다 보니 많은 난관이 있었다. 하지만, 챌린지를 참여하는 다른 분들이 qna 채널에 올린 질문과 멘토님의 답변 덕분에 인사이트를 얻을 수 있었고, 또 스터디를 정기적으로 진행하면서 서로의 정보를 공유하는 과정에서 많은 도움을 얻을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 예상치 못한 과제 난이도로 인해 많이 당황했었고, 업무와 병행하면서 2주 만에 이걸 대체 어떻게 완성하라는 거라며 불평도 했었지만, 지금은 오히려 Numble의 이번 챌린지 덕분에 많은 것을 배울 수 있었어서 매우 감사한 마음이다. 지금까지는 AWS에서 MSA 단위로 개발하면서 백엔드를 살짝 경험했던 터라, 이러한 API 서버에 대한 개념이 많이 부족했었는데 이번 챌린지를 수행하면서 디자인 패턴, IoC, DI 등등 그동안 간과했던 부분에 대해 심도 있게 공부할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 챌린지로는 대용량 트래픽에 대응하는 챌린지가 있을 예정이라 들었는데, 그때도 고민하지 않고 바로 신청해야겠다!&lt;/p&gt;</description>
      <category>  프로젝트</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/41</guid>
      <comments>https://phil-baek.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%A5%BC-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90-Flint-Numble-Nodejs-%EC%B1%8C%EB%A6%B0%EC%A7%80-X-Chekit#entry41comment</comments>
      <pubDate>Thu, 15 Sep 2022 23:51:39 +0900</pubDate>
    </item>
    <item>
      <title>갓생 살고 싶어? Time2Study와 함께 해! (디스코드 봇 개발 사이드 프로젝트)</title>
      <link>https://phil-baek.tistory.com/entry/%EA%B0%93%EC%83%9D-%EC%82%B4%EA%B3%A0-%EC%8B%B6%EC%96%B4-Time2Study%EC%99%80-%ED%95%A8%EA%BB%98-%ED%95%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brtLqz/btrKp5vUjZD/oHqj8K09LlzeWBIgLgEj50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brtLqz/btrKp5vUjZD/oHqj8K09LlzeWBIgLgEj50/img.png&quot; data-alt=&quot;갓생을 살고 싶은 당신. Time2Study를 사용해보자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brtLqz/btrKp5vUjZD/oHqj8K09LlzeWBIgLgEj50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrtLqz%2FbtrKp5vUjZD%2FoHqj8K09LlzeWBIgLgEj50%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;931&quot; height=&quot;850&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;1019&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;갓생을 살고 싶은 당신. Time2Study를 사용해보자.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Issue&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘&lt;span&gt;&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;갓생&lt;/b&gt;&lt;/span&gt;이란&lt;/span&gt; 단어가 매우 유행이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갓생이란 간단히 말해서 부지런한 삶을 사는 것을 의미하는데, 나 또한 이 갓생을 한 번 살아보기 위해 친구들과 매일 퇴근 후, 캠스터디를 하는 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  캠스터디가 무엇인지 궁금하다면 아래 더보기를 눌러보자!&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;캠스터디란, 서로의 공부하는 모습을 화상으로 공유하여 온라인상에서도 마치 오프라인처럼 면학 분위기를 느낄 수 있는 온라인 스터디 모임을 의미한다 ^,^&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 캠스터디 플랫폼으로 디스코드를 선택하고, 약 1주 정도 진행하는 와중... 스터디를 하다 보니 이것저것 필요한 게 꽤 많다는 것을 알게 되었다. 공부 시간 인증이라거나, 무단 불참에 대한 제재 등등..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부하기도 바빠 죽겠는데 이런 규칙들을 일일이 관리하고, 회원들에게 공지하는 건 너무나 귀찮으면서도 시간을 많이 잡아먹는 일이었다. 마침 디스코드에는 봇이라는 아주 좋은 서비스가 있어 혹시 스터디를 관리해주는 봇이 있나 싶어 찾아보았지만, 역시나 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 디스코드 봇을 직접 만들어보았다. 이름하여 &lt;a href=&quot;http://time2study.kro.kr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;Time2Study!&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661254204529&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Discord Bot - Time2Study&quot; data-og-description=&quot;&quot; data-og-host=&quot;time2study.kro.kr&quot; data-og-source-url=&quot;http://time2study.kro.kr&quot; data-og-url=&quot;http://time2study.kro.kr&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;http://time2study.kro.kr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://time2study.kro.kr&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Discord Bot - Time2Study&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time2study.kro.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time2Study의 기능이나 사용법 등에 대해서는 위 링크를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Development&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 기술 블로그이다 보니, Time2Study의 기술 스택과 개발 과정에 대해서 간단하게 소개해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;프로젝트 구성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time2Study 프로젝트는 크게 두 가지의 프로덕트로 나뉠 수 있다. 하나는 이번 프로젝트에서 가장 중요한 역할을 하는 디스코드 봇인 &lt;b&gt;백엔드 프로덕트&lt;/b&gt;이고, 다른 하나는 Time2Study를 소개하는 랜딩 페이지인 &lt;b&gt;프론트엔드 프로덕트&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 프로덕트 모두 Git을 통해 CI/CD를 하였고, 프론트엔드 프로덕트의 경우, &lt;b&gt;&lt;a href=&quot;https://github.com/GwangrokBaek/Time2Study-Web&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;/b&gt;에서 소스코드를 확인할 수 있다. 백엔드 프로덕트는 디스코드 봇에 프리미엄 서비스를 넣어볼까 하는 고민도 있어서 소스코드 공개는 하지 않을 예정이다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;개발 기간&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드 프로덕트&lt;/b&gt;(디스코드 봇)의 경우, 7월 16일에서 8월 10일까지 약 한 달 정도의 개발 기간이 소요되었고, 그 이후부터는 에러 리포트 등에 대응하면서 유지보수를 하는 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드 프로덕트&lt;/b&gt;(랜딩 페이지)의 경우, 디스코드 봇이 완성된 후에 8월 15일부터 8월 18일까지 진행하였다. 아무래도 프로덕트 소개만 하는 웹사이트다 보니 간단해서 개발 기간이 짧았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;사용 Stack&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 프로덕트는 &lt;b&gt;React&lt;/b&gt;를 사용하였으며,&amp;nbsp;백엔드 프로덕트의 경우, 아래 그림에서 볼 수 있듯이 &lt;b&gt;Node js&lt;/b&gt;, &lt;b&gt;Postgres DB&lt;/b&gt;, &lt;b&gt;discord.js&lt;/b&gt; 등을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boGG1C/btrKqaX6VjE/txpKwjUK8FgECn54yZFreK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boGG1C/btrKqaX6VjE/txpKwjUK8FgECn54yZFreK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boGG1C/btrKqaX6VjE/txpKwjUK8FgECn54yZFreK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboGG1C%2FbtrKqaX6VjE%2FtxpKwjUK8FgECn54yZFreK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;474&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 discord.js는 디스코드에서 공식적으로 지원하는 node 패키지가 아니다. 디스코드에서는 개발자들이 디스코드 봇을 개발할 수 있도록 &lt;a href=&quot;https://discord.com/developers/docs/getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Discord API&lt;/a&gt;를 제공하고 있는데, 이를 사용하기 쉽도록 제삼자가 Wrapping 한 것이 &lt;a href=&quot;https://discord.js.org/#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;discord.js&lt;/a&gt;이다.&amp;nbsp;만약 API 사용에 익숙하지 않고, 본인이 node js를 사용한다면 discord.js를 통해 비교적 쉽고 빠르게 디스코드 봇을 개발할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 많은 고마운 분들께서 다양한 언어에서 디스코드 봇을 개발할 수 있도록 비공식 라이브러리를 제공하고 있다. 관련 내용은 아래를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;라이브러리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Python&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;a href=&quot;https://discordpy.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;discord.py&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Java&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;a href=&quot;https://github.com/DV8FromTheWorld/JDA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JDA&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;C#&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;a href=&quot;https://github.com/discord-net/Discord.Net&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Discord.net&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Go&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;a href=&quot;https://pkg.go.dev/github.com/bwmarrin/discordgo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Discordgo&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;개발 과정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스코드 봇을 개발하는 방법에 대해서는 Discord API의 &lt;a href=&quot;https://discord.com/developers/docs/getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;와 discord.js의 &lt;a href=&quot;https://discordjs.guide/#before-you-begin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;에 설명이 아주 잘 되어 있다. 따라서 이번 섹션은 개발하는 과정에서 부딪혔던 여러 이슈와 그 의사 결정 과정에 할애를 할까 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 무료 호스팅? Heroku!&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 단계에서는 로컬 호스트에 테스트 서버를 올려 Time2Study의 기능을 테스트해왔었는데, 실제 유저에게 지속적인 서비스 제공 및 배포를 하기 위해 서버 호스팅이 필요한 시점이 다가왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS를 사용할까 싶었지만, 내 개인 계정은 프리티어가 끝나서 24시 호스팅을 하기 위해서는 요금을 내야 하는 상황이었다. 그래서 무료로 사용할 수 있는 호스팅 업체가 없나 탐색하던 와중, &lt;a href=&quot;https://id.heroku.com/login&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Heroku&lt;/a&gt;를 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Heroku의 무료 플랜을 사용하면 속도가 다소 느리고, 용량이 제한적이라는 한계가 있지만, 내 프로덕트의 경우에는 우수한 성능이 필요하지 않아서 Heroku를 사용하기로 결정하였다. 또한, Github Actions를 통해 배포 파이프라인을 구축하지 않더라도, Heroku에서 특정 브랜치를 기반으로 자동 배포를 지원한다는 점이 마음에 들었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzasXz/btrKqacKmIz/obPbZzqX0e25qZbZEffzk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzasXz/btrKqacKmIz/obPbZzqX0e25qZbZEffzk1/img.png&quot; data-alt=&quot;Deploy - Automatic deploys 탭에서 자동 배포를 활성화할 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzasXz/btrKqacKmIz/obPbZzqX0e25qZbZEffzk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzasXz%2FbtrKqacKmIz%2FobPbZzqX0e25qZbZEffzk1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;291&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Deploy - Automatic deploys 탭에서 자동 배포를 활성화할 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 랜딩 페이지도 마찬가지로 Heroku에서 호스팅을 하기로 결정하였고, &lt;a href=&quot;https://내도메인.한국&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;내도메인.한국&lt;/a&gt; 이라는 곳에서 무료 도메인을 받아 Heroku의 DNS에 연결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Dqam/btrKn7uJXFy/5S2HOx3OeaFjoi64YXhgNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Dqam/btrKn7uJXFy/5S2HOx3OeaFjoi64YXhgNK/img.png&quot; data-alt=&quot;Settings - Domains 탭에서 나만의 도메인을 DNS에 연결할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Dqam/btrKn7uJXFy/5S2HOx3OeaFjoi64YXhgNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Dqam%2FbtrKn7uJXFy%2F5S2HOx3OeaFjoi64YXhgNK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1066&quot; height=&quot;284&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Settings - Domains 탭에서 나만의 도메인을 DNS에 연결할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;  React 웹사이트를 Heroku에 배포할 시, 실패하는 경우&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;React 기반 웹사이트를 Heroku에 배포할 때 Framework와 Buildpacks가 아래와 다른 값으로 설정되어 있으면 실패한다. Heroku app의 Framework와 Buildpacks가 아래처럼 설정되어 있는지 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eoKSrb/btrKpEk4StW/CsJ6zsIHehsqHlLcvwHuj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eoKSrb/btrKpEk4StW/CsJ6zsIHehsqHlLcvwHuj1/img.png&quot; data-alt=&quot;React 기반 웹사이트는 Framework가 React.js (create-react-app) multi로 설정되어 있어야 한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eoKSrb/btrKpEk4StW/CsJ6zsIHehsqHlLcvwHuj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeoKSrb%2FbtrKpEk4StW%2FCsJ6zsIHehsqHlLcvwHuj1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;329&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;React 기반 웹사이트는 Framework가 React.js (create-react-app) multi로 설정되어 있어야 한다&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TDACV/btrKp5JyjNP/wZSYtuk0z972uOCtqnSxuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TDACV/btrKp5JyjNP/wZSYtuk0z972uOCtqnSxuk/img.png&quot; data-alt=&quot;Buildpacks에 https://buildpack-registry.s3.amazonaws.com/buildpacks/mars/create-react-app.tgz를 추가해주자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TDACV/btrKp5JyjNP/wZSYtuk0z972uOCtqnSxuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTDACV%2FbtrKp5JyjNP%2FwZSYtuk0z972uOCtqnSxuk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1060&quot; height=&quot;173&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Buildpacks에 https://buildpack-registry.s3.amazonaws.com/buildpacks/mars/create-react-app.tgz를 추가해주자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. File System이 아닌 RDBMS를 활용하자!&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time2Study는 여러 기능을 지원하기 위해 목표 공부 시간, 보유 뱃지 수 등 유저와 관련된 여러 정보를 수집하고 관리한다. 서비스를 운영하기 이전에는 이러한 데이터들을 단순하게 json 파일로 관리하여 File I/O를 통해 데이터를 읽고, 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 지금은 사용자 수가 적고, 관련된 정보가 적어 File을 I/O 하는 과정이 짧기에 큰 문제가 되지 않는다. 하지만, 나중에 데이터가 많아지고 복잡한 쿼리가 필요한 경우를 고려했을 때 관계형 데이터베이스로 이전하는 것이 옳다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서비스를 운영하기 직전, File System을 버리고 &lt;a href=&quot;https://www.postgresql.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PostgreSQL&lt;/a&gt;로 교체하는 작업을 진행했다. 데이터베이스 핸들링을 편하게 하기 위해 node-postgres라는 패키지를 사용하였고, 자주 쓰이는 연속적인 SQL 쿼리들을 기능 단위로 묶어서 API로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cduyXI/btrKF8fqExA/mNuOCOcO4oKc149O5inBk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cduyXI/btrKF8fqExA/mNuOCOcO4oKc149O5inBk0/img.png&quot; data-alt=&quot;CRUD를 위해 만든 여러 API 중 하나&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cduyXI/btrKF8fqExA/mNuOCOcO4oKc149O5inBk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcduyXI%2FbtrKF8fqExA%2FmNuOCOcO4oKc149O5inBk0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;886&quot; height=&quot;444&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CRUD를 위해 만든 여러 API 중 하나&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS 작업을 하는 것 자체는 큰 어려움이 없었다. 하지만, 앞으로는 확장성, 편의성 등 다양한 변수들을 기반으로 신중하게 아키텍처를 설계해야겠다는 생각이 들었다. 만약 실제 운영 중에 이러한 문제에 부딪혔다면 상당히 당황스러웠을 듯하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 서버 또한 디스코드 봇 서버처럼 상시 동작을 해야 하는데, Heroku에서 용량 제한이 있는 무료 PostgreSQL DB 서버를 특정 app에 붙일 수 있다. 이를 사용할 경우, 디스코드 봇 서버에서는 node-postgres 패키지를 통해 아래 사진에 보이는 credential 정보를 인자로 전달하여 client로서 접속하고 CRUD를 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3cSC/btrKGySt6TW/HytKGUdThD8YizLTsoRMp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3cSC/btrKGySt6TW/HytKGUdThD8YizLTsoRMp1/img.png&quot; data-alt=&quot;Database Credentials 정보가 유출되지 않게끔 조심하자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3cSC/btrKGySt6TW/HytKGUdThD8YizLTsoRMp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3cSC%2FbtrKGySt6TW%2FHytKGUdThD8YizLTsoRMp1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1371&quot; height=&quot;525&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Database Credentials 정보가 유출되지 않게끔 조심하자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1661516413395&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { Client } = require(&quot;pg&quot;)

const client = new Client({
    user: user,
    host: db host,
    database: db name,
    password: db pwd,
    port: port,
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;b&gt;3. Prod 및 Dev 환경으로 분리하자!&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 정식 출시하기 전이라면, 하나의 서버 환경에서 개발하고, 테스트를 하는 것이 큰 문제가 되지 않을 수 있다. 하지만, 이미 정식 운영을 하는 중이라면, 실제 유저들이 사용하는 Prod 서버와 개발 및 검증용 목적인 Dev 서버로 나누는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 기능을 개발하고, 잘 동작하는지 테스트하기 위해 Prod 서버에 배포하였을 경우, 미처 파악하지 못한 오류가 있어서 시스템이 뻗는다면... 상상만 해도 끔찍하다. 지속 가능한 서비스를 제공하고, 위와 같은 불상사를 막기 위해 Prod 및 Dev 환경으로 서버를 분리하는 것은 정말 중요하다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 환경 분리는 JS의 경우, dot-env 패키지와 package.json의 빌드 스크립트를 통해 쉽게 적용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&amp;nbsp;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;.env.dev&lt;/span&gt;&amp;nbsp;및 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;.env.prod&lt;/span&gt; 파일을 생성하고, 이 파일에 서버 Host, DB URL 등 분리가 필요한 환경 변수들의 값을 작성해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1661516159304&quot; class=&quot;makefile&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .env.prod

ENV=prod
DATABASE_URL=https://xxxxxx
DISCORD_TOKEN=XXXXXXXXX&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 아래 사진과 같이 dev용, prod용 빌드 스크립트를 나눠 작성하고, 소스 코드에서는 process.env.XX로 환경 변수에 접근하면 된다. (dot-env 패키지는 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;.env&lt;/span&gt; 파일에 작성되어 있는 값을 환경변수로 가져오므로 아래 스크립트처럼 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;.env.dev&lt;/span&gt; 또는 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;.env.prod&lt;/span&gt;를 .env 파일로 복사해주자)&lt;/p&gt;
&lt;pre id=&quot;code_1661516246496&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// package.json

&quot;scripts&quot;: {
    &quot;build:dev&quot;: &quot;cp .env.dev .env &amp;amp;&amp;amp; node .&quot;,
    &quot;build:prod&quot;: &quot;cp .env.prod .env &amp;amp;&amp;amp; node .&quot;,
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 주의해야 할 것은 환경 변수 특성상, 외부에 노출되어서는 안 될 아주 중요한 정보일 경우가 대다수이므로 만약 github을 통해 CI/CD를 하고 있다면, &lt;span style=&quot;background-color: #99cefa;&quot;&gt;.gitingore&lt;/span&gt; 파일에 환경 변수 관련 파일을 추가하는 것을 잊지 말자.&lt;/p&gt;
&lt;pre id=&quot;code_1661516298666&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .gitignore

node_modules
.env
.env.prod
.env.dev
yarn-error.log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Limits&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Time2Study의 개발을 완료하였다. 아, 완료는 아니고 세상에 내보낼 준비가 되었다 정도로 해두는 게 좋을 것 같다. 요즘 개발자로서 일을 하며 느낀 생각은 사실 프로덕트에 있어서 완료라는 것은 없다는 것이다. 다양한 이유로 유지보수와 모니터링이 필요한 상황이 찾아오며 이를 통해 SW는 생명을 이어 나간다. SW가 생명을 잃는 순간은 더 이상 유지보수를 이어나갈 의지가 없는 경우인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time2Study에게는 앞으로 어떠한 상황이 찾아올까? 지금 당장에 생각하더라도 사실 개발 관련 부분에서나, 개발 외적인 부분에서나 정말 많은 개선이 아직 필요하다. 개발 외적인 부분을 고려하는 것은 이번 프로젝트의 목표가 아니었기에 개발 관련 부분에 대해 조금 다루어보자면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;node-schedule, 너 왜 그래?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time2Study의 여러 기능 중에 뱃지 시스템이란 기능이 있다. 유저의 다양한 정보를 기반으로 특정 조건을 만족할 경우, 뱃지를 제공하는 시스템인데 그러기 위해서는 특정 시점에 유저의 정보를 수집하고 분석하는 크롤러가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 난관에 부딪혔던 점은 언제, 어디서, 어떻게 정보를 수집할 것이냐였다. 내가 떠올린 방법들은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 뱃지 관련 유저 정보를 생성하는 모든 소스 코드 위치에 크롤러를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 시간 간격이 짧은 스케줄러를 하나 만들고, 여기에 크롤러를 붙여서 비교적 빠른 피드백을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 시간 간격이 긴 스케줄러를 하나 만들고, 여기에 크롤러를 붙여서 피드백은 느리지만, 리소스 소모를 최소한으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 1번 방법은 리소스 소모도 적고, 즉각적인 피드백을 기대할 수 있지만 소스 코드 관리 측면에서 불필요한 종속성이 높아지고, 휴먼 에러가 발생할 가능성도 높다고 판단해서 진행하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 방법은 비교적 피드백이 빠르겠지만 유저 경험 측면에서는 3번 방법과 차이 없을 것이라 생각하였고, 그럼에도 불구하고 리소스 소모가 많다는 점에서 진행할 가치가 적다고 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 3번 방법을 선택하였고, node-schedule을 통해 스케줄러를 만들었는데... 우선 크롤링 방법도 마음에 들지 않지만, 그것보다 더 문제인 것은 node-schedule이 어떨 때는 정상적으로 동작하고 어떨 때는 동작하지 않는다는 점이다. 버그 재현이 어려워서 아직 원인 파악 및 해결 방법을 찾지 못하는 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뱃지 시스템을 비롯한 Time2Study에서 스케줄링을 필요로 하는 다양한 기능들에서 node-schedule을 사용 중이라서 여기에 대해 시간을 들여 개선할 필요가 있다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;미래에는 로드 밸런싱이 필요할 수도 있어!&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 유저 수가 현저히 적기 때문에 (앞으로도 그럴 가능성이 매우 크기 때문에) 로드 밸런싱을 고려하지 않고, 하나의 프로세스에서 모든 request 들을 순차적으로 처리하고 있다. 하지만 앞으로 유저 수가 더 늘어날 수도 있으며, 그렇지 않다 하더라도 무중단 배포 등 다양한 상황에서 필요할 수 있기에 로드 밸런싱 작업을 고려하는 것이 좋다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 디스코드 봇 서버에는 로드 밸런싱을 어떻게 적용할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Heroku를 사용하는 경우라면, 단순히 웹 컨테이너 역할을 하는 dyno의 수를 늘려주고, pm2를 사용하여 cpu 개수만큼 디스코드 봇 서버 프로세스를 할당하면 된다. 하지만 단순히 하나의 동일한 디스코드 봇 서버 프로세스를 여러 개 실행하는 경우, 디스코드 서버로부터 오는 메시지에 대해 모든 프로세스들이 응답을 하기 때문에 사용자가 여러 개의 응답을 받는 경우가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 방법을 사용할 경우, 디스코드 봇 서버를 기능 단위로 몇몇의 프로세스로 쪼갠 다음, 디스코드 API 중 &lt;a href=&quot;https://discord.com/developers/docs/topics/gateway#sharding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Shard&lt;/a&gt;라는 기능을 활용해 각각의 프로세스들이 특정 메시지만 처리하도록 해야 한다. 이렇게 하면&amp;nbsp;메시지의 종류에 따라 이를 적절히 분산하여 처리한다는 점에서 한 개의 프로세스에서 모든 것을 처리할 때 보다 성능이 좋아진다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 막상 이렇게 구상하고 보니, 하나의 프로세스를 여러 개의 작은 단위로 쪼갠다는 점에서 MSA와 매우 흡사하다는 것을 알게 되었다. 어차피 dyno의 개수를 늘리면 더 이상 Heroku를 무료로 사용하지도 못하는데... MSA는 실제 사용량에 대해서만 요금을 부과한다는 비용적인 측면에서 이를 사용하지 않을 이유가 없는 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 관점에서 보았을 때 추후에 AWS로 서비스를 이관할 필요가 있다고 생각한다.&lt;/p&gt;</description>
      <category>  프로젝트</category>
      <category>Heroku</category>
      <category>node</category>
      <category>Postgres</category>
      <category>react</category>
      <category>디스코드</category>
      <category>디스코드봇</category>
      <category>백엔드</category>
      <category>프로젝트</category>
      <category>프론트엔드</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/40</guid>
      <comments>https://phil-baek.tistory.com/entry/%EA%B0%93%EC%83%9D-%EC%82%B4%EA%B3%A0-%EC%8B%B6%EC%96%B4-Time2Study%EC%99%80-%ED%95%A8%EA%BB%98-%ED%95%B4-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8#entry40comment</comments>
      <pubDate>Fri, 26 Aug 2022 23:30:50 +0900</pubDate>
    </item>
    <item>
      <title>[리뷰] 코딩 테스트로 시작하는 파이썬 프로그래밍</title>
      <link>https://phil-baek.tistory.com/entry/%EB%A6%AC%EB%B7%B0-%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;리뷰에 앞서&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;출판사 영진닷컴으로부터 책을 제공받아 작성된 리뷰입니다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 1년 만에 블로그를 포스팅하는 기분이다. 그동안 현생이 너무 바쁘고, 힘든 일도 나를 방문해주어 이를 핑계 삼아 블로그로부터 시선을 애써 돌리고 있었다. 그렇게 동굴 속을 또 한 번 헤매고 있던 와중, 이번 일을 통해 옛날 취준생 시절 열심히 파이썬 공부를 하던 때를 떠올리다 보니 어찌어찌 동굴 밖으로 한 걸음 나오게 되었다. 이번 일을 기회삼아 다시 예전처럼 열심히 현생을 살아보자! (역시 나는 방목해서는 안될 부류인가 보다 ^.^)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;2066&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UJvOm/btrCfmd0RXH/3xKdfgqv4CZILH8bWFhGCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UJvOm/btrCfmd0RXH/3xKdfgqv4CZILH8bWFhGCk/img.png&quot; data-alt=&quot;동물도 코딩을 배우는 IT 열풍이란..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UJvOm/btrCfmd0RXH/3xKdfgqv4CZILH8bWFhGCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUJvOm%2FbtrCfmd0RXH%2F3xKdfgqv4CZILH8bWFhGCk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;672&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;2066&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동물도 코딩을 배우는 IT 열풍이란..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;누가 읽으면 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책 제목에서부터 언급되어 있듯이 이 책은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;파이썬 프로그래밍을 &quot;시작하는&quot; 분들에게 적극적으로 추천할 수 있는 책&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 개발을 처음 시작하는 분들은 대학 강의 또는 온라인 강의 사이트에서 프로그래밍 언어 강의를 수강할 것인데, 언어적인 측면에서 도움이 될 수는 있겠지만 그것이 현업 프로젝트에 도움이 된다거나, 취업 시 코딩 테스트에 많은 도움이 되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 테스트를 치르기 위해서는 언어에 대한 개념과 더불어 이를 문제 해결 능력과 연결 지을 수 있어야 한다. 이 책은 그러한 점에서 도움이 되는 책이라 할 수 있는데, 이 책을 읽으면 좋을 분과 그 이유에 대해서 아래에서 설명하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 코딩 테스트 또는 프로그래밍 지식이 전무한 분&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자가 처음부터 자료구조와 알고리즘을 심도 있게 공부하면 쉽게 지칠 수 있다. 하지만 이 책은 스택, 큐 등 자료구조에 대한 내용이나 유명한 알고리즘의 개념에 대해서 직접적으로 언급하지 않는다. 각 문제마다 &lt;b&gt;파이썬의 내장 메서드를 통해 자료구조 및 알고리즘을 맛볼 수 있게 함으로써 언어 개념 및 알고리즘 공부를 함께할 수 있는 효과&lt;/b&gt;를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다른 강의에서는 상세하게 설명하지 않는 파이썬 설치 방법이라거나 파이썬 쉘 실행방법 등 궁금한데 명쾌하게 알려주는 곳이 없고, 뭐라고 검색해야 할지도 몰라서 &lt;b&gt;경험적으로 깨달아야 하는 부분에 대해서도 상세하게 알려준다.&lt;/b&gt; 따라서 프로그래밍을 한 번도 해보지 않은 입문자에게 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자에게 파이썬이 좋을까요?라는 의문점이 들 수도 있다. 이러한 의문이 있는 분은 아래 접힌 글을 펼쳐서 읽어보기를 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 본인이 개발자는 되고 싶은데 무얼 해야 할지 막막하고 개발의 &quot;개&quot;자도 모른다면 나는 개인적으로 파이썬을 먼저 공부하는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;프로그래밍에는 정말 다양한 종류의 지식이 필요하다. 여기에는 언어적 문법 이해도도 있을 것이고, 네트워크, OS와 같은 CS 지식과 더불어 현업 도메인 지식이나 TDD, 애자일과 같은 SW 공학 지식도 포함된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인이 목표하는 바가 뚜렷하다면 해당 주제로 검색을 해서 커리큘럼을 짜 나가면 되겠지만, 그렇지 않다면 먼저 코딩이란 무엇이고&amp;nbsp; 이것이 어떻게 동작하는지에 대해서 이해하고 이를 기반으로 다양한 프로젝트를 하며 본인이 흥미를 느끼는 분야를 탐색해야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 언어 중 파이썬이라는 언어는 책에도 언급되어 있지만, 타 언어에 비해 더 구조적이고 읽기 쉽다. 또한 다른 언어에서 사용할 수 없는 많은 기능들을 포함한다. 따라서 사용자가 보다 쉽게 프로젝트를 구현할 수 있다는 장점이 있다. 또한 이 언어는 특이하게도 웹, 임베디드 시스템, 데스크탑 앱, 게임 등 다양한 영역에서 사용될 수 있기에 아직 목표가 정해지지 않은, 또는 목표가 쉽게 바뀔 수 있는 입문자에게 적극적으로 추천한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 다른 언어 개발 경험이 조금 있지만 파이썬으로 코딩 테스트를 응시하고 싶은 분&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 바로 이 케이스에 속한다. 나는 IoT SW 엔지니어로서 대부분의 프로젝트에서 C/C++을 사용했었다. 하지만 C/C++을 사용해 코딩 테스트를 치는 것은 상당히 어려울 수 있다. 특히 문자열 처리가 매우 악랄한데 파이썬에서는 문자열 처리는 물론 BFS, DFS 알고리즘도 단 몇 줄이면 완성할 수 있다. 그래서 코딩 테스트에 있어 상당히 간편하고 쉬운 파이썬이 많이 추천되고는 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지만 &lt;b&gt;코딩 테스트 하나를 위해 두꺼운&amp;nbsp; 파이썬 책 하나를 사서 현업에 필요도 없는 깊은 내용까지 공부하는 것은 상당히 비효율적이다.&lt;/b&gt; 중요한 것은 위에서 언급했듯이 이 언어의 개념을 문제 해결 능력과 어떻게 연결 짓는가이기 때문이다. 그러한 점에서 이 책은 파이썬 자체의 깊은 개념보다는 &lt;b&gt;파이썬의 각 핵심 개념들을 코딩 테스트 문제 풀이에 어떻게 적용하는가에 초점을 맞추고 있기에 추천한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이런 분들은 아쉬울 수 있어요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재차 얘기하지만 이 책은 &lt;b&gt;파이썬 프로그래밍을 &quot;시작하는&quot; 분들에게 적극적으로 추천할 수 있는 책&lt;/b&gt;이다. 따라서 이미 파이썬에 대해 잘 알고 있었던 분이나 알고리즘 자체에 대해 좀 더 공부하고 싶은 분에게는 다소 아쉬울 수 있다. 따라서 이 책을 읽을 시 아쉬움을 느낄 수 있는 분과 그 이유에 대해서 아래에서 설명하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 파이썬에 대해 더 깊게 배우고 싶은 분&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에 대해 조금 더 깊게 배우고 싶은 분들은 아마 이미 파이썬의 핵심 개념들에 대해서는 통달했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파이썬의 GIL이 어떻게 동작하는지, 리스트 컴프리헨션을 어떻게 사용하는지, 바이트 코드가 어떻게 변환되는지, 제너레이터를 어떻게 어떤 상황에서 사용하면 좋은지 등등 이러한 내용을 찾는 분이라면 번지수를 잘못짚었다. 사실 이러한 내용에 대해서 잘 모르더라도 파이썬으로 코딩 테스트를 치르는 데에는 문제없다. &lt;b&gt;이 책은 파이썬의 핵심 개념을 코딩 테스트에 어떻게 활용하는가에 집중되어 있으므로 다른 책을 살펴보는 것이 좋을 듯하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 더 다양하고 깊은 지식을 요구하는 알고리즘과 자료구조를 공부하고 싶은 분&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책에서 코딩 테스트 내용을 다루기는 하지만, 아쉽게도 알고리즘과 자료구조에 대해 깊은 내용을 설명하고 있지는 않다. 물론 빅오 계산법이라거나 간단한 정렬 및 탐색 알고리즘에 대해서는 소개하고 있다. 그렇지만 &lt;b&gt;백준 골드나 플래티넘 이상에서 필요로 하는 보다 심화된 알고리즘에 대해서 배우고 싶다면 다른 책을 살펴보는 것을 추천한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;본격적인 리뷰 시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 이 책을 읽으면 좋을 분과 아쉬울 수 있는 분에 대해 설명하였다. 지금부터는 이 책이 어떤 식으로 구성되어 있고, 어떤 특징을 갖고 있는지를 설명해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목차&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차는 아래와 같이 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;2340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4H2tg/btrCbn5O5dj/uUD1hPKg8hbkKuk5EZurOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4H2tg/btrCbn5O5dj/uUD1hPKg8hbkKuk5EZurOK/img.png&quot; data-alt=&quot;총 10개의 챕터로 구성되어 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4H2tg/btrCbn5O5dj/uUD1hPKg8hbkKuk5EZurOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4H2tg%2FbtrCbn5O5dj%2FuUD1hPKg8hbkKuk5EZurOK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;578&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;2340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;총 10개의 챕터로 구성되어 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차에서 볼 수 있듯이 &lt;b&gt;각 챕터마다 파이썬의 핵심 개념들에 대해 설명&lt;/b&gt;하고 있으며, 이러한 개념들을 실제로 어떻게 사용할 수 있는지, &lt;b&gt;코딩 테스트에서는 어떻게 활용할 수 있는지&lt;/b&gt;에 대해 설명하고 있다. 또한 목차 제목에는 포함되어 있지 않지만, 파이썬의 기능 중 하나인 모듈 패키지를 어떻게 사용하는지, 문자열을 어떻게 다루는지, 이터러블 객체의 인덱싱 등에 대해서도 잘 설명되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어적 핵심 개념 외에 &quot;6장. 함수를 사용한 프로그램 디자인&quot;에서 &lt;b&gt;하향식 설계&lt;/b&gt;라는 개념을 통해 프로그램을 모듈식으로 설계하고 구현하는 방법이나 &quot;10장. Big-O와 프로그램의 효율성&quot;에서 &lt;b&gt;런타임 최적화와 타이밍 지표&lt;/b&gt;에 대해서도 많은 페이지를 할애하여 설명하고 있다. &lt;b&gt;프로그래밍은 문제를 분할하여 논리적으로 해결&lt;/b&gt;하는 것이며, &lt;b&gt;단순 구현에 그치지 않고 효율을 어떻게 끌어올릴까&lt;/b&gt;에 대해 끊임없이 고민해야 하므로 입문자에게 있어서 좋은 내용이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 챕터 내에서는 아래와 같은 구성 방식을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 - 테스트 케이스 탐구 - 문제 풀이에 필요한 개념 설명 - 개념 확인용 객관식 문제 - 문제 해설 - 연습 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 좋은 점은 &lt;b&gt;개념 설명에 앞서 문제를 먼저 제시하여 독자가 스스로 문제에 대해 고민해볼 시간을 준다는 점&lt;/b&gt;이다. 예제로 제공되는 문제가 극악무도한 난이도를 가지는 것은 아니라서 &quot;테스트 케이스 탐구&quot;에서 제공되는 힌트를 곁들인다면 어렵더라도 시도해 볼 수 있는 문제라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독자는 문제에 대해 고민해본 다음에 뒷 장에 이어 나오는 &quot;문제 풀이에 필요한 개념 설명&quot;을 통해 개념에 대해 숙지하고, &quot;개념 확인용 객관식 문제&quot;를 통해 앞서 배운 개념에 대해 간단히 테스트할 수 있다. 그리고 문제 해설을 통해 문제를 어떤 식으로 풀이하면 되는지에 대해 확인할 수 있는데, 이때 &lt;b&gt;문제 풀이에서는 중요한 행마다 번호를 부여해서 순차적으로 아주 상세하게 풀이 과정을 설명하고 있다. 따라서 입문자도 충분히 내용을 이해하기 쉬울 것이라 생각&lt;/b&gt;한다. 하지만 문제 풀이 과정이 너무 줄글로 작성되어 있어서 읽는데 피로도가 쌓일 수는 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2871&quot; data-origin-height=&quot;1986&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wLbyz/btrB7MS0geD/s65qukeJLHewhg75sW0FR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wLbyz/btrB7MS0geD/s65qukeJLHewhg75sW0FR1/img.png&quot; data-alt=&quot;문제 풀이과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wLbyz/btrB7MS0geD/s65qukeJLHewhg75sW0FR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwLbyz%2FbtrB7MS0geD%2Fs65qukeJLHewhg75sW0FR1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;431&quot; data-origin-width=&quot;2871&quot; data-origin-height=&quot;1986&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문제 풀이과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 연습 문제를 통해서 앞서 배운 개념들에 대해 복습할 수 있다. 연습 문제에 대한 풀이는 없지만, 답안(코드)에 대해서는 해당 문제 출제 온라인 사이트에서도 확인할 수 있고, 또는 &lt;a href=&quot;https://youngjin.com/book/book_detail.asp?prod_cd=9788931466010&amp;amp;seq=7029&amp;amp;cate_cd=1&amp;amp;child_cate_cd=9&amp;amp;goPage=1&amp;amp;orderByCd=1&amp;amp;searchType=Y&amp;amp;keyword1=%C4%DA%B5%F9%20%C5%D7%BD%BA%C6%AE%B7%CE%20%BD%C3%C0%DB%C7%CF%B4%C2#nolink&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;영진닷컴&lt;/a&gt;에서 전체 예제 및 문제에 대한 답안(코드)을 한 번에 다운로드할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHO6A7/btrB48WhYVj/k4kxpiasBJTV7OfLMxES91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHO6A7/btrB48WhYVj/k4kxpiasBJTV7OfLMxES91/img.png&quot; data-alt=&quot;부록CD 다운로드를 클릭하면 전체 답안(코드)을 한 번에 다운로드 받을 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHO6A7/btrB48WhYVj/k4kxpiasBJTV7OfLMxES91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHO6A7%2FbtrB48WhYVj%2Fk4kxpiasBJTV7OfLMxES91%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;370&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;부록CD 다운로드를 클릭하면 전체 답안(코드)을 한 번에 다운로드 받을 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 많이 언급했지만, 이 책의 특징을 꼽으라면 다음과 같을 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 각 챕터마다 배운 파이썬 핵심 개념을 코딩 테스트 문제에 연결 지을 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 프로그래밍 언어 강의 또는 도서를 듣고 읽을 경우, 그래서 이 개념을 코딩 테스트에 어떻게 적용하면 되는데?라고 생각하는 경우가 있다. 마치 학창 시절의 수학 문제를 풀 때와 같다. 수학 공식을 외웠음에도 문제 접근 방법을 몰라 수학 시험지에서 비가 내리던 옛날 추억이 떠오른다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책에서는 &quot;테스트 케이스 탐구&quot;에서 문제 접근 방법을 정리하고, &quot;개념 설명&quot;을 통해 어떠한 개념이 왜 필요하며, 어떤 식으로 사용되는지에 대해 설명하고 있다. 마지막으로 &quot;문제 해설&quot;에서 실제로 코딩 테스트 문제에 앞서 배운 개념을 적용하여 독자가 개념 이해 및 코딩 테스트 문제 풀이를 보다 쉽게 할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 독자가 최대한 이해하기 쉽게 익숙한 용어를 사용하며, 풀이 과정을 상세하게 설명한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 책의 주된 대상이 입문자이다 보니 많이 어려운 용어를 사용하지 않으며, 추상적인 용어도 마치 옆 자리 친구가 설명하듯이 최대한 풀어서 설명한다. 특히나 &quot;문제 풀이&quot;의 풀이 과정은 매우 친절한데, 중요한 행마다 번호를 부여하여 아주 상세하게 설명한다. 예를 들자면 아래와 같은 내용이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⑤ 우리의 다음 임무는 데크의 각 카드를 보고 플레이어에게 점수를 주는 것입니다. 일반적인 for 루프를 사용하면 현재 카드를 볼 수 있습니다. 그러나 그것만으로는 충분하지 않습니다. 현재 카드가 높은 카드라면 이후의 카드도 볼 수 있어야 합니다. 이를 용이하게 하기 위해 범위 기반 for 루프를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⑥ 이 루프를 반복할 때마다 데크에서 가져온 카드를 기반으로 현재 플레이어에게 부여되는 점수를 결정합니다. 점수를 얻는 각 규칙은 데크에 남아 있는 몇 장의 카드에 의해 결정됩니다. remaining 변수가 남은 카드의 수를 알려 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1652607330772&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i in range(NUM_CARDS):        # 5
    card = deck[i]
    points = 0
    remaining = NUM_CARDS - i - 1 # 6

	... (생략)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 볼 수 있듯이 이 행에서 왜 이러한 코드를 작성했고, 이 코드의 역할은 무엇인지에 대해서 자세하게 설명하고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 파이썬 핵심 개념 외에 기초적인 프로그래밍 지식도 함께 기술되어 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급했듯이 파이썬 핵심 개념 외에 하향식 설계 방법, docstring을 작성하는 방법, 프로그램의 런타임을 확인할 수 있는 지표는 무엇인지와 같은 기초적이지만 필수적인 프로그래밍 지식도 함께 기술되어 있다. 물론 책 주제로부터 벗어나는 내용이다 보니 그리 상세하게 기술되어 있지는 않다. 하지만 이와 같은 개념들을 실제로 어떻게 적용할 수 있는지 이해하는 데에는 문제없다고 생각한다. 보다 심도 깊은 내용을 원한다면 이 책에서 알게 된 키워드를 바탕으로 검색하면 될듯하다.&lt;/p&gt;</description>
      <category>  리뷰</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/37</guid>
      <comments>https://phil-baek.tistory.com/entry/%EB%A6%AC%EB%B7%B0-%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D#entry37comment</comments>
      <pubDate>Sun, 15 May 2022 19:13:31 +0900</pubDate>
    </item>
    <item>
      <title>Javascript의 this는 왜이렇게 복잡한가요!</title>
      <link>https://phil-baek.tistory.com/entry/Javascript%EC%9D%98-this%EB%8A%94-%EC%99%9C%EC%9D%B4%EB%A0%87%EA%B2%8C-%EB%B3%B5%EC%9E%A1%ED%95%9C%EA%B0%80%EC%9A%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;JS의 this는 함수 실행 방식과 선언 방식에 따라 값이 달라지기에 나를 포함한 여러 주니어 개발자를 혼란에 빠트리는 주범이다. 그래서 코드를 작성할 때마다 의도치 않은 값이 나올 때가 종종 있고, 그럴 때마다 나는 항상 this에 대해 구글에 검색하곤 한다. 하지만 이렇게 찾은 정보들은 연결되지 않고 단편적으로 내 뇌 속에 자리 잡고 있었는데, 이참에 개념을 한 번 쫙 정리해볼까 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;this 란 무엇인가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this를 한 문장으로 표현하자면, 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;execution context를 참조하는 데 사용되는 키워드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▼ execution context란?&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;execution context란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;execution context란 콜스택에 저장되는 함수의 실행 정보를&lt;/u&gt; 의미한다. JS에서는 &lt;b&gt;특정 함수가 호출될 때마다 콜 스택에 해당 함수의 execution context를 저장&lt;/b&gt;하는데, 이때 execution context는 간단하게 말해서 &lt;b&gt;해당 함수 내에서 사용되는 변수와 함수들의 정보에 대한 총 집합체&lt;/b&gt;라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, this 키워드를 사용하면 이 execution context에서 특정 값을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS 파일이 실행되는 첫 순간에는 global execution context가 생성되고, 여기서 전역 변수 등을 참조한다고 생각하면 된다. 이후 함수가 하나씩 실행될 때마다 function execution context가 하나씩 생성된다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this가 바인딩되는 방식에는 크게 &lt;b&gt;암시적 바인딩&lt;/b&gt;과 &lt;b&gt;명시적 바인딩&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 암시적 바인딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this가 런타임에 &lt;u&gt;함수가 호출되는 방식&lt;/u&gt;에 따라 동적으로 바인딩되는 것을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 명시적 바인딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;bind, call, apply 등을 사용&lt;/u&gt;해서 특정 객체에 대해 this를 바인딩하는 것을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 사실 크게 어렵지 않지만, 일반 함수 정의가 아닌 Arrow function으로 정의할 때에는 그 동작이 조금 달라진다. 이에 대해서는 아래에서 더 자세하게 다루겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 우선 일반 함수로 정의했을 때 함수가 호출되는 방식에 따라 this가 가리키는 값에 대해 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수가 호출되는 방식에 따른 this (일반 함수로 정의)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. Object Method (도트 표기법)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1645952687279&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
    value: 10,
    funcA: function() {
        console.log(&quot;object-funcA&quot;)
        console.log(this.value)
    }
}

temp.funcA() // object-funcA와 10 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 temp 객체가 funcA 메서드를 호출하고 있기에 &lt;b&gt;temp 객체가 this에 암시적으로 바인딩&lt;/b&gt;된다. 따라서 temp.funcA()를 호출할 시, object-funcA와 10을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 경우, 도트 왼쪽의 객체가 this라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기서 this가 난해한 이유가 하나 나오는데, &lt;span style=&quot;color: #ee2323;&quot;&gt;일반 함수가 아닌 Arrow Function으로 정의하거나 Callback Function으로 전달하였을 경우에는 위와 다르게 동작&lt;/span&gt;하기 때문이다. 이에 대해서는&amp;nbsp;Global Context에서의 this에서 설명하겠다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. Global Context&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1645953957986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
    value: 10,
    funcA: function() {
        console.log(&quot;object-funcA&quot;)
        console.log(this.value)
    }
}

let funcA = temp.funcA
funcA() // object-funcA와 undefined 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 funcA에 temp.funcA를 할당하고 이를 호출하고 있다. 위에서 다뤘던 Object Method를 떠올리면 object-funcA와 10을 출력할 것 같지만, 실제로는 object-funcA와 undefined를 출력한다. &lt;b&gt;대체 왜?!!!!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 앞서 말했듯이, this의 바인딩은 정의될 때 정해지는 것이 아니라, &lt;b&gt;런타임에 동적으로 바인딩&lt;/b&gt;되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 temp.funcA는 global execution context에서 정의된 변수 funcA에 할당되었고, global execution context에서 funcA를 호출하고 있다. &lt;b&gt;this의 바인딩은 호출될 때 동적으로 이루어지며&lt;/b&gt;, 이는 곧 브라우저 환경이라면 window.funcA()와 같고, Node js 환경이라면 global.funcA()와 같다. 따라서 undefined를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 만약, 아래와 같이 value 값을 전역에서 정의해준다면 undefined가 아닌 해당 값을 출력할까?&lt;/p&gt;
&lt;pre id=&quot;code_1645955377162&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let value = 5;

let temp = {
    value: 10,
    funcA: function() {
        console.log(&quot;object-funcA&quot;)
        console.log(this.value)
    }
}

let funcA = temp.funcA
funcA() // object-funcA와 undefined 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;아니오&lt;/b&gt;&lt;/span&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 ES6부터는 strict mode가 기본으로 적용되는데, strict mode에서는 global execution context를 가리키는 this가 undefined를 가리키도록 변경되기 때문이다. 따라서 마찬가지로 undefined를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;✻ Callback Function에서의 this&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 앞서 Object Method에서 언급했던&lt;b&gt; Callback Function에서의 동작이 사뭇 다른 이유&lt;/b&gt;도 함께 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1645954500257&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
  value: 10,
  funcA: function () {
    console.log(&quot;object-funcA&quot;);
    console.log(this.value);
  },
};

setTimeout(temp.funcA, 100) // object-funcA와 undefined 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 객체는 동일하지만, setTimeout 함수의 Callback Function으로 temp.funcA를 전달하고 있다. Object Method 방식을 사용하고 있어서 this.value가 10이 출력되어야 할 것 같지만, 실제로는 undefined를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 temp.funcA는 setTimeout의 첫 번째 파라미터에 callback = temp.funcA 형식으로 전달될 것이고, setTimeout 함수의 내부에서 callback()을 호출하여 temp.funcA를 호출할 때 &lt;b&gt;this가 동적으로 바인딩&lt;/b&gt;되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 이유로 JS의 내장 객체 함수인 filter 메서드나 map 메서드와 같이 Callback Function을 인자로 전달받는 메서드의 경우, 추가로 thisArg도 전달받아 직접 this를 바인딩할 수 있게끔 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF4jiq/btrurS7LvPM/ZMQlT2ez9YvgR3MNHFxVdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF4jiq/btrurS7LvPM/ZMQlT2ez9YvgR3MNHFxVdK/img.png&quot; data-alt=&quot;filter 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF4jiq/btrurS7LvPM/ZMQlT2ez9YvgR3MNHFxVdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF4jiq%2FbtrurS7LvPM%2FZMQlT2ez9YvgR3MNHFxVdK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1026&quot; height=&quot;92&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;filter 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ozecG/btruBbYMWCs/of28lKNnGL6IR6cgYShdVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ozecG/btruBbYMWCs/of28lKNnGL6IR6cgYShdVK/img.png&quot; data-alt=&quot;map 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ozecG/btruBbYMWCs/of28lKNnGL6IR6cgYShdVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FozecG%2FbtruBbYMWCs%2Fof28lKNnGL6IR6cgYShdVK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;1022&quot; height=&quot;90&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;map 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this는 이처럼 복잡한 동작 방식을 갖고 있다. 하지만 복잡하다고 해서 사용하지 않을 수도 없다. 그렇다면 어떻게 this를 안전하게 사용할 수 있을까? &lt;b&gt;명시적 바인딩&lt;/b&gt;을 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 명시적 바인딩&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1645956134014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
  value: 10,
  funcA: function () {
    console.log(&quot;object-funcA&quot;);
    console.log(this.value);
  },
};

let funcA = temp.funcA.bind(temp);
funcA(); // object-funcA와 10 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제와 같이 temp.funcA를 할당할 때 bind 메서드를 사용하고 인자로 객체를 전달해주면, this는 해당 객체에 명시적으로 바인딩된다. 이는 Callback Function을 사용할 때에도 마찬가지이다.&lt;/p&gt;
&lt;pre id=&quot;code_1645956232122&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
  value: 10,
  funcA: function () {
    console.log(&quot;object-funcA&quot;);
    console.log(this.value);
  },
};

setTimeout(temp.funcA.bind(temp), 100); // object-funcA와 10 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 this의 동작 방식이 너무 복잡해서 머리가 지끈거린다. 그러면 그냥 편하게 bind 메서드를 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bind 뿐만 아니라 call, apply 메서드도 명시적 바인딩에 사용되는데 이들의 차이점은 추가적인 인자를 전달하느냐, 안 하느냐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 &lt;b&gt;일반 함수로 정의했을 때, 함수가 호출되는 방식에 따른 this의 동작 방식&lt;/b&gt;을 알아보았다. 여기서 끝이라면 좋겠지만, this는 Arrow Function으로 정의했을 때 그 동작 방식이 완전히 달라진다. 따라서 Arrow Function에서의 this에 대해 알아보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Arrow Function에서의 this&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 일반 함수로 정의하였을 경우에 this는 런타임에 동적으로 바인딩된다고 설명했다. 하지만 Arrow Function의 경우에는 &lt;b&gt;함수가 정의되는 시점에 this가 가리키는 값이 정해진다.&lt;/b&gt; 따라서 이를 &lt;b&gt;컴파일 타임에 정적으로 바인딩&lt;/b&gt;된다고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 this의 값은 &lt;b&gt;상위 execution context의 lexical environment&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 간단하게 말하자면, &lt;b&gt;정의되는 시점에서 자신을 둘러싸고 있는 함수의 변수를 사용&lt;/b&gt;한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 예제를 한 번 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1645957351092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Temp {
  constructor() {
    this.value = 10;
  }
  funcA = function () {
    console.log(&quot;class-funcA&quot;);
    console.log(this.value);
  };
  funcB = () =&amp;gt; {
    console.log(&quot;class-funcB&quot;);
    console.log(this.value);
  };
}

const classTemp = new Temp();

classTemp.funcA(); // class-funcA와 10 출력
classTemp.funcB(); // class-funcB와 10 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 funcA와 funcB에 대해 각각 일반 함수 정의 및 Arrow Function 정의 방식을 사용해 클래스 필드로 함수를 정의하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 classTemp.funcA()의 경우 위에서 언급한 &lt;b&gt;Object Method 방식&lt;/b&gt;을 사용하고 있기에 당연히 class-funcA와 10을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;classTemp.funcB()의 경우에도 class-funcB와 10을 출력하고 있는데, 이는 funcB가 정의될 때 상위 context인 Temp 클래스의 lexical environment에 value 값이 저장되어 있으므로 해당 값이 this에 &lt;b&gt;정적으로 바인딩&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;만약 Callback Function으로 전달할 경우에는 this가 어떻게 동작할까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1645957797271&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Temp {
  constructor() {
    this.value = 10;
  }
  funcA = function () {
    console.log(&quot;class-funcA&quot;);
    console.log(this.value);
  };
  funcB = () =&amp;gt; {
    console.log(&quot;class-funcB&quot;);
    console.log(this.value);
  };
}

const classTemp = new Temp();

setTimeout(classTemp.funcA, 100); // class-funcA와 undefined 출력
setTimeout(classTemp.funcB, 100); // class-funcB와 10 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;classTemp.funcA의 경우 예상한 대로 this.value를 출력할 경우, undefined가 출력된다. 하지만, classTemp.funcB의 경우에는 정상적으로 10이 출력되는데 이는 funcB가 Arrow Function으로 정의되었기에 &lt;b&gt;동적으로 바인딩되지 않고 정의되는 시점에 이미 정적으로 바인딩되었기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, funcB에서의 this.value는 funcB가 정의되는 시점에 이미 10이라는 값으로 바인딩되었으므로 그 값이 변하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 &lt;b&gt;Callback Function을 전달할 때 명시적 바인딩을 사용하기도 하지만, 런타임에 this가 바뀌지 않는다는 특성 탓에 대부분 Arrow Function을 전달&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class에서의 Arrow Function에 대해 알아보았는데, 이번에는 객체에서의 Arrow Function에 대해 한 번 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 예제에서 temp.funcB를 호출할 시, this.value로 어떤 값이 출력될까? 객체 내부에 value: 10으로 정의되어 있으므로 10이 출력될까?&lt;/p&gt;
&lt;pre id=&quot;code_1645958321366&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
  value: 10,
  funcA: function () {
    console.log(&quot;object-funcA&quot;);
    console.log(this.value);
  },
  funcB: () =&amp;gt; {
    console.log(&quot;object-funcB&quot;);
    console.log(this.value);
  },
};

temp.funcA();
temp.funcB();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;undefined이다.&lt;/b&gt;&lt;/span&gt; &lt;b&gt;대체 왜...?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 &lt;b&gt;객체는 execution context를 생성하지 않기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 Arrow Function에서의 this는 상위 execution context의 lexical environment에서 해당 값을 찾는다고 하였는데, 이 예제에서 funcB 메서드는 함수가 아닌 객체 내부에서 정의되어 있다. 따라서 &lt;b&gt;상위 execution context는 global execution context이기 때문에 undefined가 출력&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 아까 Class에서는 왜 정상적으로 동작이 되었을까? Class는 JS에서 function 타입으로서 Class가 생성될 때 function execution context가 생성되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 메서드를 정의할 때 Arrow Function으로 정의하면 안 된다는 이유가 바로 여기서 살펴본 this의 동작 방식 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 그렇다면 이런 의문을 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;객체는 execution context를 생성하지 않으니까 Arrow Function을 일반 함수로 한 번 감싸주면, 일반 함수가 Object Method에 의해 암시적으로 객체의 값이 바인딩되니까 괜찮지 않을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 의문에 대해 아래 예제를 통해 다뤄보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1645959365526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = {
  value: 10,
  funcB: () =&amp;gt; {
    console.log(&quot;object-funcB&quot;);
    console.log(this.value);
  },
  funcC: function () {
    console.log(&quot;object-funcC&quot;);
    setTimeout(this.funcB, 100);
  },
  funcD: function () {
    console.log(&quot;object-funcD&quot;);
    setTimeout(() =&amp;gt; {
      console.log(&quot;object-funcD-1&quot;);
      console.log(this.value);
    }, 100);
  },
};

temp.funcC();
temp.funcD();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;funcB는 Arrow Function으로 정의되어 있고, funcC에서는 이를 일반 함수로 감싸고 있다. 그리고 아래에서 Object Method를 사용하고 있기에 temp 객체가 funcC의 this에 동적으로 바인딩될 것이다. 여기서 Arrow Function은 상위 Context의 lexical environment를 참조한다고 했으므로 왠지 10이 정상적으로 출력될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 마찬가지로 &lt;b&gt;undefined&lt;/b&gt;가 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 Arrow Function의 this가 정적으로 바인딩되는 시점은 함수가 정의되는 시점이기 때문이다. funcB는 이미 위에서 먼저 정의되었고, 이때 global execution context에 정적으로 바인딩되었다. 따라서 funcC에서 이를 호출하더라도 funcB의 this 값은 undefined로 고정되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신에 funcD는 funcB를 호출하는 것이 아니라 Arrow Function을 직접 작성했는데, 이 경우에는 &lt;b&gt;funcD의 this 값이 temp&lt;/b&gt; 이므로 Arrow Function이 정의될 때 &lt;b&gt;상위 Context의 lexical environment가 temp 여서 10을 출력하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 this의 동작 방식에 대해 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, this는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 일반 함수로 정의했을 때와 Arrow Function으로 정의했을 때 그 동작 방식이 다르며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 또한 일반 함수 정의 방식에서는 함수가 호출되는 방식에 따라 그 동작 방식이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 글 읽어주셔서 감사합니다! 제 실력이 부족하여 본문의 내용이 실제 동작 원리와 다를 수 있습니다. 그러한 경우에는 댓글로 정정해주시면 감사하겠습니다!!!&lt;/p&gt;</description>
      <category> /JavaScript</category>
      <category>This</category>
      <category>바인딩</category>
      <category>자바스크립트</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/36</guid>
      <comments>https://phil-baek.tistory.com/entry/Javascript%EC%9D%98-this%EB%8A%94-%EC%99%9C%EC%9D%B4%EB%A0%87%EA%B2%8C-%EB%B3%B5%EC%9E%A1%ED%95%9C%EA%B0%80%EC%9A%94#entry36comment</comments>
      <pubDate>Sun, 27 Feb 2022 20:06:17 +0900</pubDate>
    </item>
    <item>
      <title>Cortex-M3를 요약하자면?</title>
      <link>https://phil-baek.tistory.com/entry/Cortex-M3%EB%A5%BC-%EC%9A%94%EC%95%BD%ED%95%98%EC%9E%90%EB%A9%B4</link>
      <description>&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-4825619235969952&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cortex-M3는 2010년에 출시되어 그 나이가 대략 10살을 넘어섰다. 그런데도 불구하고 Cortex-M3의 특징에 대해 정리하는 것은 아직까지 쓰이는 곳이 꽤 많으며, 요즘 보고 있는 책인 ARM Cortex-M3 시스템 프로그래밍 완전정복 1에서 Cortex-M3를 다루고 있기 때문이다! Cortex-M3에 대해 간략히 정리하자면 아래와 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 196px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;ARM Cortex-M3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 16px; text-align: center;&quot;&gt;Architecture&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 16px; text-align: center;&quot;&gt;ARMv7-M&amp;nbsp;&lt;b&gt;(Havard)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;ISA Support&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Thumb /&amp;nbsp;&lt;b&gt;Thumb-2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Pipeline&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;3-stage +&amp;nbsp;&lt;b&gt;branch speculation&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Interrupts&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;NMI + 1 ~ 240 physical interrupts&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Interrupt Latency&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;12 cycles&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Inter-Interrupt Latency&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;6 cycles&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Sleep Modes&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Integrated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Memory Protection&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;8 region&amp;nbsp;&lt;b&gt;MPU&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Dhrystone&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;1.25 DMIPS/MHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;Power Consumption&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;0.19mW/MHz&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 표에서 볼 수 있듯이 Cortex-M3는 Havard Architecture이며, Thumb-2 명령어 셋을 지원하는 것을 확인할 수 있다. 또한 분기 예측을 지원하고, 꽤나 많은 인터럽트 소스를 가지는 것을 확인할 수 있다. 이번에는 Cortex-M3에 대해 간략하게만 알아보고, 다음 글에서 더 자세하게 알아보도록 하겠다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Havard Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 아키텍처에는 대표적으로 두 가지 종류가 있다. 바로 하버드 아키텍처와 폰 노이만 아키텍처이다. 둘의 차이에 대해 간단하게 설명하자면 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;데이터 버스와 명령어 버스가 서로 분리&lt;/b&gt;&lt;/span&gt;되어 있는지에 대한 여부이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폰 노이만 아키텍처는 전체적인 모습은 아래 그림과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;369&quot; data-filename=&quot;Von Neumann.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IXHDm/btraKEHiFLM/hlbjnKboMJSoKzq3FIFrW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IXHDm/btraKEHiFLM/hlbjnKboMJSoKzq3FIFrW0/img.png&quot; data-alt=&quot;폰 노이만 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IXHDm/btraKEHiFLM/hlbjnKboMJSoKzq3FIFrW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIXHDm%2FbtraKEHiFLM%2FhlbjnKboMJSoKzq3FIFrW0%2Fimg.png&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;369&quot; data-filename=&quot;Von Neumann.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;폰 노이만 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림에서 볼 수 있듯이, 폰 노이만 아키텍처는 산술/논리를 담당하는 ALU Unit과 제어를 담당하는 Control Unit이 같은 메모리를 공유한다. 즉, 데이터 메모리와 명령어 메모리가 서로 분리되지 않고, &lt;b&gt;하나의 버스를 공유하는 구조&lt;/b&gt;이다. 구조가 간단하다는 특징이 있지만, 한 가지 문제점이 있다. 바로 &lt;b&gt;명령어를 읽을 때, 데이터를 읽거나 쓸 수 없다는 점&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에, 하버드 아키텍처는 아래 그림의 모습과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;321&quot; data-filename=&quot;Havard.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DAhwn/btraJyAvZZ3/BMJjbyUKMa1KgoYPFYqVE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DAhwn/btraJyAvZZ3/BMJjbyUKMa1KgoYPFYqVE1/img.png&quot; data-alt=&quot;하버드 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DAhwn/btraJyAvZZ3/BMJjbyUKMa1KgoYPFYqVE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDAhwn%2FbtraJyAvZZ3%2FBMJjbyUKMa1KgoYPFYqVE1%2Fimg.png&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;321&quot; data-filename=&quot;Havard.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;하버드 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림에서 볼 수 있듯이, 하버드 아키텍처는 하드웨어적으로 명령용, 그리고 데이터용으로 메모리 및 버스를 분할한 구조로서 명령어와 데이터를 동시에 읽을 수 있다는 특징이 있다. 이러한 하버드 아키텍처는 ARM9부터 적용 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 CPU 내부적으로는 하버드 아키텍처를, 외부적으로는 폰 노이만 아키텍처를 적용해 속도를 더욱 향상시킨 구조를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Thumb-2 ISA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM7은 Thumb 뿐만 아니라, ARM ISA도 지원하지만, Cortex-M3는 Thumb-2만을 지원한다. 이때 Thumb은 16 bits의 사이즈를 가지며, ARM은 32 bits의 사이즈를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM7의 경우, 16 bits 사이즈인 Thumb 명령어를 사용함으로써 &lt;b&gt;코드 사이즈를 줄일 수 있다는 장점&lt;/b&gt;이 있다. 하지만 Thumb 명령어의 경우, 사이즈가 작아 &lt;b&gt;명령어의 종류가 제한적&lt;/b&gt;이다. 또한, 모든 인터럽트는 32 bits 사이즈인 ARM 모드로 동작되어야 하기 때문에 Thumb mode로 동작중에 인터럽트가 발생할 경우, 이러한 &lt;b&gt;모드 전환에 많은 시간이 소모된다는 단점&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 Cortex-M3에서는 Thumb-2 ISA를 지원하는데 &lt;b&gt;Thumb-2는 16 bits와 32 bits 모두를 지원하기에 모드 전환 없이 사용 가능&lt;/b&gt;하다는 특징이 있다. 따라서 &lt;b&gt;모드 전환에 필요한 시간 소모를 줄이고, 코드 사이즈도 줄일 수 &amp;nbsp;있어 매우 효율적&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pipeline Branch Speculation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cortex-M3의 파이프라인은 Fetch - Decode - Execute 3단계 구조에 더해서 분기 예측(Branch Speculation)을 수행한다. 먼저 분기 예측을 수행하게 된 배경에 대해 말해보자면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 명령 수행 중에 Branch(분기문)를 만나게 되면(Control Hazard) 다른 주소로 점프해야하기에 이전에 파이프라인에 저장해둔 정보는 필요가 없다. 즉, &lt;b&gt;PC 값이 변하기 때문에 중간에 있는 명령어를 실행하지 못하는 현상이 발생&lt;/b&gt;하는 것이다. 따라서 이러한 경우, &lt;b&gt;파이프라인을 Flush 하고, 다시 Fetch - Decode - Excecute를 수행하므로 2 Cycle을 더 소모&lt;/b&gt;하게 되는데, 이는 많은 낭비를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;371&quot; data-filename=&quot;branch jump.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9LR5T/btraNIPHr8m/kKwfAuib5lcPrzWTVHMJcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9LR5T/btraNIPHr8m/kKwfAuib5lcPrzWTVHMJcK/img.png&quot; data-alt=&quot;BR 명령어에 의해 다른 주소로 이동하게 되어 SUB와 ADD는 필요 없어짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9LR5T/btraNIPHr8m/kKwfAuib5lcPrzWTVHMJcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9LR5T%2FbtraNIPHr8m%2FkKwfAuib5lcPrzWTVHMJcK%2Fimg.png&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;371&quot; data-filename=&quot;branch jump.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;BR 명령어에 의해 다른 주소로 이동하게 되어 SUB와 ADD는 필요 없어짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분기 예측은 위와 같은 Control Hazard를 피하기 위해 다양한 알고리즘을 사용해서 이를 해결하는 기법이다. 가장 간단하게 예를 하나 들어보자면 다음과 같은 방법이 있다. for 반복문이 100번 실행된다고 가정할 때, 원래는 for 반복문 끝에서 다음 명령을 Fetch 해야 하지만, for 반복문의 첫 번째 명령을 가져옴으로써 파이프라인 Flush 없이 빠른 속도로 100회의 반복문을 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Cortex-M3 Interrupt&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 버전과 달리, Cortex-M3에서는 시스템에서 관리되는 16개의 인터럽트에 더해 &lt;b&gt;최대 240개까지의 인터럽트를 할당&lt;/b&gt;할 수 있다. 또한 인터럽트 지연시간도 대폭 줄었는데, &lt;b&gt;stack operation에 대한 사이클 수가 줄었고, tail-chining 및 late-arriving과 같은 인터럽트 관련 알고리즘이 적용&lt;/b&gt;되어 이전에 비해 보다 빠른 인터럽트 처리가 가능하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;228&quot; data-filename=&quot;스크린샷 2021-07-29 오후 11.53.37.png&quot; width=&quot;457&quot; height=&quot;261&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oWxHs/btraNIhQreC/n95jQqL8x7vnn9sZNTS5aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oWxHs/btraNIhQreC/n95jQqL8x7vnn9sZNTS5aK/img.png&quot; data-alt=&quot;Cortex-M3 Interrupt 블록 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oWxHs/btraNIhQreC/n95jQqL8x7vnn9sZNTS5aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoWxHs%2FbtraNIhQreC%2Fn95jQqL8x7vnn9sZNTS5aK%2Fimg.png&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;228&quot; data-filename=&quot;스크린샷 2021-07-29 오후 11.53.37.png&quot; width=&quot;457&quot; height=&quot;261&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;Cortex-M3 Interrupt 블록 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▼ NMI란?&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Maskable Interrupt&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NMI란 인터럽트 마스크에 영향을 받지 않는 &lt;b&gt;가장 우선순위가 높은 인터럽트&lt;/b&gt;를 의미한다. 즉, 기타 HW 장치에 의해 생성되는 다양한 인터럽트 요구를 우회하기 때문에 다른 인터럽트에 의해 저지(Mask)되지 않는다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 문헌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ARM Cortex-M3 시스템 프로그래밍 완전정복 1&lt;/p&gt;</description>
      <category> /ARM</category>
      <category>Cortex-M3</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/35</guid>
      <comments>https://phil-baek.tistory.com/entry/Cortex-M3%EB%A5%BC-%EC%9A%94%EC%95%BD%ED%95%98%EC%9E%90%EB%A9%B4#entry35comment</comments>
      <pubDate>Thu, 29 Jul 2021 23:57:35 +0900</pubDate>
    </item>
    <item>
      <title>ARM Cortex 시리즈에 대해 알려주세요</title>
      <link>https://phil-baek.tistory.com/entry/ARM-Cortex-%EC%8B%9C%EB%A6%AC%EC%A6%88%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EB%A0%A4%EC%A3%BC%EC%84%B8%EC%9A%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ARM Cortex 시리즈는 ARM11 이후에 나온 시리즈로 모든 ARM 시리즈 중 가장 잘나가는 시리즈이다. 역대 ARM 시리즈에 대한 정보는 아래에서 확인할 수 있다!&lt;/p&gt;
&lt;figure id=&quot;og_1627398209738&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;ARM 아키텍처의 역사&quot; data-og-description=&quot;ARM Cortex-M3 시스템 프로그래밍 완전정복 1의 첫 번째 챕터인 Introduction 파트에서는 전체적인 ARM 제품군의 역사와 Cortex 시리즈의 특징에 대해 설명하고 있다. 먼저 ARM 아키텍처의 역사에 대해 중요&quot; data-og-host=&quot;phil-baek.tistory.com&quot; data-og-source-url=&quot;https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1&quot; data-og-url=&quot;https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QXbnJ/hyK1jfYxMp/1LnoKTiarXbZ5KkMNkg5yk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/CPK13/hyK2CydfGZ/JkhqGH27mU4AXDKrbPhNH1/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/b5Yq1S/hyK1nQbDSu/zxuUKzUmOkEWJSxQBHXKNk/img.png?width=880&amp;amp;height=543&amp;amp;face=0_0_880_543&quot;&gt;&lt;a href=&quot;https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QXbnJ/hyK1jfYxMp/1LnoKTiarXbZ5KkMNkg5yk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/CPK13/hyK2CydfGZ/JkhqGH27mU4AXDKrbPhNH1/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/b5Yq1S/hyK1nQbDSu/zxuUKzUmOkEWJSxQBHXKNk/img.png?width=880&amp;amp;height=543&amp;amp;face=0_0_880_543');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ARM 아키텍처의 역사&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ARM Cortex-M3 시스템 프로그래밍 완전정복 1의 첫 번째 챕터인 Introduction 파트에서는 전체적인 ARM 제품군의 역사와 Cortex 시리즈의 특징에 대해 설명하고 있다. 먼저 ARM 아키텍처의 역사에 대해 중요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;phil-baek.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;원래는 ARM11 이후에 ARM12가 나와야 하는데 이제껏 짝수 시리즈에서 항상 참패를 해서 Cortex라고 이름을 바꾼것일수도,,&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Cortex 시리즈별 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cortex 시리즈는 아래와 같이 크게 3가지 라인으로 구성된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 53px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;ARM Cortex-&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;M&amp;nbsp;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(Microcontroller)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;- &lt;b&gt;저가형 응용프로그램&lt;/b&gt;에 최적화된 임베디드 프로세서 계열&lt;br /&gt;- Thumb2 명령어 세트 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;ARM Cortex-&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;R &lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(Real-Time)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;- &lt;b&gt;실시간 시스템&lt;/b&gt;을 위한 임베디드 프로세서 계열&lt;br /&gt;- ARM, Thumb, Thumb2 명령어 세트 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;ARM Cortex-&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;A &lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(Application)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;- &lt;b&gt;복잡한 OS&lt;/b&gt; 및 사용자 응용 프로그램에 사용하는 응용 프로그램 프로세서 계열&lt;br /&gt;- ARM, Thumb, Thumb2 명령어 세트 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용 외에, ARM Core의 보다 더 자세한 스펙은 &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_ARM_microarchitectures&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;위키피디아&lt;/a&gt;에서 확인할 수 있다. 그 중에서 Cortex-A 시리즈와 Cortex-M 시리즈에 대해서만 부분적으로 발췌해보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;53&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.53.10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/la3n6/btraGFSNZpR/S2K4K7TeDfSpIOHkiFoY11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/la3n6/btraGFSNZpR/S2K4K7TeDfSpIOHkiFoY11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/la3n6/btraGFSNZpR/S2K4K7TeDfSpIOHkiFoY11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fla3n6%2FbtraGFSNZpR%2FS2K4K7TeDfSpIOHkiFoY11%2Fimg.png&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;53&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.53.10.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;281&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.53.03.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/basxy3/btraHslnzYr/KkK4GpmXZsmC74hwM5VkPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/basxy3/btraHslnzYr/KkK4GpmXZsmC74hwM5VkPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/basxy3/btraHslnzYr/KkK4GpmXZsmC74hwM5VkPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbasxy3%2FbtraHslnzYr%2FKkK4GpmXZsmC74hwM5VkPK%2Fimg.png&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;281&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.53.03.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;233&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.52.30.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XPPHt/btraDAdhDov/ri7jzHek8poK5fdG0VUrL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XPPHt/btraDAdhDov/ri7jzHek8poK5fdG0VUrL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XPPHt/btraDAdhDov/ri7jzHek8poK5fdG0VUrL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXPPHt%2FbtraDAdhDov%2Fri7jzHek8poK5fdG0VUrL0%2Fimg.png&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;233&quot; data-filename=&quot;스크린샷 2021-07-27 오후 10.52.30.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Cache (I/D) 는 각각 Instruction Cache, Data Cache를 의미하며, MIPS는 Million Instructions Per Second의 약자로 1초당 수행되는 명령어의 개수를 의미한다. 이때 단위인 DMIPS는 Dhrystone MIPS의 약자로 드라이스톤 벤치마크 테스트의 결과를 정수화해서 하중한 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스펙에 대해 자세한 내용을 모르더라도 전체적으로 Cortex-A 시리즈의 컴퓨팅 파워나 기능들이 훨씬 우세한 것을 확인할 수 있다. (물론 그만큼 가격도 비싸고 전력 소모량도 어마어마하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▶ Cortex-A 시리즈의 MMU 섹션에서 TrustZone이란?&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TrustZone&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중코어 시스템의 특징으로는 코어 중 하나가 보안을 담당하여 개인키, 사용자 데이터 등을 분리하여 보안할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 단일코어에서는 이러한 보안 문제를 어떻게 처리하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 TrustZone을 통해 민감한 정보에 대한 보안을 수행한다. 간단히 말해서 TrustZone은 실행 환경의 메모리, 주변장치, 기능들을 보안 영역과 비보안 영역으로 분리하는 하드웨어 메커니즘이다. 전체적인 동작 과정은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 부팅 시에 TEE(Trusted Execution Environment)를 먼저 부팅하고, REE(Rich Execution Environment)를 부팅해 부팅 단계부터 보안을 수행할 수 있다. 여기서 TEE와 REE는 API를 통해서만 통신이 가능하고, 서로 간의 모드 전환은 예외를 통해서만 가능하다. IoT 시대가 점점 가속화되면서 보안에 대한 문제도 점차 부상하고 있는데, TrustZone은 이러한 상황 속에서 펌웨어 보안 역할을 담당한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ARM은 팹리스 회사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM이 이토록 많은 프로세서 시리즈를 제공하다 보니 하드웨어 회사로 착각하는 경우가 있다. 하지만 ARM은 프로세서를 직접 만드는 회사가 아니라, 위에서 살펴본 &lt;b&gt;Core를 IP(Intellectual Property) 형태로 판매하는 팹리스 회사이다.&lt;/b&gt; 여기서 팹리스란 반도체 공장이 없다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다양한 제조업체들은 ARM의 IP에 대해 라이센스 계약을 맺어 저마다의 커스터마이징이 적용된 제품을 출시하는데, 이러한 제조업체들을 파운드리 회사라고 한다. 예를들어 파운드리 부문에서 삼성전자의 라이벌이라 불리는 TSMC가 대표적인 파운드리 회사라고 할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ARM의 수익 모델인 IP란 무엇인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IP는 반도체 디바이스 내에 구현되기 위해 미리 정의된 기능들의 블록을 의미한다.&lt;/b&gt; 현재는 여기에서 더 확장하여 소프트웨어가 포함된 일종의 솔루션을 제공해 더 많은 부가가치를 창출하고 있다. 그럼 이제 ARM의 돈줄인 IP에 대해 Cortex-M0와 Cortex-M3를 통해 한 번 알아보겠다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Cortex-M0&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cortex-M0는 가장 작고, 가장 적은 전력 소모를 가지는 프로세서로서 에너지 효율성이 높다. 그 구조는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;744&quot; data-filename=&quot;arm_cortexm0_block.jpg&quot; width=&quot;533&quot; height=&quot;474&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK3EY8/btraFEGC53I/aLkFxUNd9pEQpygc0Uc34K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK3EY8/btraFEGC53I/aLkFxUNd9pEQpygc0Uc34K/img.jpg&quot; data-alt=&quot;Cortex-M0 Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK3EY8/btraFEGC53I/aLkFxUNd9pEQpygc0Uc34K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK3EY8%2FbtraFEGC53I%2FaLkFxUNd9pEQpygc0Uc34K%2Fimg.jpg&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;744&quot; data-filename=&quot;arm_cortexm0_block.jpg&quot; width=&quot;533&quot; height=&quot;474&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;Cortex-M0 Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 아키텍쳐에서 몇몇 블록만 설명을 하자면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;WIC &lt;/b&gt;(Wake-up Interrupt Controller)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저전력 상태에서 프로세서 외부의 소형 블록이 인터럽트 감지&lt;/b&gt; 기능을 수행하도록 하는 컴포넌트 (Cortex-M의 저전력 지원 기능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NVIC&lt;/b&gt; (Nested Vectored Interrupt Controller)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중첩된 인터럽트를 제어&lt;/b&gt;하는 컴포넌트.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 인터럽트에는 우선순위가 있다. 만약 인터럽트 수행 중에 보다 높은 우선순위를 가진 인터럽트가 발생했을 경우, NVIC를 통해 현재 인터럽트를 중단하고 해당 인터럽트를 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(NVIC를 사용하기 위해서는 Peripheral의 인터럽트 뿐만 아니라, NVIC의 레지스터에도 해당 인터럽트를 가리키는 비트를 설정하여 둘 다 Enable 해줘야 한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;AMBA&lt;/b&gt; (Advanced Microcontroller Bus Architecture)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM 프로세서에 사용되는 On-chip 버스 통신 규격으로, 그 종류로는 AHB, APB, AXI 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AHB&lt;/b&gt; (Advanced High performance Bus)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AHB 방식은 &lt;b&gt;고속통신을 지원&lt;/b&gt;하고, 데이터를 전송할때 &lt;b&gt;MUX를 기반으로 여러개의 버스를 활성화&lt;/b&gt;한다. 또한 클럭 단위로 데이터를 보내는 것이 아닌, 한 번에 많은 양의 데이터를 계속 보내는 &lt;b&gt;Burst mode&lt;/b&gt;도 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;고속 통신은 파이프라인을 통해 매 클럭마다 데이터를 보낸다는 의미&lt;/b&gt;&lt;/span&gt;이다. 이것이 가능한 이유는 파이프라인덕에 새로운 Instruction 주소를 읽는 것과 이전에 읽은 Instruction 주소에 대해 데이터를 보내는 것을 동시에 수행할 수 있기 때문이다. 따라서 데이터 전송 시에 2 클럭이 아닌 1 클럭을 소모하므로 이를 고속 통신이라고 일컫는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(하지만, 파이프라인의 맨 처음에는 첫 주소를 읽고 난 뒤에 데이터를 보내야 하므로 2 클럭을 소모한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AHB-lite&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AHB는 동시에 여러개의 버스 마스터를 활성화 하지만, &lt;b&gt;AHB-lite는 하나의 버스 마스터만 활성화&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Cortex-M3&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cortex-M3는 제어 시스템이나 무선 네트워킹에 주로 사용되며, 32-bit 프로세서로 준수한 성능을 자랑하면서도 저렴하다는 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;443&quot; data-filename=&quot;unnamed.jpg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI0xgw/btraEgF1SBs/Czh9v2k6If3ob6ym4Kpt41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI0xgw/btraEgF1SBs/Czh9v2k6If3ob6ym4Kpt41/img.jpg&quot; data-alt=&quot;Cortex-M3 Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI0xgw/btraEgF1SBs/Czh9v2k6If3ob6ym4Kpt41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI0xgw%2FbtraEgF1SBs%2FCzh9v2k6If3ob6ym4Kpt41%2Fimg.jpg&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;443&quot; data-filename=&quot;unnamed.jpg&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;Cortex-M3 Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 아키텍쳐에서 몇몇 블록만 설명을 하자면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ETM&lt;/b&gt; (Embedded Trace Macrocell)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trace 정보를 수집하는 방법으로 디버깅 방식 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SWV&lt;/b&gt; (Serial Wire Viewer)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SWO 핀을 통해 디버깅을 수행하는 방법으로 printf를 출력할 수 있고, 2개의 핀만 사용하므로 HW가 간단해진다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;FPB&lt;/b&gt; (Flash Patch and Breakpoint)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flash Patch란 바이너리 코드를 수정하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DWT&lt;/b&gt; (Data Watchpoint and Trace)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세서가 접근할때마다 메모리 위치 정보(주소, 데이터 값 등)를 포착하는 등 선택적 데이터 트레이스를 가능케 해주는 컴포넌트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 트레이스의 종류에 대해 간략히 설명하자면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로파일링 트레이스 : 다양한 동작에서 CPU가 사용하는 클럭 수 포착&lt;/li&gt;
&lt;li&gt;이벤트 트레이스 : 처리한 예외의 이력 및 기간 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DAP&lt;/b&gt; (Debug Access Port)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부의 JTAG 디버거와의 인터페이스를 담당하는 컴포넌트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 JTAG TAP 컨트롤러와 디버거가 직접 통신하였다. 하지만, 멀티코어 디버깅시, 각 컨트롤러가 코어마다 TDI와 TDO를 직렬로 엮어서 체인을 구성했었기 때문에 슬립모드와 같이 코어 하나의 클럭 또는 전력이 차단되는 경우, 다른 코어의 디버거 연결도 끊기는 문제가 있었다. 이를 해결하기 위해 DAP를 통해 각 코어가 DAP에 병렬 연결 됨으로써 문제를 해결.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 문헌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ARM Cortex-M3 시스템 프로그래밍 완전정복 1&lt;/p&gt;</description>
      <category> /ARM</category>
      <category>arm</category>
      <category>Cortex 시리즈</category>
      <category>Cortex-M0</category>
      <category>Cortex-M3</category>
      <category>파운드리</category>
      <category>팹리스</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/34</guid>
      <comments>https://phil-baek.tistory.com/entry/ARM-Cortex-%EC%8B%9C%EB%A6%AC%EC%A6%88%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EB%A0%A4%EC%A3%BC%EC%84%B8%EC%9A%94#entry34comment</comments>
      <pubDate>Tue, 27 Jul 2021 23:57:10 +0900</pubDate>
    </item>
    <item>
      <title>ARM 아키텍처 역사 탐험</title>
      <link>https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ARM Cortex-M3 시스템 프로그래밍 완전정복 1의 첫 번째 챕터인 Introduction 파트에서는 전체적인 ARM 제품군의 역사와 Cortex 시리즈의 특징에 대해 설명하고 있다. 먼저 ARM 아키텍처의 역사에 대해 중요한 특징 위주로 간단하게 정리해보자면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(이전 제품군에서 새롭게 추가된 기능 위주로 작성)&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM2&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;26bits&lt;/span&gt; address&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Non cache&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM3&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 4KB &lt;span style=&quot;background-color: #99cefa;&quot;&gt;cache&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM6&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;32bits&lt;/span&gt;&amp;nbsp;address&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- coprocessor bus 지원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;※ coprocessor&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;코프로세서는 &lt;b&gt;메인 프로세서를 보조하는 프로세서&lt;/b&gt;로 MMU on/off, 캐시 메모리 on/off 등을 수행. CP15라고도 불리며, 어셈블리어 명령어(CDP, MRC, MCR, LDC, STC 등)를 통해 코프로세서를 제어할 수 있다.&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM7&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 8KB, 4KB cache&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ARM7TDMI&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;MMU&lt;/span&gt;(Memory Management Unit), MPU(Memory Protection Unit) 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 8KB cache&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;3-stage pipeline&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;thumb 명령어&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;251&quot; data-filename=&quot;ARM7 pipeline.png&quot; width=&quot;358&quot; height=&quot;290&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvWub3/btrat2hl56J/GbHQNZmTJyGe8lkTvxeak0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvWub3/btrat2hl56J/GbHQNZmTJyGe8lkTvxeak0/img.png&quot; data-alt=&quot;ARM7TDMI Pipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvWub3/btrat2hl56J/GbHQNZmTJyGe8lkTvxeak0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvWub3%2Fbtrat2hl56J%2FGbHQNZmTJyGe8lkTvxeak0%2Fimg.png&quot; data-origin-width=&quot;310&quot; data-origin-height=&quot;251&quot; data-filename=&quot;ARM7 pipeline.png&quot; width=&quot;358&quot; height=&quot;290&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;ARM7TDMI Pipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;※&amp;nbsp;Pipeline&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;펌웨어 코드는 바이너리 파일로 컴파일되어 메모리(ex 플래시 메모리)에 저장된다. 따라서 ARM Core는 프로그램을 실행하기 위해 일련의 단계를 수행하는데, ARM7TDMI는 3 단계의 파이프라인(Fetch - Decode - Execute)을 사용한다. 처리 내용은 아래와 같다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;&lt;b&gt;1. Fetch&lt;/b&gt; : 메모리에서 바이너리 파일의 구성요소인 Instruction을 하나씩 가져온다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;&lt;b&gt;2. Decode&lt;/b&gt; : 가져온 Instruction을 해석하여 Instruction의 종류와 타겟을 판단한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;&lt;b&gt;3. Execute&lt;/b&gt; : 해석한 명령대로 처리 과정을 수행한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;기본적으로 3단계이지만, 더 빠른 처리속도를 위해 현재는 13 단계까지 세분화가 되어왔다. 이때 &lt;b&gt;각 단계별로 사용되는 자원이 다르기에 이를 중첩해서 사용하는데, 이를 파이프라인&lt;/b&gt;이라 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;293&quot; data-filename=&quot;pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Ik60/btrav6qhlUk/NCtQqxrDzIbkDlOoTNN2O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Ik60/btrav6qhlUk/NCtQqxrDzIbkDlOoTNN2O1/img.png&quot; data-alt=&quot;3-stage pipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Ik60/btrav6qhlUk/NCtQqxrDzIbkDlOoTNN2O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Ik60%2Fbtrav6qhlUk%2FNCtQqxrDzIbkDlOoTNN2O1%2Fimg.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;293&quot; data-filename=&quot;pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;3-stage pipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;※ Stall&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARM7TDMI에서 ARM 명령어 중에 메모리에 접근하는 명령어인 LDR 명령어가 사용되면 파이프라인 동작 단계가 달라진다. 왜냐하면 ARM7 파이프라인은 위에서 언급했듯이 Fetch-Decode-Execute 3단계로 이루어지는데 &lt;b&gt;메모리에서 데이터를 읽는 Memory 단계와 레지스터에 쓰기를 수행하는 Write 단계에서는 해당 Cycle이 Stall(중단)&lt;/b&gt;되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;251&quot; data-filename=&quot;Pipeline stall.png&quot; width=&quot;505&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgFo55/btrapsmYq5C/k2IgMNOnOdNgcj8FqHxYW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgFo55/btrapsmYq5C/k2IgMNOnOdNgcj8FqHxYW1/img.png&quot; data-alt=&quot;Pipeline Stall 현상&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgFo55/btrapsmYq5C/k2IgMNOnOdNgcj8FqHxYW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgFo55%2FbtrapsmYq5C%2Fk2IgMNOnOdNgcj8FqHxYW1%2Fimg.png&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;251&quot; data-filename=&quot;Pipeline stall.png&quot; width=&quot;505&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;Pipeline Stall 현상&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▼ &lt;b&gt;하드웨어 디버거를 사용하여 파이프라인 분석 시, 유의점&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC는 항상 Fetch를 가리켜야 한다. 하지만, 만약 0x2098에 mov R0, #0x3이 있고, 0x209C에 break point를 걸었을 경우, 프로그램을 실행했을 때, 레지스터 상태를 보면 R0에 3이 들어가 있는 것을 확인할 수 있다. &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;즉, 0x2098은 이미 Excetue 단계이고, 0x209C는 Fecth가 아닌 Decode 단계라는 의미인데 실제로는 그렇지 않다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;JTAG 기반의 디버거는 &lt;b&gt;실시간성이 깨진다는 특징&lt;/b&gt;이 있기 때문이다. 즉, 실제 파이프라인을 고려하여 디버깅하는 것이 아니라, &lt;b&gt;파이프라인을 무시하고, Fetch-Decode-Execute를 한꺼번에 실행&lt;/b&gt;한다. 따라서 0x2098은 이미 Execute가 끝났지만, 0x209C는 Fetch를 실행할 준비 단계이다.&amp;nbsp;(이때 step 명령을 실행하면, 다음 주소의 Fetch-Decode-Execute가 한꺼번에 처리되면서 ARM Core가 동작한다.) 하지만, 변수에 break point를 설정하는 경우에는 파이프라인을 정확히 지키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : 임베디드 레시피&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM8&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;5-stage pipeline&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;분기 예측&lt;/span&gt; (Branch prediction)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- double-bandwidth memory&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ARM9TDMI&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv4T&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ARM9E&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv5TE&lt;br /&gt;- TCMs&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;※&amp;nbsp;TCM (Tightly Coupled Memory)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;전반적인 속도 향상을 위해 cache를 사용하지만, 완벽하게 실시간으로 저장이나 로딩이 되지는 않는다. 정확한 시간을 예상 가능하게 해주기 위해 &lt;b&gt;빠른 SRAM을 Core에 밀접하게 위치시키는데 이를 TCM&lt;/b&gt;이라 한다.&lt;/span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;171&quot; data-filename=&quot;ARM9 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6OenZ/btraBu5yWuv/fCkP5nb0QkWLE0tKT73cNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6OenZ/btraBu5yWuv/fCkP5nb0QkWLE0tKT73cNK/img.png&quot; data-alt=&quot;ARM9 pipeline (Memory 및 Write 단계가 추가되어 해당 단계에서 다른 명령어가 stall되지 않는다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6OenZ/btraBu5yWuv/fCkP5nb0QkWLE0tKT73cNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6OenZ%2FbtraBu5yWuv%2FfCkP5nb0QkWLE0tKT73cNK%2Fimg.png&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;171&quot; data-filename=&quot;ARM9 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;ARM9 pipeline (Memory 및 Write 단계가 추가되어 해당 단계에서 다른 명령어가 stall되지 않는다)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;ARM10&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv5TE(J)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;6-stage pipeline&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;311&quot; data-filename=&quot;ARM10 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5KbDQ/btraCRSVQNq/qbIcXFPrDAF8WjHPSakPbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5KbDQ/btraCRSVQNq/qbIcXFPrDAF8WjHPSakPbk/img.png&quot; data-alt=&quot;ARM10 pipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5KbDQ/btraCRSVQNq/qbIcXFPrDAF8WjHPSakPbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5KbDQ%2FbtraCRSVQNq%2FqbIcXFPrDAF8WjHPSakPbk%2Fimg.png&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;311&quot; data-filename=&quot;ARM10 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;ARM10 pipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ARM11&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Architecture : ARMv6&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;8, 9-stage pipeline&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #99cefa;&quot;&gt;Thumb-2&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MMU + &lt;span style=&quot;background-color: #99cefa;&quot;&gt;TrustZone&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;543&quot; data-filename=&quot;ARM11 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rwM2b/btrazMMHqga/Z7kMNaTaupzOPVDoNH7T2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rwM2b/btrazMMHqga/Z7kMNaTaupzOPVDoNH7T2K/img.png&quot; data-alt=&quot;ARM11 pipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rwM2b/btrazMMHqga/Z7kMNaTaupzOPVDoNH7T2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrwM2b%2FbtrazMMHqga%2FZ7kMNaTaupzOPVDoNH7T2K%2Fimg.png&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;543&quot; data-filename=&quot;ARM11 pipeline.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;ARM11 pipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 ARM11에는 Pipeline 효율을 높이기 위한&amp;nbsp;&lt;b&gt;Program Flow Prediction&lt;/b&gt;&amp;nbsp;기능이 있다. 이는&amp;nbsp;&lt;b&gt;명령의 흐름을 예측하여 Pipeline의 Stall 현상을 줄여서 효율성을 높이는 역할을 수행&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;333&quot; data-filename=&quot;program flow prediction.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BjDIX/btraHsTccxb/NjDOskvN64MLKUK7tmg5o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BjDIX/btraHsTccxb/NjDOskvN64MLKUK7tmg5o1/img.png&quot; data-alt=&quot;ARM11 Program Flow Prediction&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BjDIX/btraHsTccxb/NjDOskvN64MLKUK7tmg5o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBjDIX%2FbtraHsTccxb%2FNjDOskvN64MLKUK7tmg5o1%2Fimg.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;333&quot; data-filename=&quot;program flow prediction.png&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;ARM11 Program Flow Prediction&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Coprocessor에 있는 CR(Control Register)의 Z[11] bit를 조절하면 Program Flow Prediction을 Enable / Disable 할 수 있다. 이후, Dynamic Branch Prediction, Branch Folding 등 각 세부 기능은 ACR(Auxiliary Control Register)을 통해 설정할 수 있다. (Coprocessor의 레지스터를 읽고, 저장하기 위해 MRC와 MCR 명령어를 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 문헌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ARM Cortex-M3 시스템 프로그래밍 완전정복 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 임베디드 레시피&lt;/p&gt;</description>
      <category> /ARM</category>
      <category>ARM Cortex-M3 시스템 프로그래밍 완전정복1</category>
      <category>ARM 역사</category>
      <category>Cortex-M3</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/33</guid>
      <comments>https://phil-baek.tistory.com/entry/1-1-Introduction-ARM-Cortex-M3-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-1#entry33comment</comments>
      <pubDate>Tue, 27 Jul 2021 09:57:10 +0900</pubDate>
    </item>
    <item>
      <title>취준생이 패스트캠퍼스 강의 제안을 받은 건에 대하여</title>
      <link>https://phil-baek.tistory.com/entry/%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%B8-%EB%82%B4%EA%B0%80-%ED%8C%A8%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%8D%BC%EC%8A%A4-%EA%B0%95%EC%9D%98-%EC%A0%9C%EC%95%88%EC%9D%84-%EB%B0%9B%EC%9D%80-%EA%B1%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 꽤 지나기는 했지만, 지금으로부터 약 한 달 전쯤에 패스트캠퍼스로부터 강의 제안이 왔었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그때가 5월 중순쯤으로 아직 직방에 합격하기 전이었던 터라, 하루하루 열심히 취준 생활을 하던 상황이었다 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메일함에 들어가 보니 안 읽은 메시지가 있길래, 혹시나 지원한 기업에서 온 메일일까? 걱정 반, 기대 반의 마음을 가지고 메일을 열었는데,, 정작 메일함에 들어있던 메일은 생각지도 못한 메일이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;470&quot; width=&quot;802&quot; height=&quot;356&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXyNPp/btq6NuWae9F/UjTNW0CU3C34Rfg9CeZoQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXyNPp/btq6NuWae9F/UjTNW0CU3C34Rfg9CeZoQK/img.png&quot; data-alt=&quot;혹시나 문제가 될 수 있을까봐 세부 내용은 블락처리하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXyNPp/btq6NuWae9F/UjTNW0CU3C34Rfg9CeZoQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXyNPp%2Fbtq6NuWae9F%2FUjTNW0CU3C34Rfg9CeZoQK%2Fimg.png&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;470&quot; width=&quot;802&quot; height=&quot;356&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;혹시나 문제가 될 수 있을까봐 세부 내용은 블락처리하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;?!! 생각지도 못했다. 물론 언젠가 나도 인프런이나 패캠에 있는 강사님들처럼 기똥찬 강의를 해보고 싶다는 생각은 있었지만, 그 순간이 이렇게 빨리 찾아올 줄은 전혀 생각지도 못했다;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌면 인과율이 잘못 계산되어서 5년 뒤에나 도착해야 할 메일이 심각한 오류로 인해 5년 앞당겨져서 도착한 것이 아닐까 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건도 상당히 괜찮았고, 내부적으로 이미 확정되고 제안받은 상황이어서 사실 거절할 이유는 딱히 없었다. 하지만, 2가지 이유로 인해 정중하게 거절할 수밖에 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 지금은 취뽀에 집중할 때&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;20년 12월에 퇴사하고, 아직 이직하지 않은 내게 패스트캠퍼스 강의 제안은 매우 구미가 당겼다. 제안받은 강사료도 꽤나 괜찮은 편이었고, 무엇보다도 커리어에도 좋을 것이라 생각하였다. 하지만, 두 마리 토끼를 잡으려다 두 마리 모두 놓칠 수 있겠다 생각하였고, 그러다 공백기가 길어진다면 오히려 커리어에 악영향을 줄 수 있겠다 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot; width=&quot;456&quot; height=&quot;304&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/topHD/btq6PEqGdfF/orpzOqkLdX1EuT9h0IjeXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/topHD/btq6PEqGdfF/orpzOqkLdX1EuT9h0IjeXk/img.png&quot; data-alt=&quot;잡았다 요놈&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/topHD/btq6PEqGdfF/orpzOqkLdX1EuT9h0IjeXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtopHD%2Fbtq6PEqGdfF%2ForpzOqkLdX1EuT9h0IjeXk%2Fimg.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot; width=&quot;456&quot; height=&quot;304&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;잡았다 요놈&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 아직은 시기상조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했다시피, 나도 언젠가는 온라인 강의를 통해 수많은 개발자들에게 도움이 되고 싶다는 생각을 종종 한다. 하지만, 내가 생각하기에 지금 내 역량은 아직 누구를 가르치기에는 부족한 역량이라 생각하였다. 강의를 듣는 분들은 대부분 취준생이거나 주니어 개발자일 텐데, 아직 깊이가 부족한 내 지식으로는 그분들에게 도움을 주기는커녕, 오히려 혼란을 가중시킬 것이라 판단하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;239&quot; height=&quot;329&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;337&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4LkyG/btq6PbIGV6m/36LKYsQw0YZdy9e1DO8RzK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4LkyG/btq6PbIGV6m/36LKYsQw0YZdy9e1DO8RzK/img.gif&quot; data-alt=&quot;패스트캠퍼스가 슬로우캠퍼스로 바뀌는 기적&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4LkyG/btq6PbIGV6m/36LKYsQw0YZdy9e1DO8RzK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/4LkyG/btq6PbIGV6m/36LKYsQw0YZdy9e1DO8RzK/img.gif&quot; width=&quot;239&quot; height=&quot;329&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;337&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;패스트캠퍼스가 슬로우캠퍼스로 바뀌는 기적&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-4825619235969952&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌 되었든 간에, 그때 당시의 내게 있어서 매우 감사한 제안이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취준 생활은 마치 동굴과도 같아, 그 앞길이 너무 깜깜해 보여 자꾸만 들어왔던 입구를 뒤돌아보게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;과연 내가 이 동굴을 잘 통과할 수 있을까? 반대쪽 출구에 무사히 도달할 수 있을까? 반대쪽 출구는 과연 내가 꿈꾸던 좋은 장소일까?..&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 자신감은 점점 하락하고 하나, 둘씩 포기하거나 타협하는 순간이 찾아온다. 내 취준 생활이 그리 길지는 않았지만, &lt;b&gt;이 강의 제안 메일은 동굴 속을 자신 있게 헤쳐나갈 수 있는 손전등과도 같았다.&lt;/b&gt; 덕분에 자신감을 되찾을 수 있었고, 열심히 나아가서 직방에 합격하기까지 큰 도움이 되었다. (직방 합격 후기는 추후에 올릴 예정이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;그리고 또 하나의 목표를 가지는 계기가 되었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스트캠퍼스나 인프런을 비롯해서 많은 온라인 교육 사이트를 살펴보면, 임베디드 시스템 관련 강의는 거의 없다. 심지어 서적조차 5년이 지난 책들이 대부분이며, 구글에도 최신 정보는 찾아보기가 힘들 정도이다. 찾더라도 대부분 아두이노, 라즈베리파이를 대상으로 한 포스팅들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 앞으로는 임베디드 시스템에 관한 포스팅을 꾸준히 해볼까 한다. 이를 통해, &lt;b&gt;임베디드 시스템 엔지니어를 목표로 하는 많은 취준생분들께서 임베디드 시스템 동굴 탐험을 성공하는데 도움이 되었으면 좋겠다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;853&quot; width=&quot;513&quot; height=&quot;342&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZlAtA/btq6My5JqXu/cUWs8wgSlsTApCpT9apTBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZlAtA/btq6My5JqXu/cUWs8wgSlsTApCpT9apTBK/img.png&quot; data-alt=&quot;많이 밝지는 않더라도, 배터리 만큼은 보장합니다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZlAtA/btq6My5JqXu/cUWs8wgSlsTApCpT9apTBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZlAtA%2Fbtq6My5JqXu%2FcUWs8wgSlsTApCpT9apTBK%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;853&quot; width=&quot;513&quot; height=&quot;342&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;많이 밝지는 않더라도, 배터리 만큼은 보장합니다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> </category>
      <category>강의제안</category>
      <category>손전등</category>
      <category>패스트캠퍼스</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/32</guid>
      <comments>https://phil-baek.tistory.com/entry/%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%B8-%EB%82%B4%EA%B0%80-%ED%8C%A8%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%8D%BC%EC%8A%A4-%EA%B0%95%EC%9D%98-%EC%A0%9C%EC%95%88%EC%9D%84-%EB%B0%9B%EC%9D%80-%EA%B1%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC#entry32comment</comments>
      <pubDate>Wed, 9 Jun 2021 12:30:06 +0900</pubDate>
    </item>
    <item>
      <title>패스트캠퍼스와 함께한 SQLD, 그리고 합격 후기</title>
      <link>https://phil-baek.tistory.com/entry/%ED%8C%A8%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%8D%BC%EC%8A%A4%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%9C-SQLD-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;4월 16일, SQLD (SQL 개발자) 자격증을 드디어 취득하였다. 오늘은 SQLD 자격증 취득을 목표로 하는 많은 분들께 도움이 되고자 합격 후기를 작성해볼까 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;341&quot; width=&quot;978&quot; height=&quot;282&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dR1FkT/btq6EWMxEBJ/FN3oOkycK35GrDwZHCAFP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dR1FkT/btq6EWMxEBJ/FN3oOkycK35GrDwZHCAFP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dR1FkT/btq6EWMxEBJ/FN3oOkycK35GrDwZHCAFP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdR1FkT%2Fbtq6EWMxEBJ%2FFN3oOkycK35GrDwZHCAFP0%2Fimg.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;341&quot; width=&quot;978&quot; height=&quot;282&quot; data-ke-mobilestyle=&quot;widthOrigin&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;사실 임베디드 시스템 엔지니어인 내게 SQLD 자격증은 그다지 가치 있는 자격증은 아니었다. 하지만, IoT 시스템이 점점 핫해지면서 다양한 엣지 디바이스로부터 얻는 데이터를 효율적으로 관리할 때 SQL 지식이 있으면 어느 정도 쓸모 있겠다 싶어 SQL과 데이터베이스를 공부하는 김에 자격증 취득에도 도전하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLD 가 뭐예요?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLD (SQL Developer)는 국가공인자격이다. SQL 관련 자격증으로는 SQLD와 SQLP 가 있는데, 응시자격이 따로 있는 SQLP (SQL Professional) 와는 달리, &lt;b&gt;SQLD 자격증은 별도의 응시자격이 없으며 난이도도 훨씬 쉬운 편이다.&lt;/b&gt; 하지만 SQLP에 비해 상대적으로 쉬운 것이지, 절대적인 난이도가 매우 쉽다는 의미는 아니다,,,! 학부 수업에서 데이터베이스 또는 SQL을 배우지 않은 사람에겐 다소 어려울 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLD의 과목은 크게 두 가지로 나뉘며, 자세한 내용은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;474&quot; width=&quot;720&quot; height=&quot;555&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPs2hD/btq6McmG7Ip/aAMgIyvO8Fa4aGdDKcvLD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPs2hD/btq6McmG7Ip/aAMgIyvO8Fa4aGdDKcvLD0/img.png&quot; data-alt=&quot;객관식 말고 단답형도 나오지만, 난이도가 어렵지는 않다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPs2hD/btq6McmG7Ip/aAMgIyvO8Fa4aGdDKcvLD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPs2hD%2Fbtq6McmG7Ip%2FaAMgIyvO8Fa4aGdDKcvLD0%2Fimg.png&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;474&quot; width=&quot;720&quot; height=&quot;555&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;객관식 말고 단답형도 나오지만, 난이도가 어렵지는 않다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;여기서 2과목인 SQL 기본 및 활용에 Optimizer 최적화 부분도 포함되는데,, 어휴,, 이 부분이 내겐 다소 괴랄했다 ㅠ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;하지만 Optimizer 부분은 시험에 많이 안 나오니, 만약 어렵다면 다른 부분을 더 준비해서 높은 점수를 받는 것을 추천한다!^^&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;참고로, 데이터 관련 자격증에는 SQLD 외에도 DAsP, ADsP 등 다양한 자격증이 있는데 그 내용이 살짝 다르다. SQLD는 SQL 실무 내용을 중점으로 다루는 반면, DAsP와 ADsP는 데이터에 좀 더 중점을 두고 있다. 따라서 본인이 데이터 분석가 또는 DBA 가 아닌, &lt;b&gt;SW 개발자를 목표로 하고 있다면 개인적으로 SQLD를 취득하는 것이 유리하다고 생각한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 이미지는 2021 백엔드 개발자 로드맵인데 &lt;a href=&quot;https://roadmap.sh/backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 원본을 확인할 수 있다. 이미지의 밑을 보면, SQL 및 DB 지식을 필요로 하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;312&quot; width=&quot;708&quot; height=&quot;279&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdl4d1/btq6FvHN8Ds/TlOnUlTVZzEJkjtzlkyBtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdl4d1/btq6FvHN8Ds/TlOnUlTVZzEJkjtzlkyBtk/img.png&quot; data-alt=&quot;공부할게 참 많은 것 같다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdl4d1/btq6FvHN8Ds/TlOnUlTVZzEJkjtzlkyBtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcdl4d1%2Fbtq6FvHN8Ds%2FTlOnUlTVZzEJkjtzlkyBtk%2Fimg.png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;312&quot; width=&quot;708&quot; height=&quot;279&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;공부할게 참 많은 것 같다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;SQLD 자격증, 과연 필요한가?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;그렇다면 SQLD 자격증을 취득할 이유가 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLP 취득을 목표로 하지 않는 한 비추다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLP를 응시하기 위해서는 SQLD 자격증을 보유 중이거나, 다른 데이터 관련 자격증을 보유하고 있어야 한다. SQLP 자격증의 난이도가 꽤 있는 만큼, 관련 업계에서 꽤 괜찮은 자격증인 것으로 알고 있다. 하지만, SQLD 자격증은 그 내용 자체가 무난하고 쉬운 편이라 &lt;b&gt;취업 가점 또는 실력 증빙의 용도로서는 사실 큰 역할을 하지 못한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;657&quot; width=&quot;687&quot; height=&quot;392&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edmFB0/btq6N3o8XRA/LqPlYkmblcc7jS3kMKRRPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edmFB0/btq6N3o8XRA/LqPlYkmblcc7jS3kMKRRPk/img.png&quot; data-alt=&quot;SQLP 응시자격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edmFB0/btq6N3o8XRA/LqPlYkmblcc7jS3kMKRRPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedmFB0%2Fbtq6N3o8XRA%2FLqPlYkmblcc7jS3kMKRRPk%2Fimg.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;657&quot; width=&quot;687&quot; height=&quot;392&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;SQLP 응시자격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;개발자 취준생에게는 쏘쏘다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ISTQB처럼 특정 기업에서 대놓고 우대사항 요건으로 명시된다면 모르겠지만, SQLD 자격증이 우대사항으로 명시되어 있는 경우는 없었던 것으로 기억한다. 애초에 학부에서 다루는 DB 및 SQL 정도의 수준이라, 1분 1초가 아까운 개발자 취준생에게는 자격증 취득을 목표로 하기보다는 개인적으로 학습하는 것을 추천한다! 차라리 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;JDBC를 사용하는 프로젝트를 수행&lt;/span&gt;하며 필요한 DB 및 SQL 지식을 학습하는 것이 훨씬 도움이 될 것이라 생각한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;하지만, 앞서 말했다시피 &lt;b&gt;내용 자체가 그리 어려운 자격증이 아니기 때문에 이력서에 한 줄이라도 더 쓰고 싶은 경우에는 취득하는 것도 나쁘지 않다고 생각한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-4825619235969952&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;패스트캠퍼스 SQLD 완주반, 그리고 학습 방법&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;패스트캠퍼스의 마케팅만큼은 아주 끝내준다고 생각한다,,!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;632&quot; width=&quot;571&quot; height=&quot;431&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQhxE/btq6JnCtPIC/QbbgDLyFi2vBCbBocFm7N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQhxE/btq6JnCtPIC/QbbgDLyFi2vBCbBocFm7N1/img.png&quot; data-alt=&quot;100%? 아니 50% 환급도 어렵다 진심. 역시 인간은 나태하다^^&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQhxE/btq6JnCtPIC/QbbgDLyFi2vBCbBocFm7N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQhxE%2Fbtq6JnCtPIC%2FQbbgDLyFi2vBCbBocFm7N1%2Fimg.png&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;632&quot; width=&quot;571&quot; height=&quot;431&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;100%? 아니 50% 환급도 어렵다 진심. 역시 인간은 나태하다^^&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;퇴사를 하고 학습욕구가 불타오르던 나는 인프런부터 시작해서 패스트캠퍼스까지 다양한 교육 사이트에서 양질의 강의를 탐색하고 있었는데, 마침 패스트캠퍼스에서 &lt;a href=&quot;https://fastcampus.co.kr/SQL_online_challenge?_ga=2.80862103.1691933530.1623090342-1312963358.1623090342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SQLD 완주반 모집 광고&lt;/a&gt;와 맞닥뜨렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLD 완주반의 경우, &lt;b&gt;매주 정해진 커리큘럼과 과제&lt;/b&gt;가 주어진다. 이때 &lt;b&gt;매주 과제를 제출해서 통과하면 강의료를 나눠서 환급&lt;/b&gt;받을 수 있는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;1. 나만 열심히 한다면, 강의료를 환급받을 수 있고&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;2. 그러기 위해서 매주 열심히 강의를 듣고, 과제를 할 것&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;이라는 기대감에 바로 신청하였다. 결과는?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;s&gt;ㅎ 총 6주인가 7주 과정 중에서 딱 2번 환급받았다,,ㅎ&lt;/s&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;400&quot; width=&quot;401&quot; height=&quot;334&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/At9hj/btq6GpOtPxR/SpihH80C52qi6VitrSdKN1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/At9hj/btq6GpOtPxR/SpihH80C52qi6VitrSdKN1/img.gif&quot; data-alt=&quot;하지만 여러분은 분명 100% 환급받을 수 있을 거예요,,!! 팥팅ㅠ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/At9hj/btq6GpOtPxR/SpihH80C52qi6VitrSdKN1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/At9hj/btq6GpOtPxR/SpihH80C52qi6VitrSdKN1/img.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;400&quot; width=&quot;401&quot; height=&quot;334&quot; data-ke-mobilestyle=&quot;widthOrigin&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;figcaption&gt;하지만 여러분은 분명 100% 환급받을 수 있을 거예요,,!! 팥팅ㅠ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;SQLD 완주반은 꽤나 괜찮았다. 구글 클래스룸을 통해 별도의 학습 공간을 제공하는데, 여기에서 &lt;b&gt;완주반 관련 공지, 과제 공지 확인 및 과제 제출이 가능하였고, 뿐만 아니라 강사님들과 질의응답도 가능&lt;/b&gt;했다. 답변도 빠르고, 상세하게 설명해주셔서 많은 도움이 됐었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;약간 아쉬운 점은 신규 강사님들의 강의 외에, 과거에 다른 강사분께서 찍어둔 강의를 패스트캠퍼스에서 이번 완주반에 추가하여 재탕한 것 같은데, &lt;b&gt;해당 강의는 PPT 그대로 읽는 강의여서 좀 아쉬웠다 :(&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;학습 방법에 대해 조금 더 자세하게 설명하자면, &lt;b&gt;1과목인 데이터 모델링의 이해는 내용이 많은 편이 아니어서 달달 외웠으며&lt;/b&gt;, &lt;b&gt;2과목인 SQL 기본 및 활용은 SQLD 완주반에서 제공하는 기출문제 및 과제 풀이를 중심으로 학습&lt;/b&gt;하였다. SQLD 완주반 과제 난이도가 꽤나 어려워서 해당 과제만 다 풀 수 있다면 2과목에서 SQL 문제는 쉽게 풀 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;학습 기간은 대략 4주 정도다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;깃허브에 복습 용도로 SQLD 관련 내용을 정리해두었었는데, &lt;a href=&quot;https://github.com/zester926/SQL_in_practice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1623090789889&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;zester926/SQL_in_practice&quot; data-og-description=&quot;SQL 폐관수련   : SQL 고수가 되는 그날까지! Contribute to zester926/SQL_in_practice development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/zester926/SQL_in_practice&quot; data-og-url=&quot;https://github.com/zester926/SQL_in_practice&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kXZRI/hyKuBVCFIs/8481xY8tKPqr78WDFymriK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/zester926/SQL_in_practice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/zester926/SQL_in_practice&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kXZRI/hyKuBVCFIs/8481xY8tKPqr78WDFymriK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;zester926/SQL_in_practice&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SQL 폐관수련   : SQL 고수가 되는 그날까지! Contribute to zester926/SQL_in_practice development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;시험 후기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Optimizer 부분을 거의 못 봐서 좀 많이 쫄았는데, 전혀 걱정할 필요가 없었던 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1과목 '데이터 모델링의 이해'&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;우선 1과목은 범위가 넓지 않고, 꼬아서 내는 문제도 없으므로 개념서를 달달 외우기만 해도 충분하다. 참고로 &lt;b&gt;1과목 위에 언급한 &lt;a href=&quot;https://github.com/zester926/SQL_in_practice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브&lt;/a&gt;만 참고해도 &lt;span style=&quot;color: #006dd7;&quot;&gt;충분히 100점&lt;/span&gt;을 받을 수 있다.&lt;/b&gt; 시중에 있는 두꺼운 책을 보는 것은 오히려 시간낭비이다 차라리 그 시간에 2과목을 더 준비하는 것이 옳은 전략이다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2과목 'SQL 기본 및 활용'&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;다음으로 2과목이 조금 어려울 수 있다. &lt;b&gt;쿼리 문제의 경우에는 &lt;span style=&quot;color: #006dd7;&quot;&gt;집계 함수, 조인, 서브 쿼리&lt;/span&gt;가 중점적으로 나왔으며, &lt;span style=&quot;color: #006dd7;&quot;&gt;Null 값을 대체할 수 있는 다양한 함수&lt;/span&gt;들이 있는데, 각 함수마다의 서로 다른 결과값을 공부해가는 것을 추천&lt;/b&gt;한다. 이때 SQL 쿼리 문제뿐만 아니라, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;DDL, DML, DCL, TCL의 종류 및 특징&lt;/span&gt;을 잘 파악&lt;/b&gt;하고, 온라인 상에 떠도는 2과목 기출문제들을 잘 풀 수 있다면, 괜찮은 점수를 받을 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;무료 SQLD 기출문제는 온라인에 차고 넘치므로 최대한 많은 문제를 풀어보는 것을 추천한다!&lt;/span&gt;&lt;/p&gt;</description>
      <category>Certs</category>
      <category>SQL 완주반</category>
      <category>SQLD</category>
      <category>데이터베이스</category>
      <category>완주반</category>
      <category>패스트캠퍼스</category>
      <author>philBaek (백광록)</author>
      <guid isPermaLink="true">https://phil-baek.tistory.com/30</guid>
      <comments>https://phil-baek.tistory.com/entry/%ED%8C%A8%EC%8A%A4%ED%8A%B8%EC%BA%A0%ED%8D%BC%EC%8A%A4%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%9C-SQLD-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0#entry30comment</comments>
      <pubDate>Fri, 16 Apr 2021 18:12:22 +0900</pubDate>
    </item>
  </channel>
</rss>