<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>언젠가는</title>
    <link>https://enumclass.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 14:01:16 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>enumclass</managingEditor>
    <image>
      <title>언젠가는</title>
      <url>https://tistory1.daumcdn.net/tistory/3817404/attach/1fd59103d40044919d9dc5db0f494ab4</url>
      <link>https://enumclass.tistory.com</link>
    </image>
    <item>
      <title>Ubuntu root disk size 확장하기</title>
      <link>https://enumclass.tistory.com/276</link>
      <description>&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;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;pre id=&quot;code_1713848489389&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information as of Tue Apr 23 04:47:21 AM UTC 2024

  System load:                      0.0
  Usage of /:                       97.6% of 11.21GB
...

  =&amp;gt; / is using 97.6% of 11.21GB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 본인이 사용하는 우분투의 환경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용량이 97.6%에 도달한 것을 확인 할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;스페이스 확인&lt;/h2&gt;
&lt;pre id=&quot;code_1713848574422&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~$ lsblk
NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0  63.9M  1 loop /snap/core20/2182
loop1                       7:1    0 130.1M  1 loop /snap/docker/2915
loop2                       7:2    0    87M  1 loop /snap/lxd/27948
loop3                       7:3    0  74.2M  1 loop /snap/core22/1122
loop4                       7:4    0  39.1M  1 loop /snap/snapd/21184
loop5                       7:5    0  63.9M  1 loop /snap/core20/2264
loop7                       7:7    0    87M  1 loop /snap/lxd/27428
loop8                       7:8    0  38.7M  1 loop /snap/snapd/21465
sr0                        11:0    1  1024M  0 rom
vda                       252:0    0    25G  0 disk
├─vda1                    252:1    0     1M  0 part
├─vda2                    252:2    0     2G  0 part /boot
└─vda3                    252:3    0    23G  0 part
  └─ubuntu--vg-ubuntu--lv 253:0    0  11.5G  0 lvm  /&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lsblk 커맨드를 이용해서, root space가 11.5G로 설정되어 있는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 루트 디스크인 vda3디스크에 23G가 할당되어 있음을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용하자.&lt;/p&gt;
&lt;pre id=&quot;code_1713848695573&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~# vgs
  VG        #PV #LV #SN Attr   VSize   VFree
  ubuntu-vg   1   1   0 wz--n- &amp;lt;23.00g 11.50g&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;/h2&gt;
&lt;pre id=&quot;code_1713848723430&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~# sudo lvextend -L 22.5G /dev/ubuntu-vg/ubuntu-lv
  Size of logical volume ubuntu-vg/ubuntu-lv changed from &amp;lt;11.50 GiB (2943 extents) to 22.50 GiB (5760 extents).
  Logical volume ubuntu-vg/ubuntu-lv successfully resized.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lvextend 명령어를 이용해서 ext4 파일 시스템을 확장시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fat일 경우 다른 명령어를 사용해야 한다. 'xfs_growfs '&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;pre id=&quot;code_1713848875996&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~# df -h
Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              392M  1.3M  390M   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv   12G   11G     0 100% /
tmpfs                              2.0G     0  2.0G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/vda2                          2.0G  147M  1.7G   9% /boot
tmpfs                              392M  4.0K  392M   1% /run/user/1000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아직도 루트 사이즈는 12G에 멈춰 있는 것을 확인 할 수 있다. 이를 넓혀줘야 한다.&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;pre id=&quot;code_1713848935986&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~# resize2fs /dev/ubuntu-vg/ubuntu-lv
resize2fs 1.46.5 (30-Dec-2021)
Filesystem at /dev/ubuntu-vg/ubuntu-lv is mounted on /; on-line resizing required
old_desc_blocks = 2, new_desc_blocks = 3
The filesystem on /dev/ubuntu-vg/ubuntu-lv is now 5898240 (4k) blocks long.&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;/h2&gt;
&lt;pre id=&quot;code_1713848969635&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root@ubuntu:~# lsblk
NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0  63.9M  1 loop /snap/core20/2182
loop1                       7:1    0 130.1M  1 loop /snap/docker/2915
loop2                       7:2    0    87M  1 loop /snap/lxd/27948
loop3                       7:3    0  74.2M  1 loop /snap/core22/1122
loop4                       7:4    0  39.1M  1 loop /snap/snapd/21184
loop5                       7:5    0  63.9M  1 loop /snap/core20/2264
loop7                       7:7    0    87M  1 loop /snap/lxd/27428
loop8                       7:8    0  38.7M  1 loop /snap/snapd/21465
sr0                        11:0    1  1024M  0 rom
vda                       252:0    0    25G  0 disk
├─vda1                    252:1    0     1M  0 part
├─vda2                    252:2    0     2G  0 part /boot
└─vda3                    252:3    0    23G  0 part
  └─ubuntu--vg-ubuntu--lv 253:0    0  22.5G  0 lvm  /
root@ubuntu:~# df -h
Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              392M  1.3M  390M   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv   23G   11G   11G  53% /
tmpfs                              2.0G     0  2.0G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/vda2                          2.0G  147M  1.7G   9% /boot
tmpfs                              392M  4.0K  392M   1% /run/user/1000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 root의 사이즈가 23G로 변경된 것을 확인 할 수 있다.&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;</description>
      <category>Software활용</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/276</guid>
      <comments>https://enumclass.tistory.com/276#entry276comment</comments>
      <pubDate>Tue, 23 Apr 2024 14:11:24 +0900</pubDate>
    </item>
    <item>
      <title>Llama 2 Local Install</title>
      <link>https://enumclass.tistory.com/275</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Llama 2를 다음 환경에서 작동 시키는 것을 목표로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- OS: Ubuntu 22&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- GPU: Nvidia 3090 TI 24GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Container: tensorflow/tensorflow:latest-gpu-jupyter&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 --cpuset-cpus를 사용해서 cpu의 사용량을 제한시켰다 만약 cpu로 모델이 동작할 경우, 호스트의 모든 리소스를 점유하는 상황이 생길 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1713098541989&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --gpus all --cpuset-cpus=&quot;0-3&quot; -d -it --rm -v &amp;lt;local volume path&amp;gt;:/tf/notebooks -p 8888:8888 tensorflow/tensorflow:latest-gpu-jupyter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Python version: 3.9&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;- 우분투 22와 도커가 설치 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Nvidia GPU가 도커상에서 작동 하는 것이 확인 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/273&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.03.31 - [AI] - [AI] Ubuntu 22 Nvidia GPU Docker로 연동하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hugging face에 로그인 할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 순서&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;hugging face Llama 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/meta-llama/Llama-2-7b-hf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/meta-llama/Llama-2-7b-hf&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 접근 후 사용 등록을 요청한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgQJHE/btsGBufDIev/lwnoYZupCyUiuYgCejypIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgQJHE/btsGBufDIev/lwnoYZupCyUiuYgCejypIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgQJHE/btsGBufDIev/lwnoYZupCyUiuYgCejypIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgQJHE%2FbtsGBufDIev%2FlwnoYZupCyUiuYgCejypIk%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;634&quot; height=&quot;247&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 화면이 나오면 Submit을 누르고 영어로 이름, 성 그리고 주소를 입력하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 소속을 작성하면 되는데, 개인 공부로 할 예정이라 Indpendent라고 작성 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 사람들은 하루가 걸린다고 하는데, 본인은 30분이내에 다음과 같이 사용 가능 메일이 도착하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfXg2Q/btsGCV39jXb/FbxEWbWJ9ysO6UcPBxy3B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfXg2Q/btsGCV39jXb/FbxEWbWJ9ysO6UcPBxy3B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfXg2Q/btsGCV39jXb/FbxEWbWJ9ysO6UcPBxy3B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfXg2Q%2FbtsGCV39jXb%2FFbxEWbWJ9ysO6UcPBxy3B0%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;890&quot; height=&quot;55&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;55&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;h4 data-ke-size=&quot;size20&quot;&gt;Meta original Model (Not preferred)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://llama.meta.com/llama-downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://llama.meta.com/llama-downloads&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7ieA/btsGAYae3pH/LdMoLkgxpWvZrdwCUnGC70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7ieA/btsGAYae3pH/LdMoLkgxpWvZrdwCUnGC70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7ieA/btsGAYae3pH/LdMoLkgxpWvZrdwCUnGC70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7ieA%2FbtsGAYae3pH%2FLdMoLkgxpWvZrdwCUnGC70%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;992&quot; height=&quot;770&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;770&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;28&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deZAw1/btsGAYOO8Vz/jageNfC6qmo27I4E1GcLF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deZAw1/btsGAYOO8Vz/jageNfC6qmo27I4E1GcLF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deZAw1/btsGAYOO8Vz/jageNfC6qmo27I4E1GcLF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeZAw1%2FbtsGAYOO8Vz%2FjageNfC6qmo27I4E1GcLF0%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;363&quot; height=&quot;28&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;28&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이때 24시간만 사용가능한 url이 오는데, 이 url을 &lt;a href=&quot;https://github.com/meta-llama/llama&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/meta-llama/llama&lt;/a&gt; 에 있는 ./download.sh 를 이용해서 다운로드 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인은 여기를 통해서 다운받은 파일을 이용해서 로컬 실행을 하려고 했으나 실패 했다. 파이토치용 파일로 나온거 같은데, example을 봐도 복잡한게 많아서 그냥 hugging face를 이용해서 로컬 실행 시키기로 하였다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lj8qM/btsGCvR7EHm/FoSUhRWYLuh7vVeiZXdy00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lj8qM/btsGCvR7EHm/FoSUhRWYLuh7vVeiZXdy00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lj8qM/btsGCvR7EHm/FoSUhRWYLuh7vVeiZXdy00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLj8qM%2FbtsGCvR7EHm%2FFoSUhRWYLuh7vVeiZXdy00%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;956&quot; height=&quot;262&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 llama2origin이라고 하는 주피터 노트북을 생성하자.&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;Dependency Download&lt;/h3&gt;
&lt;pre id=&quot;code_1713097714963&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip install torch peft huggingface_hub accelerate
!pip install git+https://github.com/huggingface/transformers
!pip install -i https://pypi.org/simple/ bitsandbytes&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;Class Import&lt;/h3&gt;
&lt;pre id=&quot;code_1713097775268&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from huggingface_hub import HfApi, list_models, login
from transformers import LlamaForCausalLM, LlamaTokenizer, BitsAndBytesConfig
from accelerate import Accelerator&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU를 활용하려면 Accelerator는 필수적이다.&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;Hugging&amp;nbsp;Face&lt;/h3&gt;
&lt;pre id=&quot;code_1713097857724&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;login()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blVW3Z/btsGCdYwYqH/rRRRQKcySpYnJDwPyRkE50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blVW3Z/btsGCdYwYqH/rRRRQKcySpYnJDwPyRkE50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blVW3Z/btsGCdYwYqH/rRRRQKcySpYnJDwPyRkE50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblVW3Z%2FbtsGCdYwYqH%2FrRRRQKcySpYnJDwPyRkE50%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;603&quot; height=&quot;341&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 웃고 있으면 Token을 넣어 주고 Login을 누르자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금더 쉬운 방법으로는 다음과 같이 하는 것도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1713099677902&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
from huggingface_hub import login

login(token=&quot;토큰을 여기에&quot;)&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;Token 발행&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQyiYP/btsGDzGxZVT/4lKXsXbuKhRXrg9auAbZT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQyiYP/btsGDzGxZVT/4lKXsXbuKhRXrg9auAbZT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQyiYP/btsGDzGxZVT/4lKXsXbuKhRXrg9auAbZT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQyiYP%2FbtsGDzGxZVT%2F4lKXsXbuKhRXrg9auAbZT0%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;286&quot; height=&quot;443&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/&lt;/a&gt; 로 가서 Settings를 선택한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xBL5P/btsGCXAUbEx/wwBDkOfKA1KyPZd5236Jjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xBL5P/btsGCXAUbEx/wwBDkOfKA1KyPZd5236Jjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xBL5P/btsGCXAUbEx/wwBDkOfKA1KyPZd5236Jjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxBL5P%2FbtsGCXAUbEx%2FwwBDkOfKA1KyPZd5236Jjk%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;960&quot; height=&quot;426&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Access Tokens를 선택하고 New Token 버튼을 클릭해서 토큰을 생성해서 사용하자.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모델 로드&lt;/h3&gt;
&lt;pre id=&quot;code_1713098035525&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;base = 'meta-llama/Llama-2-7b-chat-hf'
tokenizer = LlamaTokenizer.from_pretrained(base)
tokenizer.pad_token_id = 0
tokenizer.padding_side = &quot;left&quot;

quantization_config = BitsAndBytesConfig(load_in_8bit=True,llm_int8_threshold=200.0)

model = LlamaForCausalLM.from_pretrained(
    base,
    quantization_config=quantization_config,
    device_map=accelerator.device,
)&lt;/code&gt;&lt;/pre&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;1118&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be5pgh/btsGBi7l7oJ/V3nFmNHyxS15Rf9SJOILL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be5pgh/btsGBi7l7oJ/V3nFmNHyxS15Rf9SJOILL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be5pgh/btsGBi7l7oJ/V3nFmNHyxS15Rf9SJOILL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe5pgh%2FbtsGBi7l7oJ%2FV3nFmNHyxS15Rf9SJOILL0%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;1118&quot; height=&quot;187&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 로드가 잘 되면 된다. model을 생성할때 생각보다 이런 저런 오류가 많이 나지만 2024년 4월 기준으로는 위의 Dependency와 코드로 잘 작동이 된다. 만약 디펜던시 오류가 계속 발생한다면,&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;390&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckb4oR/btsGColn7K3/XRYwqXWpv9VXvBHqzA0640/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckb4oR/btsGColn7K3/XRYwqXWpv9VXvBHqzA0640/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckb4oR/btsGColn7K3/XRYwqXWpv9VXvBHqzA0640/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckb4oR%2FbtsGColn7K3%2FXRYwqXWpv9VXvBHqzA0640%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;390&quot; height=&quot;151&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널을 리스타트 해보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;nvidia-smi&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 문제없이 로드 되었다면, 아래와 같이 우분투에서 해당 모델이 적제 된것을 확인 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1713098273160&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/python/notebooks$ nvidia-smi
Sun Apr 14 21:37:12 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3090 Ti     Off |   00000000:01:00.0  On |                  Off |
|  0%   48C    P8             28W /  450W |    7487MiB /  24564MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A      2480      G   /usr/lib/xorg/Xorg                             80MiB |
|    0   N/A  N/A      2627      G   /usr/bin/gnome-shell                           72MiB |
|    0   N/A  N/A      5340      G   gnome-control-center                            4MiB |
|    0   N/A  N/A     18306      C   /usr/bin/python3                             7302MiB |
+-----------------------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pid 18306에 모델이 올라가 있다&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;Prompt 작성 및 테스트&lt;/h2&gt;
&lt;pre id=&quot;code_1713098366808&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prompt = &quot;What is your name?&quot;
inputs = tokenizer(prompt, return_tensors=&quot;pt&quot;)
generate_ids = model.generate(inputs.input_ids.to('cuda'), max_length=50)
tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 프롬프트를 작성하고 model.generate를 함으로써 답변을 요청할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 해야할 것은 gpu의 경우 input_ids.to('cuda')를 꼭 해줘야 한다. 아니면 cpu작동을 하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b37JSq/btsGB059LKI/W29Bzd3gIinSrufD0kMhaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b37JSq/btsGB059LKI/W29Bzd3gIinSrufD0kMhaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b37JSq/btsGB059LKI/W29Bzd3gIinSrufD0kMhaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb37JSq%2FbtsGB059LKI%2FW29Bzd3gIinSrufD0kMhaK%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;1128&quot; height=&quot;166&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 답변이 나오면 로컬에 Llama를 간단하게 작동시키는데 성공한 것이다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/275</guid>
      <comments>https://enumclass.tistory.com/275#entry275comment</comments>
      <pubDate>Sun, 14 Apr 2024 21:43:24 +0900</pubDate>
    </item>
    <item>
      <title>Llama 2 Download Error (416 Requested Range Not Satisfiable)</title>
      <link>https://enumclass.tistory.com/274</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Error&lt;/h2&gt;
&lt;pre id=&quot;code_1713089368628&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./download.sh
Enter the URL from email: ...

Resolving download.llamameta.net (download.llamameta.net)... 54.230.61.50, 54.230.61.7, 54.230.61.98, ...
Connecting to download.llamameta.net (download.llamameta.net)|54.230.61.50|:443... connected.
HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable

...

    The file is already fully retrieved; nothing to do.

md5sum: tokenizer_checklist.chk: no properly formatted MD5 checksum lines found&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi&amp;nbsp;download.sh&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 파일에서 모든 '--continue' 를 삭제 해 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1713089444831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

# Copyright (c) Meta Platforms, Inc. and affiliates.
# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement.

set -e

read -p &quot;Enter the URL from email: &quot; PRESIGNED_URL
echo &quot;&quot;
read -p &quot;Enter the list of models to download without spaces (7B,13B,70B,7B-chat,13B-chat,70B-chat), or press Enter for all: &quot; MODEL_SIZE
TARGET_FOLDER=&quot;.&quot;             # where all files should end up
mkdir -p ${TARGET_FOLDER}

if [[ $MODEL_SIZE == &quot;&quot; ]]; then
    MODEL_SIZE=&quot;7B,13B,70B,7B-chat,13B-chat,70B-chat&quot;
fi

echo &quot;Downloading LICENSE and Acceptable Usage Policy&quot;
wget ${PRESIGNED_URL/'*'/&quot;LICENSE&quot;} -O ${TARGET_FOLDER}&quot;/LICENSE&quot;
wget ${PRESIGNED_URL/'*'/&quot;USE_POLICY.md&quot;} -O ${TARGET_FOLDER}&quot;/USE_POLICY.md&quot;&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/meta-llama/llama/issues/760&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/meta-llama/llama/issues/760&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/274</guid>
      <comments>https://enumclass.tistory.com/274#entry274comment</comments>
      <pubDate>Sun, 14 Apr 2024 19:12:43 +0900</pubDate>
    </item>
    <item>
      <title>[AI] Ubuntu 22 Nvidia GPU Docker로 연동하기</title>
      <link>https://enumclass.tistory.com/273</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu 22에 nvidia 드라이버를 최신으로 설치 하고, cuda를 이에 맞추어서 설치 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tensorflow jupyter를 이용해서 gpu를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 가이드는 아래와 같은 오류를 해결하고자 하는 방법으로 활용되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오류 메시지들&lt;/h3&gt;
&lt;pre id=&quot;code_1711891128823&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2024-03-31 12:27:30.432902: E external/local_xla/xla/stream_executor/cuda/cuda_driver.cc:282] failed call to cuInit: CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE: forward compatibility was attempted on non supported HW
2024-03-31 12:27:30.432953: I external/local_xla/xla/stream_executor/cuda/cuda_diagnostics.cc:134] retrieving CUDA diagnostic information for host: 03c8b10def1c
2024-03-31 12:27:30.432958: I external/local_xla/xla/stream_executor/cuda/cuda_diagnostics.cc:141] hostname: 03c8b10def1c
2024-03-31 12:27:30.433014: I external/local_xla/xla/stream_executor/cuda/cuda_diagnostics.cc:165] libcuda reported version is: 545.23.6
2024-03-31 12:27:30.433028: I external/local_xla/xla/stream_executor/cuda/cuda_diagnostics.cc:169] kernel reported version is: 535.161.7
2024-03-31 12:27:30.433032: E external/local_xla/xla/stream_executor/cuda/cuda_diagnostics.cc:251] kernel version 535.161.7 does not match DSO version 545.23.6 -- cannot find working devices in this configuration&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711891205758&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;load library failed: libnvidia-ml.so.1: cannot open shared object file: no such file or directory: unknown&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기존 드라이버 삭제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NVIDIA 드라이버 삭제&lt;/h3&gt;
&lt;pre id=&quot;code_1711890879814&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get remove --purge '^nvidia-.*'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711890929828&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo ubuntu-drivers autoinstall&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711890940835&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CUDA&amp;nbsp; 드라이버 삭제&lt;/h3&gt;
&lt;pre id=&quot;code_1711890969738&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get --purge remove &quot;*cuda*&quot;
sudo apt-get --purge remove &quot;*cudnn*&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NVIDIA toolkit 및 docker 삭제&lt;/h3&gt;
&lt;pre id=&quot;code_1711891053351&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get remove --purge nvidia-docker2 nvidia-container-toolkit docker docker-engine docker.io containerd runc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Driver 재설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.nvidia.com/cuda-toolkit-archive&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.nvidia.com/cuda-toolkit-archive&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드라이버의 버전과 CUDA의 버전은 반듯이 동일해야 한다. 이를 해결하기 위해 Nvidia 드라이버도 CUDA에 맞추어야 하기 때문에 위의 페이지 가이드를 따라서 드라이버도 설치 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.nvidia.com/cuda-downloads?target_os=Linux&amp;amp;target_arch=x86_64&amp;amp;Distribution=Ubuntu&amp;amp;target_version=22.04&amp;amp;target_type=deb_network&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.nvidia.com/cuda-downloads?target_os=Linux&amp;amp;target_arch=x86_64&amp;amp;Distribution=Ubuntu&amp;amp;target_version=22.04&amp;amp;target_type=deb_network&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711891396531&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-4&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711891409851&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install -y nvidia-driver-550-open
sudo apt-get install -y cuda-drivers-550&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;Docker 및 Docker toolkit 설치&lt;/h2&gt;
&lt;pre id=&quot;code_1711891561983&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update
sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1711892035982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo usermod -aG docker $USER
newgrp docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker Configuration Result&lt;/h4&gt;
&lt;pre id=&quot;code_1712202978771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker info | grep nvidia
 Runtimes: io.containerd.runc.v2 nvidia runc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위와 같이 나와야 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다음과 같이 도커 데몬 설정 파일을 수정하는 것을 추천한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;/etc/docker/daemon.json&lt;/p&gt;
&lt;pre id=&quot;code_1712291616065&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;runtimes&quot;: {
        &quot;nvidia&quot;: {
            &quot;path&quot;: &quot;nvidia-container-runtime&quot;,
            &quot;runtimeArgs&quot;: []
        }
    },
    &quot;default-runtime&quot;:&quot;nvidia&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reboot&lt;/h3&gt;
&lt;pre id=&quot;code_1711891599937&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo reboot&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;Docker 연동 확인&lt;/h2&gt;
&lt;pre id=&quot;code_1711891669401&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi
[sudo] password for steven: 
Unable to find image 'nvidia/cuda:11.8.0-base-ubuntu22.04' locally
11.8.0-base-ubuntu22.04: Pulling from nvidia/cuda
aece8493d397: Already exists 
5e3b7ee77381: Pull complete 
5bd037f007fd: Pull complete 
4cda774ad2ec: Pull complete 
775f22adee62: Pull complete 
Digest: sha256:f895871972c1c91eb6a896eee68468f40289395a1e58c492e1be7929d0f8703b
Status: Downloaded newer image for nvidia/cuda:11.8.0-base-ubuntu22.04
Sun Mar 31 13:27:28 2024       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3090 Ti     Off |   00000000:01:00.0 Off |                  Off |
|  0%   39C    P8             14W /  450W |      84MiB /  24564MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;Notebook 설치 및 GPU 연동 확인&lt;/h2&gt;
&lt;pre id=&quot;code_1711891753468&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --gpus all -d -p 8888:8888 tensorflow/tensorflow:latest-gpu-jupyter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;Notebook 연동 및 token 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;localhost:8888&lt;/p&gt;
&lt;pre id=&quot;code_1711891880867&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker exec -it 8bf /bin/bash

________                               _______________
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ /
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


WARNING: You are running this container as root, which can cause new files in
mounted volumes to be created as the root user on your host machine.

To avoid this, run the container by specifying your user's userid:

$ docker run -u $(id -u):$(id -g) args...

root@8bfb03e57f8b:/tf# history
    1  jupyter server list
    2  exit
    3  history
root@8bfb03e57f8b:/tf# jupyter server list
Currently running servers:
http://8bfb03e57f8b:8888/?token=48c6439946bd0e34d389c792708f3ee35cf98d929a889c99 :: /tf&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo5yRs/btsGfKICNSL/kt9ww6kjY9D69mN7ozYXi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo5yRs/btsGfKICNSL/kt9ww6kjY9D69mN7ozYXi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo5yRs/btsGfKICNSL/kt9ww6kjY9D69mN7ozYXi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo5yRs%2FbtsGfKICNSL%2Fkt9ww6kjY9D69mN7ozYXi1%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;1348&quot; height=&quot;333&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드를 입력 한 후 GPU의 갯수가 나오면 성공이다.&lt;/p&gt;
&lt;pre id=&quot;code_1711891957120&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import tensorflow as tf

print(&quot;Num GPUs Available: &quot;, len(tf.config.experimental.list_physical_devices('GPU')))&lt;/code&gt;&lt;/pre&gt;
&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;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JV4C0/btsGegIuXau/ZrnV8G9kFwCQnf5cEkMFN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JV4C0/btsGegIuXau/ZrnV8G9kFwCQnf5cEkMFN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JV4C0/btsGegIuXau/ZrnV8G9kFwCQnf5cEkMFN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJV4C0%2FbtsGegIuXau%2FZrnV8G9kFwCQnf5cEkMFN0%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;1116&quot; height=&quot;381&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/273</guid>
      <comments>https://enumclass.tistory.com/273#entry273comment</comments>
      <pubDate>Sun, 31 Mar 2024 22:34:14 +0900</pubDate>
    </item>
    <item>
      <title>openssl 명령어 모음</title>
      <link>https://enumclass.tistory.com/272</link>
      <description>&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;내용&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;명령어&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기타&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;certification 내용 확인&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;gt;&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-in&amp;nbsp;cert.pem&amp;nbsp;-noout&amp;nbsp;-text&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Certificate:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Data:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Version:&amp;nbsp;3&amp;nbsp;(0x2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Serial&amp;nbsp;Number:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Signature&amp;nbsp;Algorithm:&amp;nbsp;sha256WithRSAEncryption&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Issuer:&amp;nbsp;C&amp;nbsp;=&amp;nbsp;US,&amp;nbsp;O&amp;nbsp;=&amp;nbsp;Let's&amp;nbsp;Encrypt,&amp;nbsp;CN&amp;nbsp;=&amp;nbsp;R3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Validity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;발급된 cert파일과 private key가 같은지 확인&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&amp;gt; openssl x509 -noout -modulus -in cert.pem | openssl md5&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;gt; openssl rsa -noout -modulus -in privkey.pem | openssl md5&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;MD5(stdin)=&amp;nbsp;41dc41d6318ac4e5586bfa0ce8e1249b&lt;br /&gt;&lt;br /&gt;MD5(stdin)=&amp;nbsp;41dc41d6318ac4e5586bfa0ce8e1249b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;CA cert 확인&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;gt;&amp;nbsp;openssl&amp;nbsp;verify&amp;nbsp;-CAfile&amp;nbsp;isrgrootx1.pem&amp;nbsp;RSA-chain.pem&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;RSA-chain.pem:&amp;nbsp;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;ssl connectivity 확인&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;openssl s_client -verify_return_error -connect &amp;lt;domain&amp;gt;:&amp;lt;ssl port&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;CONNECTED(00000005)&lt;br /&gt;depth=2&amp;nbsp;C&amp;nbsp;=&amp;nbsp;US,&amp;nbsp;O&amp;nbsp;=&amp;nbsp;Internet&amp;nbsp;Security&amp;nbsp;Research&amp;nbsp;Group,&amp;nbsp;CN&amp;nbsp;=&amp;nbsp;ISRG&amp;nbsp;Root&amp;nbsp;X1&lt;br /&gt;verify&amp;nbsp;return:1&lt;br /&gt;depth=1&amp;nbsp;C&amp;nbsp;=&amp;nbsp;US,&amp;nbsp;O&amp;nbsp;=&amp;nbsp;Let's&amp;nbsp;Encrypt,&amp;nbsp;CN&amp;nbsp;=&amp;nbsp;R3&lt;br /&gt;verify&amp;nbsp;return:1&lt;br /&gt;depth=0 CN = &amp;lt;domain&amp;gt;&lt;br /&gt;verify&amp;nbsp;return:1&lt;br /&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>Software활용</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/272</guid>
      <comments>https://enumclass.tistory.com/272#entry272comment</comments>
      <pubDate>Fri, 1 Sep 2023 10:20:18 +0900</pubDate>
    </item>
    <item>
      <title>Window 10 pro 다중 원격 접속하기 (Hex edit)</title>
      <link>https://enumclass.tistory.com/271</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 10 Pro 또는 Enterprise의 경우는 원격 접속이 지원된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 외부에서 window os에 접속하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 오직 하나의 Session만 지원함으로써 동시에 2개 이상의 Session이 윈도우에 접근 할 수는 없는 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 이슈를 해결하기 가장 쉬운것은 RDP를 설치 하는 것이다. (이것은 Window 모든 버전을 지원한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/stascorp/rdpwrap/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/stascorp/rdpwrap/releases&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691554277412&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;Releases &amp;middot; stascorp/rdpwrap&quot; data-og-description=&quot;RDP Wrapper Library. Contribute to stascorp/rdpwrap development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/stascorp/rdpwrap/releases&quot; data-og-url=&quot;https://github.com/stascorp/rdpwrap/releases&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fm7Di/hyTAuNTlS0/Td8mn5OLxD0xyXGISYHt6k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/stascorp/rdpwrap/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/stascorp/rdpwrap/releases&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fm7Di/hyTAuNTlS0/Td8mn5OLxD0xyXGISYHt6k/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;Releases &amp;middot; stascorp/rdpwrap&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;RDP Wrapper Library. Contribute to stascorp/rdpwrap 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;이와 관련된 내용은 google에 많음으로 설명하지 않겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 10 Pro에서 dll 파일을 수정함으로써 RDP 없이 멀티 세션이 가능한것을 확인해서 이렇게 글을 남기고자 한다.&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;&lt;a href=&quot;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/#Modifying_Termsrvdll_File_to_Allow_Multiple_RDP_Session&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/#Modifying_Termsrvdll_File_to_Allow_Multiple_RDP_Session&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691554388640&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;Enable Remote Desktop For Multiple Users on Windows 10/11&quot; data-og-description=&quot;A detailed how to guide about enabling remote desktop for multiple users on Windows 10. Find out how to setup multiple remote desktop connections with the help of RDP Wrapper or by modifying the registry.&quot; data-og-host=&quot;www.helpwire.app&quot; data-og-source-url=&quot;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/#Modifying_Termsrvdll_File_to_Allow_Multiple_RDP_Session&quot; data-og-url=&quot;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qwBXB/hyTBAeEQ0N/U2skLf5WqfzQVKLeIpnXgk/img.jpg?width=1024&amp;amp;height=513&amp;amp;face=0_0_1024_513,https://scrap.kakaocdn.net/dn/b5vwfp/hyTAtuHoB9/iSGxvJ7UWPjeVVLIrJ56N1/img.jpg?width=780&amp;amp;height=776&amp;amp;face=0_0_780_776,https://scrap.kakaocdn.net/dn/TwyDE/hyTAFWbkc3/LZNrDSHzGz7J2bi3aZlWqk/img.jpg?width=1024&amp;amp;height=513&amp;amp;face=0_0_1024_513&quot;&gt;&lt;a href=&quot;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/#Modifying_Termsrvdll_File_to_Allow_Multiple_RDP_Session&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.helpwire.app/blog/allow-multiple-remote-desktop-connections-windows-10/#Modifying_Termsrvdll_File_to_Allow_Multiple_RDP_Session&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qwBXB/hyTBAeEQ0N/U2skLf5WqfzQVKLeIpnXgk/img.jpg?width=1024&amp;amp;height=513&amp;amp;face=0_0_1024_513,https://scrap.kakaocdn.net/dn/b5vwfp/hyTAtuHoB9/iSGxvJ7UWPjeVVLIrJ56N1/img.jpg?width=780&amp;amp;height=776&amp;amp;face=0_0_780_776,https://scrap.kakaocdn.net/dn/TwyDE/hyTAFWbkc3/LZNrDSHzGz7J2bi3aZlWqk/img.jpg?width=1024&amp;amp;height=513&amp;amp;face=0_0_1024_513');&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;Enable Remote Desktop For Multiple Users on Windows 10/11&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A detailed how to guide about enabling remote desktop for multiple users on Windows 10. Find out how to setup multiple remote desktop connections with the help of RDP Wrapper or by modifying the registry.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.helpwire.app&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;Terminal Service Stop&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파워 Shell을 관리자 권한 실행&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXBVPe/btsqESaSuOy/XHrEYfMf0alcknT94KImb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXBVPe/btsqESaSuOy/XHrEYfMf0alcknT94KImb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXBVPe/btsqESaSuOy/XHrEYfMf0alcknT94KImb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXBVPe%2FbtsqESaSuOy%2FXHrEYfMf0alcknT94KImb1%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;700&quot; height=&quot;472&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TermService 정지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하기 3개의 Commands를 순차적으로 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;takeown&amp;nbsp;/F&amp;nbsp;.\termsrv.dll&amp;nbsp;/A&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;icacls&amp;nbsp;.\termsrv.dll&amp;nbsp;/grant&amp;nbsp;Administrators:F&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Net&amp;nbsp;stop&amp;nbsp;TermService&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 결과가 나오면 성공한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by5yqX/btsqBwAl5YY/w0XuWO15v8gqfK3yQ6mHL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by5yqX/btsqBwAl5YY/w0XuWO15v8gqfK3yQ6mHL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by5yqX/btsqBwAl5YY/w0XuWO15v8gqfK3yQ6mHL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby5yqX%2FbtsqBwAl5YY%2Fw0XuWO15v8gqfK3yQ6mHL0%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;1007&quot; height=&quot;413&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;413&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;h2 data-ke-size=&quot;size26&quot;&gt;HexEditor 설치 및 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dll 파일의 Hex를 직접 설치할 예정임으로 Hex Editor가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mh-nexus.de/en/downloads.php?product=HxD20&quot;&gt;https://mh-nexus.de/en/downloads.php?product=HxD20&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 HxD 실행 파일을 다운받고 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 다 완료 되었으면 아래와 같이 관리자 권한으로 실행해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRuqLv/btsqD5ar5xo/MNQIcDEVTZ7bhsq5Y35eGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRuqLv/btsqD5ar5xo/MNQIcDEVTZ7bhsq5Y35eGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRuqLv/btsqD5ar5xo/MNQIcDEVTZ7bhsq5Y35eGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRuqLv%2FbtsqD5ar5xo%2FMNQIcDEVTZ7bhsq5Y35eGK%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;1062&quot; height=&quot;398&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;398&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;h2 data-ke-size=&quot;size26&quot;&gt;termsrv.dll 파일 backup&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C드라이브 Windows 폴더 System32 디렉토리에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;termsrv.dll&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;아래 이미지는 termsrv.dll.bak으로 파일을 별도로 copy해 놓았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y1msD/btsqBv9itLV/GWdbgJLCXb5NkGGZv1fLFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y1msD/btsqBv9itLV/GWdbgJLCXb5NkGGZv1fLFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y1msD/btsqBv9itLV/GWdbgJLCXb5NkGGZv1fLFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy1msD%2FbtsqBv9itLV%2FGWdbgJLCXb5NkGGZv1fLFK%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;562&quot; height=&quot;281&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HxD로 termsrv.dll 파일 로드&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;133&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HVq27/btsqBGCR7T9/9hKK3wznMgSC3mYfSC7Di0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HVq27/btsqBGCR7T9/9hKK3wznMgSC3mYfSC7Di0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HVq27/btsqBGCR7T9/9hKK3wznMgSC3mYfSC7Di0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHVq27%2FbtsqBGCR7T9%2F9hKK3wznMgSC3mYfSC7Di0%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;133&quot; height=&quot;200&quot; data-origin-width=&quot;133&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 노란색 폴더 아이콘을 눌러서 &quot;C:\Windows\System32\termsrv.dll&quot; 파일을 로드한다.&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;span style=&quot;background-color: #f9f2f4; color: #c7254e; text-align: start;&quot;&gt;39 81 3C 06 00 00 0F 84 XX XX XX XX&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위의 hex문자열을 찾아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`ctl + F`&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;를 눌러서 아래와 같이 찾기 기능을 실행 시킨다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HCgbx/btsqD6ga9u5/aEH33q9khuR9jG9DlbDoxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HCgbx/btsqD6ga9u5/aEH33q9khuR9jG9DlbDoxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HCgbx/btsqD6ga9u5/aEH33q9khuR9jG9DlbDoxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHCgbx%2FbtsqD6ga9u5%2FaEH33q9khuR9jG9DlbDoxk%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;599&quot; height=&quot;432&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 16진수 하나만 찾기가 가능하다. (내가 모르는 것일 수도...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 가장 빈도가 낮게 나오는 3C를 조회하였다.&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;&lt;b&gt;모두검색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyu7Oy/btsqCp8siSh/z3KuUGwEpJszxkHZJmAgC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyu7Oy/btsqCp8siSh/z3KuUGwEpJszxkHZJmAgC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyu7Oy/btsqCp8siSh/z3KuUGwEpJszxkHZJmAgC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyu7Oy%2FbtsqCp8siSh%2Fz3KuUGwEpJszxkHZJmAgC0%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;1396&quot; height=&quot;660&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;681개의 검색결과가 나왔다. 여기에서 3C를 기준으로 위의 문자열을 찾아 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아래와 같이&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEUNwz/btsqERpGsnG/vGWuX3JNYLMMtKrXTAK4O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEUNwz/btsqERpGsnG/vGWuX3JNYLMMtKrXTAK4O1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEUNwz/btsqERpGsnG/vGWuX3JNYLMMtKrXTAK4O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEUNwz%2FbtsqERpGsnG%2FvGWuX3JNYLMMtKrXTAK4O1%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;941&quot; height=&quot;172&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0001E750 5번째 Offset에서 부터 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;위 이미지는 이미 변경된 상태이다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323; background-color: #ffc1c8;&quot;&gt;B8 00 01 00 00 89 81 38 06 00 00 90&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 숫자로 변경해 주자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;혹시라도 헷깔릴 수 있으니까 한번더 정리하면&lt;br /&gt;39 81 3C 06 00 00 0F 84 XX XX XX XX&lt;br /&gt;이렇게 되어있는 숫자를&amp;nbsp;&lt;br /&gt;B8 00 01 00 00 89 81 38 06 00 00 90&lt;br /&gt;이렇게 변경해 주면 된다.&lt;br /&gt;39는 B8로 81은 00 으로...&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;span style=&quot;background-color: #ffc1c8; color: #ee2323; text-align: start;&quot;&gt;06 00 00 90&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 사람마다 기존 값이 다르다. 그냥 강제로 수정하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;146&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pqC9A/btsqERDblYy/J7G2V5ckxq8oyuou93uaS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pqC9A/btsqERDblYy/J7G2V5ckxq8oyuou93uaS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pqC9A/btsqERDblYy/J7G2V5ckxq8oyuou93uaS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpqC9A%2FbtsqERDblYy%2FJ7G2V5ckxq8oyuou93uaS0%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;146&quot; height=&quot;131&quot; data-origin-width=&quot;146&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;b&gt;Net&amp;nbsp;start&amp;nbsp;TermService&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm3I4f/btsqCtiGdWN/hJKrMhmr852LLRkze0gUF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm3I4f/btsqCtiGdWN/hJKrMhmr852LLRkze0gUF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm3I4f/btsqCtiGdWN/hJKrMhmr852LLRkze0gUF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm3I4f%2FbtsqCtiGdWN%2FhJKrMhmr852LLRkze0gUF0%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;590&quot; height=&quot;91&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&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;만약에 문제가 있다면 기존에 backup해둔 파일로 기존 수정된 파일을 대체시켜 주자.&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;구지 RDP 깔기 귀찮은 사람은 이렇게 하는게 가장 깔끔할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Software활용</category>
      <category>RDP</category>
      <category>Windows</category>
      <category>원격데스크톱</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/271</guid>
      <comments>https://enumclass.tistory.com/271#entry271comment</comments>
      <pubDate>Wed, 9 Aug 2023 13:36:21 +0900</pubDate>
    </item>
    <item>
      <title>Shell Scripting 정리</title>
      <link>https://enumclass.tistory.com/270</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bash Shell Script를 사용할 일이 있어서 급하게 Review하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기나오는 내용은 educative Shell for programer와 Mastering Linux Shell Scripting에 있는 내용을 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/PacktPublishing/Mastering-Linux-Shell-Scripting-Second-Edition&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/PacktPublishing/Mastering-Linux-Shell-Scripting-Second-Edition&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기초&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Default Shell 찾기&lt;/h3&gt;
&lt;pre id=&quot;code_1688281232849&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~$ ps $$
    PID TTY      STAT   TIME COMMAND
  10374 pts/0    Ss     0:00 -bash
  
  $ echo $SHELL
/bin/bash&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shell 종류&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bash Shell : Bourne shell, 가장 기초 쉘&lt;/li&gt;
&lt;li&gt;Ksh shell : Korn shell, bash shell과 호환 됨&lt;/li&gt;
&lt;li&gt;Csh : C언어로 만들어진 쉘&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shell 변경하기&lt;/h3&gt;
&lt;pre id=&quot;code_1688281382583&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ chsh -s /bin/bash&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;Command Line 단축키&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;단축키&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;행동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Ctrl + C&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;현 실행 중인 Task를 종료한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Ctrl + L&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;화면을 clear 한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Ctrl + w&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;커서의 마지막 한문장을 삭제한다&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ aaa bbb ddd &lt;br /&gt;&lt;/span&gt;에서 bbb 앞에서 단축키를 입력하면 aaa가 사라진다&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ bbb ddd&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;tab&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;자동완성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Ctrl + R&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내가 앞서 사용했던 history에서 찾는 단어와 유사성이 있는 실행문을 찾아 준다&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;(reverse-i-search)`kaf': docker-compose -f docker-compose-kafka-triplet.yml down &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Ctrl + U&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&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;Variables Type&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Shell variables&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘이 정상적인 작동을 하게끔 사전 정의된 Local Variable이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘이 시작하면서 로드하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$shell_var&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;Local Variables&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 쉡 내부에서만 작동하는 Variable. 다른 쉘에 영향을 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 해당 쉘에서만 순간적으로 사용하게 하기 위해 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1688283504406&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ MY_VALUE=hello
$ echo $MY_VALUE
hello&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;h4 data-ke-size=&quot;size20&quot;&gt;Environment Variable&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 쉘에서 접근 가능한 varialbe이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bash 에서는 export 를 사용해서 정의하게 된다. 대표적으로 PATH 가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1688283697000&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cat .profile 

# if running bash
if [ -n &quot;$BASH_VERSION&quot; ]; then
    # include .bashrc if it exists
    if [ -f &quot;$HOME/.bashrc&quot; ]; then
        . &quot;$HOME/.bashrc&quot;
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d &quot;$HOME/bin&quot; ] ; then
    PATH=&quot;$HOME/bin:$PATH&quot;
fi

# set PATH so it includes user's private bin if it exists
if [ -d &quot;$HOME/.local/bin&quot; ] ; then
    PATH=&quot;$HOME/.local/bin:$PATH&quot;
fi&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;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$USER&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ echo $USER &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;theyoung &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$HOSTNAME&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ echo $HOSTNAME &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;theyoung-MS-7D77 &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$$&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ echo $$ &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;10374&lt;br /&gt;현 스크립트 ID&lt;/span&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$0&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;$ echo $0 &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;-bash &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;현 실행중인 스크립트의 이름&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$1~9&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;스크립트 실행시 입력된 아규먼트&lt;/span&gt;&lt;/div&gt;
&lt;/div&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;Variable setup 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1688283990592&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ VAR_NAME=HELLO
$ echo &quot;my name is $VAR_NAME !&quot;
my name is HELLO !
$ echo &quot;my name is ${VAR_NAME} !&quot;
my name is HELLO !&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 해야할 것은 Uppercase는 일반적으로 Environment Variable로 많이 사용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ 또는 ${} 를 이용해서 value를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Space가 필요하다면 &quot; &quot; 를 해줘야 한다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Shell Scripting&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hello world&lt;/h3&gt;
&lt;pre id=&quot;code_1688285597494&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo &quot;Hello World&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688285544298&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ vi hello.sh
/script$ chmod +x hello.sh 
/script$ ./hello.sh 
Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;#!/bin/bash : 쉬뱅, '#' 심볼 이후에 스크립트가 시작됨을 system에게 알려줌&lt;/li&gt;
&lt;li&gt;echo : standard 아웃풋&amp;nbsp;&lt;/li&gt;
&lt;li&gt;exit 0 : 종료, 0이외의 값은 오류를 뜻함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1688285824829&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ echo $?
0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 스크립트 실행의 결과가 0임을 나타냄&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;command1 || command2 : command1이 실패할 경우 command 2가 실행됨. 그외 실행 안됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1688285990287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo &quot;Hello New World&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 shell이 있을 경우&lt;/p&gt;
&lt;pre id=&quot;code_1688286018177&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./hello.sh || ./hello2.sh 
Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에 있는 쉘만 실행 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 앞쉘을 1로 바꾸면&lt;/p&gt;
&lt;pre id=&quot;code_1688286070026&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./hello.sh || ./hello2.sh 
Hello World
Hello New World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;command2가 실행 된것을 확인 할 수 있다.&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;Arguments&lt;/h3&gt;
&lt;pre id=&quot;code_1688286630998&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo &quot;Hello New World $1, count $#, all the arguments $*&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688286645508&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ ./hello2.sh ar1 ar2 ar3 ar4
Hello New World ar1, count 4, all the arguments ar1 ar2 ar3 ar4&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$숫자 : 아규먼트 값&lt;/li&gt;
&lt;li&gt;$# : 아규먼트 카운트&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;h3 data-ke-size=&quot;size23&quot;&gt;Defined Variables&lt;/h3&gt;
&lt;pre id=&quot;code_1688286983281&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
name=&quot;superman&quot;
job=&quot;hero&quot;
echo &quot;Hello New World $1, count $#, all the arguments $*&quot;
echo &quot;name ${name}, job ${job}&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688287001115&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./hello2.sh 
Hello New World , count 0, all the arguments 
name superman, job hero&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;space를 중간에 넣지 말것!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) name = &quot;superman&quot;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;List Variables&lt;/h4&gt;
&lt;pre id=&quot;code_1688287228873&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
name=&quot;superman&quot;
job=&quot;hero&quot;
list=(1 2 3 4 5)
echo &quot;Hello New World $1, count $#, all the arguments $*&quot;
echo &quot;name ${name}, job ${job}&quot;
echo &quot;${list[*]}&quot;
unset list[0]
echo &quot;${list[*]}&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688287244082&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./hello2.sh 
Hello New World , count 0, all the arguments 
name superman, job hero
1 2 3 4 5
2 3 4 5&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unset은 리스트의 특정 index를 삭제함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Environment variables&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;printenv로 환경변수 값을 확인할 수 있음&lt;/p&gt;
&lt;pre id=&quot;code_1688287324102&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ printenv
SHELL=/bin/bash
LANGUAGE=ko:en
[omitted]
SSH_TTY=/dev/pts/2
LC_NUMERIC=ko_KR.UTF-8
_=/usr/bin/printenv
OLDPWD=/home/theyoung&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;export (변수 scope)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 스크립트간 로컬 variable은 공유되지 않음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export활용해서 공유 가능함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 실행 스크립트내에서 다른 스크립트를 호출한 경우에 한함&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참고로 hello2.sh에서 name정의 부분은 삭제 하였다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1688287639980&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo &quot;Hello World&quot;
name=&quot;spider man&quot;
export name
./hello2.sh&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688287656648&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./hello.sh 
Hello World
Hello New World , count 0, all the arguments 
name spider man, job hero
1 2 3 4 5
2 3 4 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 혹시라도 hello2.sh에 name 정의 부분을 안 삭제 한다고 해도 원래 hello.sh의 name은 해당 프로세스 내에서 수정되지는 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;bash command 실행&lt;/h3&gt;
&lt;pre id=&quot;code_1688287981296&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
cur_dir=`pwd`
echo $cur_dir

iam=$(whoami)
echo $iam&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 내에서 command를 실행하는 방법은 2가지가 있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;` 백틱&lt;/li&gt;
&lt;li&gt;$() : 중괄호&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1688288041310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ ./command.sh 
/script
theyoung&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;Debugmode script 실행&lt;/h3&gt;
&lt;pre id=&quot;code_1688288161242&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ bash -x ./command.sh
++ pwd
+ cur_dir=/home/theyoung/script
+ echo /home/theyoung/script
/home/theyoung/script
++ whoami
+ iam=theyoung
+ echo theyoung
theyoung&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;사용자 입력 read&lt;/h3&gt;
&lt;pre id=&quot;code_1688288374982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo -n &quot;Your name : &quot;
read
echo &quot;My name is $REPLY&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688288387661&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./read.sh 
Your name : steven 
My name is steven&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$REPLY 를 통해서 입력 결과를 표시함&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;command line 을 통한 입력&lt;/h4&gt;
&lt;pre id=&quot;code_1688288513463&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ read -p &quot;Your name is &quot; name
Your name is steven
/script$ echo $name
steven&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read 명령어를 통해서 name 변수에 이름이 입력 된것을 확인 가능함&lt;/p&gt;
&lt;pre id=&quot;code_1688288618812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo -n &quot;Your name : &quot;
read
echo &quot;My name is $REPLY&quot;

read -p &quot;Your ages : &quot; age 
echo &quot;My ages is $age&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688288633190&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ ./read.sh 
Your name : steven
My name is steven
Your ages : 43
My ages is 43&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;read -n1 을 아규먼트로 넣을 경우 1회의 키만 받는 다는 의미임&lt;br /&gt;read -sn1 에 s를 넣을 경우 입력값이 보이지 않게 됨&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;while-case&lt;/h3&gt;
&lt;pre id=&quot;code_1688289074509&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
while [ -n &quot;$1&quot; ]
do
    case &quot;$1&quot; in
    -f) echo &quot;--first option selected&quot;;;
    -s) echo &quot;--second option selected&quot;;;
    -t) echo &quot;--third option selected&quot;;;
    *) echo &quot;no option selected&quot;;;
    esac
shift
done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while [ 비교문 ]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;do&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;done&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;비교문에서 &quot;-n&quot;은 not empty string이라는 의미이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;case 비교대상 in&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esac&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;in 아래에 비교 대상을 직접적으로 )로 표시 하면 된다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;shift는 arguments를 하나씩 좌측으로 shipt해서 pop하게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;for 순환&lt;/h3&gt;
&lt;pre id=&quot;code_1688289709343&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
num=1
for param in $@
do
    echo &quot;#$num: $param&quot;

    num=$(( $num + 1 ))
done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for variable in range&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;do&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;done&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 이루어짐 여기서 $@는 모든 array라는 의미로 foreach 형태로 하나씩 space단위로 값을 빼어냄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;num=은 variable을 재정의 하는 형태이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$()를 통해서 command를 실행 시킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 내부에 ()가 하나더 있는데 'Numeric calculations'이라고 함&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688289851666&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./for.sh a b c d e f g
#1: a
#2: b
#3: c
#4: d
#5: e
#6: f
#7: g&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;Condition Test&lt;/h4&gt;
&lt;pre id=&quot;code_1688291110484&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ test $PWD == $HOME
/script$ echo $?
1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test를 통해서 현재 있는 위치와 HOME의 path가 같은지를 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0이외의 결과는 error임으로 위는 error를 뜻한다&lt;/p&gt;
&lt;pre id=&quot;code_1688291951676&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
echo &quot;Your basename is $(basename $0)&quot;
test -z $1 || echo &quot;Hello $1&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test -z $1은 $1이 empty string일때 true가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 아무런 파라메터도 없으면 hello는 나오지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1688292018131&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./test.sh
Your basename is test.sh
~/script$ ./test.sh a
Your basename is test.sh
Hello a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test는 다음과 같이 활용 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test expression : expression이 참이냐 false냐&lt;/li&gt;
&lt;li&gt;test ! expression : 결과 반대&lt;/li&gt;
&lt;li&gt;test expression -a expression : and&lt;/li&gt;
&lt;li&gt;test expression -o expression : or&lt;/li&gt;
&lt;li&gt;test -n string : string에 값이 있는가?&lt;/li&gt;
&lt;li&gt;test -z string : string에 값이 없는가?&lt;/li&gt;
&lt;li&gt;test string = string : 두 string은 같은가?&lt;/li&gt;
&lt;li&gt;test 3 -gt 0 : 3은 0보다 큰가?&lt;/li&gt;
&lt;li&gt;test 3 -lt 0 : 3은 0보다 작은가?&lt;/li&gt;
&lt;li&gt;test 3 -eq 0 : 3은 0과 같은가?&lt;/li&gt;
&lt;li&gt;test 3 -nq 0 : 3은 0과 같지 않은가&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;String을 shell script내에서 test condition으로 사용할 경우 반듯이 &quot;&quot;를 해야한다&lt;br /&gt;if [ &quot;mother&quot; = &quot;Mother&quot; ]&lt;/blockquote&gt;
&lt;pre id=&quot;code_1688292179615&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ test $USER = root
~/script$ echo $?
1

~/script$ test ! $USER = root
~/script$ echo $?
0

~/script$ test -n iam
~/script$ echo $?
0

~/script$ test -z iam
~/script$ echo $?
1

~/script$ test 3 -gt 0
~/script$ echo $?
0

~/script$ test 3 -lt 0
~/script$ echo $?
1

~/script$ test 3 -eq 0
~/script$ echo $?
1&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test -d 디렉토리 : 디렉토리 인가?&lt;/li&gt;
&lt;li&gt;test -x 실행파일 : 실행가능한가?&lt;/li&gt;
&lt;li&gt;test -f 파일 : 파일이 맞는가?&lt;/li&gt;
&lt;li&gt;test -r 파일 : 읽을 수 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1688292674608&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ test -d script/
~$ echo $?
0
~$ test -x script/
~$ echo $?
0
~$ test -f script/
~$ echo $?
1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합 컨디션&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test a = b || test b = b : a와b의 string이 같거나 b와 b의 스트링이 같으면&amp;nbsp; true이다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1688293485000&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ test a = b || test b = c
~/script$ echo $?
1
~/script$ test a = b || test b = b
~/script$ echo $?
0&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;h3 data-ke-size=&quot;size23&quot;&gt;if condition&lt;/h3&gt;
&lt;pre id=&quot;code_1688292917493&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
if [ $# -lt 1 ];then
    echo &quot;no param&quot;
fi
echo &quot;all params : $*&quot;
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력된 파라메터가 1보다 작은지 확인하는 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 컨디션 ; then&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 구성된다&lt;/p&gt;
&lt;pre id=&quot;code_1688292969435&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./if.sh a b c
all params : a b c
~/script$ ./if.sh
no param
all params :&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;else를 추가할 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;if 컨디션 ; then&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;else&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;if&lt;/p&gt;
&lt;pre id=&quot;code_1688293120110&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
if [ $# -lt 1 ];then
    echo &quot;no param&quot;
else
    echo &quot;all params : $*&quot;
fi
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688293140166&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./if.sh
no param
~/script$ ./if.sh abc
all params : abc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;복합 컨디션은 아래와 같다&lt;/p&gt;
&lt;pre id=&quot;code_1688293623941&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
if [ $# -lt 1 ] || [ -n &quot;abc&quot; ];then
    echo &quot;no param&quot;
else
    echo &quot;all params : $*&quot;
fi
exit 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위의 경우는 무조건 no param이 나오게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;-n &quot;abc&quot;는 항상 참이기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293671177&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ vi if.sh 
~/script$ ./if.sh a b c d
no param&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;h2 data-ke-size=&quot;size26&quot;&gt;Script example&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ping&lt;/h3&gt;
&lt;pre id=&quot;code_1688290685327&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
read -p &quot;server address : &quot; serveraddr
ping -c 3 $serveraddr 2&amp;gt;1 &amp;gt; /dev/null || echo &quot;Server Dead&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ping에서 -c는 retry의 횟수이다. 2&amp;gt;1은 standard 아웃풋을 모두 /dev/null로 보냄으로써 화면에 표시하지 않겠다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 ping의 결과가 0이 아닌 결과로 error로 판단되면 Server Dead가 표시된다.&lt;/p&gt;
&lt;pre id=&quot;code_1688290747442&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/script$ ./ping.sh 
server address : 1.2.3.4
Server Dead&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;/h3&gt;
&lt;pre id=&quot;code_1688290918461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
while read line
do
    echo $line
done&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688290934543&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/script$ ./readfile.sh &amp;lt; ping.sh 
#!/bin/bash
read -p &quot;server address : &quot; serveraddr
ping -c 3 $serveraddr 2&amp;gt;1 &amp;gt; /dev/null || echo &quot;Server Dead&quot;&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;&amp;nbsp;&lt;/h4&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;</description>
      <category>Software활용</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/270</guid>
      <comments>https://enumclass.tistory.com/270#entry270comment</comments>
      <pubDate>Sun, 2 Jul 2023 19:28:03 +0900</pubDate>
    </item>
    <item>
      <title>COS Pro 1급 Java 만점자 후기 및 공략</title>
      <link>https://enumclass.tistory.com/269</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 COS Pro 시험 응시를 장려하는 분위기여서, 솔선수범 하자는 마음으로 시험을 응시하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운이 좋았는지 1,000점 만점을 받았다. 시간이 지나면 기억에서 지워질 것 같아서 글로 남기고자 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uVuaL/btrRFgqdzLv/X3ri9ZkcHlNPHRGeVKuSc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uVuaL/btrRFgqdzLv/X3ri9ZkcHlNPHRGeVKuSc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uVuaL/btrRFgqdzLv/X3ri9ZkcHlNPHRGeVKuSc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuVuaL%2FbtrRFgqdzLv%2FX3ri9ZkcHlNPHRGeVKuSc0%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;1560&quot; height=&quot;262&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(사실 조회수도 좀 나올것같아서....)&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;프로그래머스 스타일로 10문제가 제출 된다. 기출 문제가 6회 공개 되어있어서 나는 구름을 이용해서 준비했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1시간 30분씩 타이머를 켜놓고 5개의 기출문제를 풀었다. 본 시험을 위해 약 7시간 30분 투자한것 같다.&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;600점 이상 획득하면 합격이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://edu.goorm.io/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://edu.goorm.io/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C, C++, Java, Python이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로는 Python을 이용해서 시험을 많이 준비한다. Array 처리나 Built-in Method가 강력한게 많아서 Java 처럼 불필요한 Class나 Method를 암기할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개발을 처음 입문하는 지원자는 Python을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 기초를 어느정도 공부했고, 잘 사용가능한 수준이 되었는지 테스트 해보고 싶다면 Java로 도전을 해도 되지만, 다른 언어에 비해서 패널티가 있다고 생각한다. 시험용으로 Java 언어 선택은 신중하게 하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java에서 암기해야할 내용은?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험장에서 제공하는 IDE는 자동완성 기능이 없다. Java docs도 제공해주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 항목은 암기를 하고 준비를 하는게 좋다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배열&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &lt;a href=&quot;https://www.w3schools.com/java/java_arrays.asp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.w3schools.com/java/java_arrays.asp&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cos에서는 int[], String[], char[] 정도면 될 듯하다. 조금더 깊게가면 2D Array까지는 공부하는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int[][], String[][], char[][]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrays Class와 함께 배열을 잘 다루면 편리한 부분이 많다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrays.sort() &lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#sort(byte[])&quot;&gt;https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#sort(byte[])&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrays.toString() : &lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#toString(boolean[])&quot;&gt;https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#toString(boolean[])&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에 deeToString이나 copyOfRange, fill 등의 유용한 기능들이 많이 있는데, 현재 기준으로는 사용하지 않아도 문제를 푸는데는 지장이 없는 것 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Collections&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List, Deque(Queue), Set, Map이 있는데, 이중에서 Implementations Class로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList, LinkedList(stack, queue 용도로 사용), HashSet, HashMap 의 사용법을 암기해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add, put, set, remove, get, peek, addFirst, addLast, peek, peekFirst, peekLast와 같이 자주 사용되는 Method는 꼭 암기하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TreeSet이나 TreeMap, LinkedHashSet, LinkedHashMap을 활용하는 정도의 문제 수준은 안나오는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 꼭 Comparator를 통한 sorting처리도 암기 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Comparator.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Comparator.html&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;String&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html&lt;/a&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String은 정말 완전히 이해하고 암기가 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 많이 사용되는 Method는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;charAt, compareTo, concat, contains, repeat, replace, replaceAll, split, startsWith, trim, toCharArray, toLowerCase, toString, toUpperCase, valueOf, substring&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 indexOf 같은 메서드도 암기하면 좋지만 준비하면서 본 method를 쓸일은 없었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기타&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그외에 Integer같은 Class들도 잘 사용 해야 하는것이 당연하겠지만, 준비하면서 valueOf말고 다른 용도로 딱히 써본적은 없는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.out.println 같은것은 너무 당연한거라 별도 언급하지 않겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;알고리즘 난이도는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 후기들에서도 많이 언급하고 있지만 전반적으로 easy 수준이다. 하지만 문제를 풀다보면 medium수준 질문도 확실히 존재한다. 그렇다고 해서 일부러 어려운 문제를 준비할 필요는 없을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답안을 테스트하는 test cases가 큰것 같지 않고, 최적화에 대한 채점은 거의 없지 않나 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면&amp;nbsp;기출 3차 전광판 문구 출력 문제를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://edu.goorm.io/learn/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java/lesson/839419/3%EC%B0%A8-%EB%AC%B8%EC%A0%9C5-%EC%A0%84%EA%B4%91%ED%8C%90-%EB%AC%B8%EA%B5%AC-%EC%B6%9C%EB%A0%A5-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://edu.goorm.io/learn/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java/lesson/839419/3%EC%B0%A8-%EB%AC%B8%EC%A0%9C5-%EC%A0%84%EA%B4%91%ED%8C%90-%EB%AC%B8%EA%B5%AC-%EC%B6%9C%EB%A0%A5-java&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;______&quot; 이렇게 생긴 전광판에 &quot;_____a&quot;, &quot;____ab&quot;, &quot;___abc&quot; 이런식으로 1초씩 문자열이 자리를 이동하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈에 보이는 데로 프로그램을 짠다면 &quot;________abcdefgh&quot; 이렇게 문자열을 만들고 앞에서 부터 한자리씩 자르기로 처리하는 방식이 가장 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예에서 &quot;[&quot; , &quot;]&quot;이 사이가 전광판이라고 생각해 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;[_______a]bcdefgh&lt;span style=&quot;color: #ee2323;&quot;&gt;_&lt;/span&gt;&quot;, &quot;[______ab]cdefgh&lt;span style=&quot;color: #ee2323;&quot;&gt;__&lt;/span&gt;&quot;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&quot;[_____abc]defgh&lt;span style=&quot;color: #ee2323;&quot;&gt;___&lt;/span&gt;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한자리씩 모든 초를 for문을 돌려서 실제로 동작 시키면 된다. 하지만 이는 최적화 되지 않았다. 모든 경우를 실제로 돌리니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 시간복잡도로 O(N)이 된다. 하지만 조금더 생각해 보면 O(1)로 해당 문제를 풀 수 있다. (이건 다음에 문제 해설 작성하게 되면 ...올려보겠다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 본 시험은 O(N) 정도의 답안으로 Pass가 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하고 싶은 말은 최적화 따위 일단 접어두자, 시간이 남는다면 모르겠지만 brute force로 괜찮다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 유형은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 유형은&amp;nbsp; &quot;디버깅&quot;, &quot;설계&quot;, &quot;코드이해&quot;로 나누어진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용을 보고 점수의 분포와 유형을 생각해 보자. 참고로 배점은 문제의 난이도에 따라서 변경이 될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;1326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFILrG/btrRJQxAxVS/lLnJm8ZHCuZfJPDp6d9qik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFILrG/btrRJQxAxVS/lLnJm8ZHCuZfJPDp6d9qik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFILrG/btrRJQxAxVS/lLnJm8ZHCuZfJPDp6d9qik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFILrG%2FbtrRJQxAxVS%2FlLnJm8ZHCuZfJPDp6d9qik%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;398&quot; height=&quot;373&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;1326&quot;/&gt;&lt;/span&gt;&lt;/figure&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;합격을 위해서는 4문제 모두 맞추는 것이 좋다. 420점,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 180점만 더하면 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한줄 바꾸기 문제인 디버깅 3문제를 다 맞추면 200점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드이해와 디버깅 문제를 다 풀기만 하면 6백 20점으로 통과가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아쉽게도 이곳에서 실수가 많이 나오는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 시험장 IDE에서 다른 Test Case를 테스트 해볼 방법을 제공하지도 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;설계 문제인 알고리즘 문제 하나는 최소한 무조건 푼다고 생각&lt;/b&gt;하고 접근해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제별 전략&lt;/h2&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;1차 문제3번 계산기 by문자열 화면을 보자.&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;716&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4yU61%2FbtrRALSEbOJ%2FdVD2aQTKhQYB5Mobggtxv0%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;420&quot; height=&quot;169&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&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;다만 구름과 다른점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 빈칸외에는 아무것도 할 수 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 System.out.println도 사용해 볼 수 없다. 오로지 머리로만 계산해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제를 보면 2가지 유형이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만들어져 있는 Method를 넣는 유형 : 예를 들자면 위 문제는 func_a, func_b, func_c가 어디에 들어갈지 찾는 유형이다.&lt;/li&gt;
&lt;li&gt;Method 내부에 코드를 채워 넣는 유형 : Method내부에 로직을 완성시키는 유형이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj5b1C/btrRACOW0dQ/LElaBeKucEzU0k3JYpQvRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj5b1C/btrRACOW0dQ/LElaBeKucEzU0k3JYpQvRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj5b1C/btrRACOW0dQ/LElaBeKucEzU0k3JYpQvRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj5b1C%2FbtrRACOW0dQ%2FLElaBeKucEzU0k3JYpQvRK%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;407&quot; height=&quot;212&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보면 @@@ &amp;lt;- 이게 빈칸 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 유형의 경우는 질문을 이해하지 않아도 풀수 있는 유형이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4yU61/btrRALSEbOJ/dVD2aQTKhQYB5Mobggtxv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4yU61%2FbtrRALSEbOJ%2FdVD2aQTKhQYB5Mobggtxv0%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;420&quot; height=&quot;169&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제 같은 경우 func_a의 return 은 int이고 func_c의 return 은 Pair이다. 위에서 두번째 줄에 있는 Pair 리턴이 가능한 것은 func_c밖에 없다. func_a와 func_c는 둘다 int return이 가능하지만 int parameter가 3개가 있는 Method는 func_a가 유일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 return 타입과 파라메터만으로 로직을 추측가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/04VOJ/btrRDUVlSo3/mTugs4oPOU6KvKiYFVeKG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/04VOJ/btrRDUVlSo3/mTugs4oPOU6KvKiYFVeKG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/04VOJ/btrRDUVlSo3/mTugs4oPOU6KvKiYFVeKG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F04VOJ%2FbtrRDUVlSo3%2FmTugs4oPOU6KvKiYFVeKG1%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;549&quot; height=&quot;99&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그래서 시험에 들어가서 문제가 길고 이해가 어렵울것 같다면 일단 패스하고 다른 문제를 먼저 푸는 것이 좋다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;최종 제출까지 문제는 이동할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기출 문제를 풀면서 코드이해 문제를 오해한것이 있는데, 실제로 시험장에서 나오는 문제는 위의 2가지 케이스가 분리되어서 나오지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두개가 같이 섞여서 나온다. 결론적으로는 문제 자체를 이해하는데 시간을 소요해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 Tip이 중요한데, 빈칸채우기는 로그를 찍어볼 수 없어서 내가 어떤 부분을 잘못했는지 확인하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 시간이 좀 남는다면 빈칸채우기에 있는 부분적 코드를 설계 문제로 copy 해와서 로깅을 찍어 볼수 있다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;오해가 있을 수 있는데, 빈칸 외의 곳은 복사가 되지 않는다. 나는 빈칸에 작성한 내 코드가 정상적으로 작동하지 않는것 같아서, 빈칸에 내가 작성한 코드를 copy해서 설계하기 코드에 붙여넣고 로그를 찍어서 잘못된 점을 찾았다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기출문제와 다른점은 한줄에 긴 코드가 들어갈 여지가 있다는 것이다. 한마디로 기출보다는 어려웠다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;1098&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/calmNr/btrRFfdRaMN/eXMM40VzeIxU3DJt3ydfCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/calmNr/btrRFfdRaMN/eXMM40VzeIxU3DJt3ydfCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/calmNr/btrRFfdRaMN/eXMM40VzeIxU3DJt3ydfCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcalmNr%2FbtrRFfdRaMN%2FeXMM40VzeIxU3DJt3ydfCK%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;325&quot; height=&quot;453&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;1098&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 한줄을 수정하라는 가이드는 프로그램상에 별도로 있지 않는다. 그래서 solution 메서드 내부에 마음데로 여러줄 수정해 보고 로그도 찍어볼 수 있다.(있었던거 같다. 하루밖에 지나지 않는데... 가물가물 하네)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이문제를 접근할 때는 문제를 보자마자 &quot;실행&quot;을 눌러주자. 실행 결과는 2가지로 나올 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일 Exception or Out of Index&lt;/li&gt;
&lt;li&gt;틀린 해답&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째와 같은 경우는 어디를 고쳐야 할지 감을 잡을 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 아래는 6차 문제4 카드섞기 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://edu.goorm.io/learn/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java/lesson/839448/6%EC%B0%A8-%EB%AC%B8%EC%A0%9C4-%EC%B9%B4%EB%93%9C-%EC%84%9E%EA%B8%B0-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://edu.goorm.io/learn/lecture/17301/cos-pro-1%EA%B8%89-%EA%B8%B0%EC%B6%9C%EB%AC%B8%EC%A0%9C-java/lesson/839448/6%EC%B0%A8-%EB%AC%B8%EC%A0%9C4-%EC%B9%B4%EB%93%9C-%EC%84%9E%EA%B8%B0-java&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2BjcH/btrRBadtxQc/WeeoXIcvS2GQhIuotqQmCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2BjcH/btrRBadtxQc/WeeoXIcvS2GQhIuotqQmCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2BjcH/btrRBadtxQc/WeeoXIcvS2GQhIuotqQmCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2BjcH%2FbtrRBadtxQc%2FWeeoXIcvS2GQhIuotqQmCk%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;483&quot; height=&quot;255&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 문제를 그대로 실행하게 되면 out of bounds Exception이 나는 것을 볼수 있다. 그것도 17번 줄에서 오류가 나는 것을 알 수 있다. 이를 통해서 17번 주변이 수정 되상이 될 것이라고 미리 예감 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 경우는 어쩔 수 없이 문제를 다 읽어 보긴 해야하는데, 추측이 가능한 경우가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 Run length 알고리즘을 만든다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;aaaabbc&quot; 이 문자열의 답은 &quot;a4b2c1&quot;이다 그런데 실행한 결과를 보니 &quot;a1a1a1a1b1b1c1&quot;로 나온다면 n번째 char와 n+1번째 char를 비교하는 부분이 잘못 작성 되었을 수 있다고 예감할 수 있다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제이다. 왜 알고리즘 문제를 설계라고 명명했는지는 잘모르겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 한번 언급한것 처럼 알고리즘 문제는 brute force로 생각하고 풀자.&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;import java.util.*;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 암기하고 하자. Collection 사용이 필요할 수도 있다.&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;UML&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UML을 따라서 그리는 문제도 하나 있는데,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7UJRN/btrRDVmrhPZ/xDj3fSdxkWtuRFKRbQsI9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7UJRN/btrRDVmrhPZ/xDj3fSdxkWtuRFKRbQsI9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7UJRN/btrRDVmrhPZ/xDj3fSdxkWtuRFKRbQsI9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7UJRN%2FbtrRDVmrhPZ%2FxDj3fSdxkWtuRFKRbQsI9K%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;303&quot; height=&quot;259&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&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;extends, implements 등은 암기가 필요하고, 기억나기로는 코드 내부 로직에 대한 부분도 빈칸으로 문제가 제출되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;나는 자신있는 모든 문제를 60분내에 푸는 것을 목표로 했다. 그래서 matrix나 문제의 지문이 길거나 이해하기 어려운 문제는 바로 풀지 않고 skip했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 60이내로 풀수 있는 문제를 모두 풀고, 안푼 문제중에 지문 이해가 가장 쉬운 문제부터 순차적으로 풀었던것 같다.&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;누군가에게 도움이 되길 바란다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>COSPro</category>
      <category>Java</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/269</guid>
      <comments>https://enumclass.tistory.com/269#entry269comment</comments>
      <pubDate>Mon, 21 Nov 2022 00:41:24 +0900</pubDate>
    </item>
    <item>
      <title>Byte hex 출력하기 / (bytes[i] &amp;amp; 0xff) + 0x100 가 뭐지?</title>
      <link>https://enumclass.tistory.com/268</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Byte를 hex 문자로 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화(Hashing) 코드를 보다가&lt;/p&gt;
&lt;pre id=&quot;code_1661677528377&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static String get_SecurePassword(String passwordToHash, byte[] salt){
  String generatedPassword = null;
  try {
   MessageDigest md = MessageDigest.getInstance(&quot;SHA-256&quot;);
   md.update(salt);
   byte[] bytes = md.digest(passwordToHash.getBytes());
   StringBuilder sb = new StringBuilder();
   for(int i=0; i&amp;lt; bytes.length ;i++)
   {
    sb.append(Integer.toString((bytes[i] &amp;amp; 0xff) + 0x100, 16).substring(1));
   }
   generatedPassword = sb.toString();
  } 
  catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  }
  return generatedPassword;
 }&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;pre id=&quot;code_1661654661590&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    sb.append(Integer.toString((bytes[i] &amp;amp; 0xff) + 0x100, 16).substring(1));&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;출력하고자 하는 문자 범위는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0x00에서 0xff 까지의 8bit로 이루어진 값을 필요로 한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Byte의 범위는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Byte코드는 127 ~ -128의 범위를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 8bit의 메모리를 갖기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ 2^7 $ ~ $ 2^7-1 $ 의 범위이다. 참고로 양수쪽의 -1은 0을 포함해야 하기 때문이다.&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;bytes의 갯수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SHA-256은 256bit의 hashing값을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661677875237&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        MessageDigest md = MessageDigest.getInstance(&quot;SHA-256&quot;);

        byte[] bytes = md.digest(&quot;abc&quot;.getBytes());

        System.out.println(bytes.length * 8);
        
        출력 : 256&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 SHA-1은 160bit, MD5는 126bit이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661677957645&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        MessageDigest md = MessageDigest.getInstance(&quot;SHA-1&quot;);

        byte[] bytes = md.digest(&quot;abc&quot;.getBytes());

        System.out.println(bytes.length * 8);
        
        출력 : 160&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1661677981371&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        MessageDigest md = MessageDigest.getInstance(&quot;MD5&quot;);

        byte[] bytes = md.digest(&quot;abc&quot;.getBytes());

        System.out.println(bytes.length * 8);
        
        출력:128&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이트를 기준으로 함으로 SHA-256은 32Bytes이다.&lt;/p&gt;
&lt;pre id=&quot;code_1661678038294&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;   for(int i=0; i&amp;lt; bytes.length ;i++)
   {
    sb.append(Integer.toString((bytes[i] &amp;amp; 0xff) + 0x100, 16).substring(1));
   }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 32회를 순회하면서 한 바이트에 들어있는 8bit값을 16진수로 표현하면 된다.&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;(bytes[i]&amp;nbsp;&amp;amp;&amp;nbsp;0xff)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0000 0000 에서 부터 1111 1111의 값을 표현해야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integer.toString 처리 시 4byte의 integer type으로 최종 casting되게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661678343306&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static String toString(int i, int radix) {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 input 파라메터가 int로 캐스팅 되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 byte[i]가 음수일때 문제가 발생한다.&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;b&gt;마이너스 1을 구하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1의 비트 : 0000 0000 0001&lt;/li&gt;
&lt;li&gt;보수 : 1111 1111 1110&lt;/li&gt;
&lt;li&gt;+1 : 1111 1111 1111&lt;/li&gt;
&lt;li&gt;-1의 비트 : 1111 1111 1111&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법을 따르면 최종적으로 음수는 좌측이 모두 1로 채워지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4byte integer의 -1은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1111 1111 1111 1111 1111 1111 1111 1111&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 음수가 불필요 함으로 8bit이후의 모든 값은 양수화 시켜줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러기 위해서 &quot;&amp;amp; 0xff&quot; 가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;00000000 0000 0000 0000 0000 1111 1111&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-1의 byte값이 최종적으로 위와 같이 변화 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 toBinaryString을 처리하면 &quot;ff&quot; 라는 값이 나온다.&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;그럼 0은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위와 같이 처리하면 한가지 문제점이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한자리 숫자 1의 경우는 01 이런식으로 2자리 출력을 해야 하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 1임으로 1로만 출력이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661678838780&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.println(Integer.toBinaryString(1 &amp;amp; 0xff));

출력 : 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 필요 한것은 2자리 string값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &quot;01&quot;이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 경우에도 2자리 이상 출력이 가능하게 할 수 있도록 &quot;0x100&quot;을 더해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1이라는 값을 기준으로 0x100을 더해주면 9비트에 1을 더해주는 것과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 : 00000000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000 0000 0000 0001&lt;/li&gt;
&lt;li&gt;0x100 더함 : 00000000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000&lt;span&gt;&amp;nbsp;&lt;/span&gt;0000 0001 0000 0001&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661679208586&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.println(Integer.toString((1 &amp;amp; 0xff) + 0x100, 16));

출력 : 101&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 나오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기서의 출력은 무조건 3자리의 String이 나오게 됨으로써, 우측 2자리 값만 취하면 목표한 결과가 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1661679305541&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.println(Integer.toString((1 &amp;amp; 0xff) + 0x100, 16).substring(1));

출력 : 01&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;/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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661679341398&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        String value = String.format(&quot;%02x&quot;, 1);
        System.out.println(value);
        
        출력 : 01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 String.format을 이용하는 방법이 가장 쉬운데, performance 측면으로 String format이 더 느림으로 위와 같이 bit manipulation을 처리 한다.&lt;/p&gt;</description>
      <category>JAVA</category>
      <category>Bit</category>
      <category>byte</category>
      <category>hashing</category>
      <category>암호화</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/268</guid>
      <comments>https://enumclass.tistory.com/268#entry268comment</comments>
      <pubDate>Sun, 28 Aug 2022 18:38:28 +0900</pubDate>
    </item>
    <item>
      <title>mac에서 java version 변경하기</title>
      <link>https://enumclass.tistory.com/267</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mac os(테스트 버전 12.5)에서 java의 버전을 쉽게 변경하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java로 이런저런 테스트를 하다가 보니 java 최신 버전(19)에서 작동 안하는 코드들이 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 mac의 java버전을 쉽게 변경하는 방법을 찾아 보았는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://coderwall.com/p/bajddw/awesome-script-for-changing-java-versions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://coderwall.com/p/bajddw/awesome-script-for-changing-java-versions&lt;/a&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;다만 2020년도에 쓰여진거라 그런지 몇가지 수정이 필요했다.&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;그래서 몇가지 수정한 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1661125177005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

alias java_ls='/usr/libexec/java_home -V 2&amp;gt;&amp;amp;1 | grep -E &quot;\d.\d.\d&quot; | grep -e &quot;Home$&quot; | colrm 1 4 | cut -d \  -f 1'

function java_use() {
    export JAVA_HOME=$(/usr/libexec/java_home -v $1)
    export PATH=$JAVA_HOME/bin:$PATH
    java -version
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 ~/.profile에 넣고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661125221355&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source ~/.profile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 하면 아래와 같이 2가지 command를 사용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;java_ls&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 컴퓨터에 인스톨되어 있는 자바의 버전을 보여준다.&lt;/p&gt;
&lt;pre id=&quot;code_1661125256140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% java_ls
17.0.3
11.0.15.1
11.0.13
1.8.333.02
1.8.0_311&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;java_use&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 리스트 중에서 사용하고자 하는 자바 버전을 선택한다.&lt;/p&gt;
&lt;pre id=&quot;code_1661125318090&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; % java_use 11.0.13
java version &quot;11.0.13&quot; 2021-10-19 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.13+10-LTS-370)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.13+10-LTS-370, mixed mode)&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;java --version&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java --version을 통해서 현 자바의 버전이 변경된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1661125396974&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% java --version
java 11.0.13 2021-10-19 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.13+10-LTS-370)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.13+10-LTS-370, mixed mode)&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 .profile은 mac에서 로드를 하지 않는다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 맥은 .bash가 아닌 .zsh을 기본 지원하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/264&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.07.26 - [Software활용] - Mac에서 zsh 또는 bash shell을 사용하기&lt;/a&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;아무튼 zsh에서 기본 로드하게 하기 위하여 .zprofile을 사용함으로써 user의 기본 설정 파일을 로드하게 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1661125556111&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% ln -s ~/.profile ~/.zprofile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭링크를 통해서 zsh에서 로드하는 .zprofile을 생성해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 .zprofile을 만들어도 실행이 되지 않는다면 $HOME에 다음 파일이 있는지 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.zshenv&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에서 .zprofile을 로드하도록 해주자.&lt;/p&gt;
&lt;pre id=&quot;code_1661125708911&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;. &quot;$HOME/.zprofile&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정해 주면, 터미널을 로드 할 때 .zprofile을 같이 로드하게 된다.&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;a href=&quot;https://stackoverflow.com/questions/18773051/how-to-make-os-x-to-read-bash-profile-not-profile-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/18773051/how-to-make-os-x-to-read-bash-profile-not-profile-file&lt;/a&gt;&lt;/p&gt;</description>
      <category>Software활용</category>
      <category>bash</category>
      <category>Java</category>
      <category>mac</category>
      <category>zsh</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/267</guid>
      <comments>https://enumclass.tistory.com/267#entry267comment</comments>
      <pubDate>Mon, 22 Aug 2022 08:50:08 +0900</pubDate>
    </item>
    <item>
      <title>linux에서 json Pretty Print 하기 (jq 명령어)</title>
      <link>https://enumclass.tistory.com/266</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl 을 이용해서 얻어온 json 포멧을&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;terminal에서 보기 좋게 만들자&lt;/p&gt;
&lt;pre id=&quot;code_1660226715863&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl http://localhost:8080/cars
{&quot;_embedded&quot;:{&quot;carList&quot;:[{&quot;id&quot;:1,&quot;createdAt&quot;:&quot;2022-08-11T22:59:40.56995&quot;,&quot;modifiedAt&quot;:&quot;2022-08-11T22:59:40.56995&quot;,&quot;condition&quot;:&quot;USED&quot;,&quot;details&quot;:{&quot;body&quot;:&quot;wood&quot;,&quot;model&quot;:&quot;crow&quot;,&quot;manufacturer&quot;:{&quot;code&quot;:100,&quot;name&quot;:&quot;Audi&quot;},&quot;numberOfDoors&quot;:null,&quot;fuelType&quot;:null,&quot;engine&quot;:null,&quot;mileage&quot;:null,&quot;modelYear&quot;:null,&quot;productionYear&quot;:null,&quot;externalColor&quot;:null},&quot;location&quot;:{&quot;lat&quot;:0.0,&quot;lon&quot;:0.0,&quot;address&quot;:null,&quot;city&quot;:null,&quot;state&quot;:null,&quot;zip&quot;:null},&quot;price&quot;:null,&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars/1&quot;},&quot;cars&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}},{&quot;id&quot;:2,&quot;createdAt&quot;:&quot;2022-08-11T22:59:40.577338&quot;,&quot;modifiedAt&quot;:&quot;2022-08-11T22:59:40.577338&quot;,&quot;condition&quot;:&quot;USED&quot;,&quot;details&quot;:{&quot;body&quot;:&quot;steel&quot;,&quot;model&quot;:&quot;house&quot;,&quot;manufacturer&quot;:{&quot;code&quot;:100,&quot;name&quot;:&quot;Audi&quot;},&quot;numberOfDoors&quot;:null,&quot;fuelType&quot;:null,&quot;engine&quot;:null,&quot;mileage&quot;:null,&quot;modelYear&quot;:null,&quot;productionYear&quot;:null,&quot;externalColor&quot;:null},&quot;location&quot;:{&quot;lat&quot;:0.0,&quot;lon&quot;:0.0,&quot;address&quot;:null,&quot;city&quot;:null,&quot;state&quot;:null,&quot;zip&quot;:null},&quot;price&quot;:null,&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars/2&quot;},&quot;cars&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}}]},&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 리눅스 terminal에서는 json 포멧을 예쁘게 보기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(아.. json을 더욱 길고 어렵게 만드는 hateoas 이름 만큼이나 밉다)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;jq설치&lt;/h2&gt;
&lt;pre id=&quot;code_1660226819782&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt install jq
[sudo] steven 암호: 
죄송합니다만, 다시 시도하십시오.
[sudo] steven 암호: 
패키지 목록을 읽는 중입니다... 완료
의존성 트리를 만드는 중입니다       
상태 정보를 읽는 중입니다... 완료
패키지 jq는 이미 최신 버전입니다 (1.6-1ubuntu0.20.04.1).
다음 패키지가 자동으로 설치되었지만 더 이상 필요하지 않습니다:
  libfwupdplugin1
'sudo apt autoremove'를 이용하여 제거하십시오.
0개 업그레이드, 0개 새로 설치, 0개 제거 및 27개 업그레이드 안 함.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo apt install jq 를 해서 jq를 install 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은 아래처럼 하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;json 파일 print&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jq . &amp;lt;&amp;lt;파일명&amp;gt;&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660227031431&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ jq . a.json
{
  &quot;_embedded&quot;: {
    &quot;carList&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;createdAt&quot;: null,
        &quot;modifiedAt&quot;: null,
        &quot;condition&quot;: &quot;USED&quot;,
        &quot;details&quot;: {
          &quot;body&quot;: &quot;Body&quot;,
          &quot;model&quot;: &quot;Audi&quot;,
          &quot;manufacturer&quot;: {
            &quot;code&quot;: 100,
            &quot;name&quot;: &quot;Audi&quot;
          },
          &quot;numberOfDoors&quot;: null,
          &quot;fuelType&quot;: null,
          &quot;engine&quot;: null,
          &quot;mileage&quot;: null,
          &quot;modelYear&quot;: null,
          &quot;productionYear&quot;: null,
          &quot;externalColor&quot;: null
        },
        &quot;location&quot;: null,
        &quot;price&quot;: null,
        &quot;_links&quot;: {
          &quot;self&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars/1&quot;
          },
          &quot;cars&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars&quot;
          }
        }
      }
    ]
  },
  &quot;_links&quot;: {
    &quot;self&quot;: {
      &quot;href&quot;: &quot;http://localhost:8080/cars&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;&amp;lt;json 나오는 명령어&amp;gt;&amp;gt; | jq .&lt;/p&gt;
&lt;pre id=&quot;code_1660227133432&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ echo '{&quot;foo&quot;: 0}' | jq .
{
  &quot;foo&quot;: 0
}&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;lt;&amp;lt;curl url&amp;gt;&amp;gt; | jq .&lt;/p&gt;
&lt;pre id=&quot;code_1660227192190&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/cars | jq -C .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1123    0  1123    0     0   109k      0 --:--:-- --:--:-- --:--:--  109k
{
  &quot;_embedded&quot;: {
    &quot;carList&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;createdAt&quot;: &quot;2022-08-11T22:59:40.56995&quot;,
        &quot;modifiedAt&quot;: &quot;2022-08-11T22:59:40.56995&quot;,
        &quot;condition&quot;: &quot;USED&quot;,
        &quot;details&quot;: {
          &quot;body&quot;: &quot;wood&quot;,
          &quot;model&quot;: &quot;crow&quot;,
          &quot;manufacturer&quot;: {
            &quot;code&quot;: 100,
            &quot;name&quot;: &quot;Audi&quot;
          },
          &quot;numberOfDoors&quot;: null,
          &quot;fuelType&quot;: null,
          &quot;engine&quot;: null,
          &quot;mileage&quot;: null,
          &quot;modelYear&quot;: null,
          &quot;productionYear&quot;: null,
          &quot;externalColor&quot;: null
        },
        &quot;location&quot;: {
          &quot;lat&quot;: 0,
          &quot;lon&quot;: 0,
          &quot;address&quot;: null,
          &quot;city&quot;: null,
          &quot;state&quot;: null,
          &quot;zip&quot;: null
        },
        &quot;price&quot;: null,
        &quot;_links&quot;: {
          &quot;self&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars/1&quot;
          },
          &quot;cars&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars&quot;
          }
        }
      },
      {
        &quot;id&quot;: 2,
        &quot;createdAt&quot;: &quot;2022-08-11T22:59:40.577338&quot;,
        &quot;modifiedAt&quot;: &quot;2022-08-11T22:59:40.577338&quot;,
        &quot;condition&quot;: &quot;USED&quot;,
        &quot;details&quot;: {
          &quot;body&quot;: &quot;steel&quot;,
          &quot;model&quot;: &quot;house&quot;,
          &quot;manufacturer&quot;: {
            &quot;code&quot;: 100,
            &quot;name&quot;: &quot;Audi&quot;
          },
          &quot;numberOfDoors&quot;: null,
          &quot;fuelType&quot;: null,
          &quot;engine&quot;: null,
          &quot;mileage&quot;: null,
          &quot;modelYear&quot;: null,
          &quot;productionYear&quot;: null,
          &quot;externalColor&quot;: null
        },
        &quot;location&quot;: {
          &quot;lat&quot;: 0,
          &quot;lon&quot;: 0,
          &quot;address&quot;: null,
          &quot;city&quot;: null,
          &quot;state&quot;: null,
          &quot;zip&quot;: null
        },
        &quot;price&quot;: null,
        &quot;_links&quot;: {
          &quot;self&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars/2&quot;
          },
          &quot;cars&quot;: {
            &quot;href&quot;: &quot;http://localhost:8080/cars&quot;
          }
        }
      }
    ]
  },
  &quot;_links&quot;: {
    &quot;self&quot;: {
      &quot;href&quot;: &quot;http://localhost:8080/cars&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;Json 압축&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;&amp;lt;json 나오는 명령어&amp;gt;&amp;gt; | jq -c .&lt;/p&gt;
&lt;pre id=&quot;code_1660227239353&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/cars | jq -c .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1123    0  1123    0     0  93583      0 --:--:-- --:--:-- --:--:-- 93583
{&quot;_embedded&quot;:{&quot;carList&quot;:[{&quot;id&quot;:1,&quot;createdAt&quot;:&quot;2022-08-11T22:59:40.56995&quot;,&quot;modifiedAt&quot;:&quot;2022-08-11T22:59:40.56995&quot;,&quot;condition&quot;:&quot;USED&quot;,&quot;details&quot;:{&quot;body&quot;:&quot;wood&quot;,&quot;model&quot;:&quot;crow&quot;,&quot;manufacturer&quot;:{&quot;code&quot;:100,&quot;name&quot;:&quot;Audi&quot;},&quot;numberOfDoors&quot;:null,&quot;fuelType&quot;:null,&quot;engine&quot;:null,&quot;mileage&quot;:null,&quot;modelYear&quot;:null,&quot;productionYear&quot;:null,&quot;externalColor&quot;:null},&quot;location&quot;:{&quot;lat&quot;:0,&quot;lon&quot;:0,&quot;address&quot;:null,&quot;city&quot;:null,&quot;state&quot;:null,&quot;zip&quot;:null},&quot;price&quot;:null,&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars/1&quot;},&quot;cars&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}},{&quot;id&quot;:2,&quot;createdAt&quot;:&quot;2022-08-11T22:59:40.577338&quot;,&quot;modifiedAt&quot;:&quot;2022-08-11T22:59:40.577338&quot;,&quot;condition&quot;:&quot;USED&quot;,&quot;details&quot;:{&quot;body&quot;:&quot;steel&quot;,&quot;model&quot;:&quot;house&quot;,&quot;manufacturer&quot;:{&quot;code&quot;:100,&quot;name&quot;:&quot;Audi&quot;},&quot;numberOfDoors&quot;:null,&quot;fuelType&quot;:null,&quot;engine&quot;:null,&quot;mileage&quot;:null,&quot;modelYear&quot;:null,&quot;productionYear&quot;:null,&quot;externalColor&quot;:null},&quot;location&quot;:{&quot;lat&quot;:0,&quot;lon&quot;:0,&quot;address&quot;:null,&quot;city&quot;:null,&quot;state&quot;:null,&quot;zip&quot;:null},&quot;price&quot;:null,&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars/2&quot;},&quot;cars&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}}]},&quot;_links&quot;:{&quot;self&quot;:{&quot;href&quot;:&quot;http://localhost:8080/cars&quot;}}}&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어영부영 세상 살다보니 Rest의 4단계 hateoas가 쓰이기 시작하네.. HAL이 Spring 때문에 이기려나&lt;/p&gt;</description>
      <category>Software활용</category>
      <category>hal</category>
      <category>HATEOAS</category>
      <category>JSON</category>
      <category>Pretty</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/266</guid>
      <comments>https://enumclass.tistory.com/266#entry266comment</comments>
      <pubDate>Thu, 11 Aug 2022 23:15:41 +0900</pubDate>
    </item>
    <item>
      <title>Slack backup 파일 Discord로 이동하기 (Slackord 사용법)</title>
      <link>https://enumclass.tistory.com/265</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack에서 백업 후 Down 받은 Json파일을 Discord로 이동 시킨다&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;얼마전에 Slack에서 아래와 같은 메시지가 왔다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;단순화된 한도:&lt;br /&gt;1만 건의 메시지 제한과 5GB의 저장 공간 대신 지난 90일 간의 메시지 내역과 파일 저장 공간에 대한 완전한 액세스를 제공하여 팀이 언제 한도에 달할지 걱정할 필요가 없습니다. 귀하의 팀이 Slack을 얼마나 많이 사용하였든 상관없이 언제든지 지난 90일 간의 내역에 액세스할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심을 보자면 이제는 용량이나 메시지 수와는 상관없이 &lt;b&gt;90일 간만 데이터를 액세스 할 수 있다는 소리&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부 일기 용으로 Slack을 사용하고 있었는데, 이제 더이상 슬렉을 사용하기 어려워 졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 무료로 제공해줘서 감사 한 마음이지만...&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 일자에 상관없이 보관될 수 있는 공간이 필요하다.&lt;/li&gt;
&lt;li&gt;물론 이미지까지 되면 더 좋고...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 다 만족시킬 수 있는 Discord로 이사를 가기로 했다.&amp;nbsp;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rXIBs/btrIFIvqqMO/rzRnjKQc3CEBOl0EAfIHSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rXIBs/btrIFIvqqMO/rzRnjKQc3CEBOl0EAfIHSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rXIBs/btrIFIvqqMO/rzRnjKQc3CEBOl0EAfIHSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrXIBs%2FbtrIFIvqqMO%2FrzRnjKQc3CEBOl0EAfIHSK%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;383&quot; height=&quot;320&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백업 하고자 하는 워크스페이스에서 Setting &amp;amp; administration을 선택하고 Workspace settings를 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2766&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oo5ZA/btrIGzezmbm/V0Ro4ZbJcKy1vjyqIrLTkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oo5ZA/btrIGzezmbm/V0Ro4ZbJcKy1vjyqIrLTkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oo5ZA/btrIGzezmbm/V0Ro4ZbJcKy1vjyqIrLTkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foo5ZA%2FbtrIGzezmbm%2FV0Ro4ZbJcKy1vjyqIrLTkk%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;691&quot; height=&quot;147&quot; data-origin-width=&quot;2766&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 이미지 우상단에 Import/Export Data를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1998&quot; data-origin-height=&quot;1070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VE9dG/btrII0QiIqE/6018LompcKt4AkOHQlgUyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VE9dG/btrII0QiIqE/6018LompcKt4AkOHQlgUyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VE9dG/btrII0QiIqE/6018LompcKt4AkOHQlgUyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVE9dG%2FbtrII0QiIqE%2F6018LompcKt4AkOHQlgUyK%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;634&quot; height=&quot;340&quot; data-origin-width=&quot;1998&quot; data-origin-height=&quot;1070&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Export 탭을 선택 후, Entire History를 선택 후 Start Export를 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blUcQe/btrIxS0qqng/5S9sH68KXC9S0azTC6yc0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blUcQe/btrIxS0qqng/5S9sH68KXC9S0azTC6yc0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blUcQe/btrIxS0qqng/5S9sH68KXC9S0azTC6yc0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblUcQe%2FbtrIxS0qqng%2F5S9sH68KXC9S0azTC6yc0k%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;683&quot; height=&quot;185&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 53KB의 파일이 생기는 것을 확인 가능하다. 이것을 다운받으면 채널과 일자별 업로드한 데이터가 JSON형식으로 잘 저장 된것을 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cszwRM/btrIxSMQ6qG/56kFpRSWe19DfMMe2YeJRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cszwRM/btrIxSMQ6qG/56kFpRSWe19DfMMe2YeJRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cszwRM/btrIxSMQ6qG/56kFpRSWe19DfMMe2YeJRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcszwRM%2FbtrIxSMQ6qG%2F56kFpRSWe19DfMMe2YeJRk%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;269&quot; height=&quot;297&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;circleci라는 채널에 2021년 2월 13일 무언가 채팅을 쳤다는 의미가 된다.&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;Discord 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Discord client를 다운로드 및 인스톨 한 후에 하기에 '+' 버튼을 눌러서 서버를 만들자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;144&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4thZM/btrIBUD1Fnk/0kIWP1okhRKlBq3fPHiP2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4thZM/btrIBUD1Fnk/0kIWP1okhRKlBq3fPHiP2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4thZM/btrIBUD1Fnk/0kIWP1okhRKlBq3fPHiP2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4thZM%2FbtrIBUD1Fnk%2F0kIWP1okhRKlBq3fPHiP2k%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;101&quot; height=&quot;431&quot; data-origin-width=&quot;144&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&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;아픈 역사 아래 더하기 버튼을 클릭하면 된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Discord Bot 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack데이터를 Discord로 옮기는 것은 다음 프로그램을 사용할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/thomasloupe/Slackord2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/thomasloupe/Slackord2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 프로그램도 찾아 보고 Slackord1도 써보고 했는데 최종적으로는 그냥 Slackord2를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어짜피 데이터 옮기는건 다 수작업 같은데.. 그나마 Slackord2가 편했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로그램은 Slack bot을 이용해서 특정 채널에 로드 된 JSON 데이터를 업로드 하는 방법을 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Discord Application 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://discord.com/developers/applications&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://discord.com/developers/applications&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에 들어가서 Application을 하나 만들어 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3504&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tXuxh/btrICZ59jX9/kKeLixXU29nGkohrMtYDkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tXuxh/btrICZ59jX9/kKeLixXU29nGkohrMtYDkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tXuxh/btrICZ59jX9/kKeLixXU29nGkohrMtYDkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtXuxh%2FbtrICZ59jX9%2FkKeLixXU29nGkohrMtYDkk%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;3504&quot; height=&quot;310&quot; data-origin-width=&quot;3504&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측에 New Application을 누르면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvVN3/btrIH7IXjtF/OeK9DAtMF4F15RhbRV2x90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvVN3/btrIH7IXjtF/OeK9DAtMF4F15RhbRV2x90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvVN3/btrIH7IXjtF/OeK9DAtMF4F15RhbRV2x90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvvVN3%2FbtrIH7IXjtF%2FOeK9DAtMF4F15RhbRV2x90%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;528&quot; height=&quot;439&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 이름이나 상관없다. 이후 Create를 클릭하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Discord Bot 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Bot을 내가 만든 Server와 연결하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLHgTS/btrIKm6pTD6/D9C4cdtRK3cHuJ0TxvYlEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLHgTS/btrIKm6pTD6/D9C4cdtRK3cHuJ0TxvYlEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLHgTS/btrIKm6pTD6/D9C4cdtRK3cHuJ0TxvYlEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLHgTS%2FbtrIKm6pTD6%2FD9C4cdtRK3cHuJ0TxvYlEK%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;640&quot; height=&quot;236&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OAuth2에 URL Generator를 선택 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scopes에서 bot을 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PtgOP/btrII1uUQvH/00IyRp7UKFrKmC5wzCDeH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PtgOP/btrII1uUQvH/00IyRp7UKFrKmC5wzCDeH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PtgOP/btrII1uUQvH/00IyRp7UKFrKmC5wzCDeH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPtgOP%2FbtrII1uUQvH%2F00IyRp7UKFrKmC5wzCDeH0%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;266&quot; height=&quot;253&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봇의 권한을 설정해 줘야 하는데, 과감하게 Administrator를 선택해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 하단에 다음과 같이 url을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2762&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bawFSB/btrIC0xgc7c/GJjNKRSIEotUwGUByNRDI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bawFSB/btrIC0xgc7c/GJjNKRSIEotUwGUByNRDI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawFSB/btrIC0xgc7c/GJjNKRSIEotUwGUByNRDI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbawFSB%2FbtrIC0xgc7c%2FGJjNKRSIEotUwGUByNRDI0%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;2762&quot; height=&quot;216&quot; data-origin-width=&quot;2762&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 URL을 브라우저 네비게이션 바에 붙여주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3L0nT/btrIC0D2mz9/Y3RiaQzSJZmQcaBHefHpdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3L0nT/btrIC0D2mz9/Y3RiaQzSJZmQcaBHefHpdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3L0nT/btrIC0D2mz9/Y3RiaQzSJZmQcaBHefHpdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3L0nT%2FbtrIC0D2mz9%2FY3RiaQzSJZmQcaBHefHpdK%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;530&quot; height=&quot;462&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 앞서 내가만든 서버(서버 선택하기 부분)를 선택할 수 있고, 선택이 완료 되면 나의 bot이 나의 서버에 참가한 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKqDgK/btrIxUqpGMu/YNIStGONU5NkVYN5ytQvr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKqDgK/btrIxUqpGMu/YNIStGONU5NkVYN5ytQvr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKqDgK/btrIxUqpGMu/YNIStGONU5NkVYN5ytQvr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKqDgK%2FbtrIxUqpGMu%2FYNIStGONU5NkVYN5ytQvr0%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;266&quot; height=&quot;234&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bot token 얻기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQbFwr/btrIBTZqmhL/7mTLayaq09YRAnFp4g39e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQbFwr/btrIBTZqmhL/7mTLayaq09YRAnFp4g39e1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQbFwr/btrIBTZqmhL/7mTLayaq09YRAnFp4g39e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQbFwr%2FbtrIBTZqmhL%2F7mTLayaq09YRAnFp4g39e1%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;601&quot; height=&quot;227&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 Discord Application에서 Bot을 선택하고 우측에 Reset Token을 누르면 Token을 발행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Token을 저장해 놓자.&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;Slackord 설치 및 사용하기&lt;/h2&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://github.com/thomasloupe/Slackord2/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/thomasloupe/Slackord2/releases&lt;/a&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;참고로 윈도우 용이다. 리눅스나, 맥은 python을 사용하는 1.0을 써야하는데 굉장히 귀찮다. 그냥 윈도우 컴퓨터를 사용하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfuWvI/btrII0W4nIe/JhXJiZAttNNotOZcfofyyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfuWvI/btrII0W4nIe/JhXJiZAttNNotOZcfofyyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfuWvI/btrII0W4nIe/JhXJiZAttNNotOZcfofyyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfuWvI%2FbtrII0W4nIe%2FJhXJiZAttNNotOZcfofyyK%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;387&quot; height=&quot;319&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slackord 2.exe를 실행 시킨다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bot 토큰 입력 및 연결&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqv2Uf/btrICZZnRYu/9pJKKC1KvUs6yc1OLK8JMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqv2Uf/btrICZZnRYu/9pJKKC1KvUs6yc1OLK8JMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqv2Uf/btrICZZnRYu/9pJKKC1KvUs6yc1OLK8JMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqv2Uf%2FbtrICZZnRYu%2F9pJKKC1KvUs6yc1OLK8JMk%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;383&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Settings &amp;gt; Enter Bot Token&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 선택하면 위와 같은 창이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 저장한 토큰을 붙여 넣고 OK를 눌러주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 잘 되었다면 아래와 같이 Connect를 통해서 Bot 접근이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci7cyd/btrIxwce39J/natBgUdwy5lrpfaiehiyd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci7cyd/btrIxwce39J/natBgUdwy5lrpfaiehiyd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci7cyd/btrIxwce39J/natBgUdwy5lrpfaiehiyd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci7cyd%2FbtrIxwce39J%2FnatBgUdwy5lrpfaiehiyd0%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;502&quot; height=&quot;184&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 성공했다면 아래와 같이 봇이 온라인이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgQg0Y/btrIJWAb02k/YjhOVSEXO6gOwgq1pnoxQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgQg0Y/btrIJWAb02k/YjhOVSEXO6gOwgq1pnoxQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgQg0Y/btrIJWAb02k/YjhOVSEXO6gOwgq1pnoxQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgQg0Y%2FbtrIJWAb02k%2FYjhOVSEXO6gOwgq1pnoxQ1%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;206&quot; height=&quot;150&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Json 로드하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이동시킬 Slack에서 백업받은 JSON을 선택해서 넣어주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(굉장히 귀찮긴 한데.. 이게 무슨 봇으로 입력하는데 한계? 이런게 있다보다. 아무튼 다른 방법을 못찾았다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu1RIx/btrIAEmQMIM/xHmIbtkXky2qpsQyKkBlm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu1RIx/btrIAEmQMIM/xHmIbtkXky2qpsQyKkBlm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu1RIx/btrIAEmQMIM/xHmIbtkXky2qpsQyKkBlm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu1RIx%2FbtrIAEmQMIM%2FxHmIbtkXky2qpsQyKkBlm0%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;313&quot; height=&quot;194&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Import JSON을 클릭 후 Import할 Json을 선택해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d16FWu/btrIy9m8YlQ/k2HYmoLmiuGLnOS9vQ9QMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d16FWu/btrIy9m8YlQ/k2HYmoLmiuGLnOS9vQ9QMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d16FWu/btrIy9m8YlQ/k2HYmoLmiuGLnOS9vQ9QMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd16FWu%2FbtrIy9m8YlQ%2Fk2HYmoLmiuGLnOS9vQ9QMK%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;335&quot; height=&quot;143&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 Load되었다면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb1Kzs/btrIEcRJJf3/D5deh2OAKWd619RnrCTkB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb1Kzs/btrIEcRJJf3/D5deh2OAKWd619RnrCTkB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb1Kzs/btrIEcRJJf3/D5deh2OAKWd619RnrCTkB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb1Kzs%2FbtrIEcRJJf3%2FD5deh2OAKWd619RnrCTkB0%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;874&quot; height=&quot;202&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 Begin과 Parsing사이에 무슨 메시지가 로드 되었는지 표시가 된다.&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;이렇게 로드가 된 다음에 해당 데이터를 업로드할 Discord 채널로 이동한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQGVE/btrIBTZrx9f/0n5ijOtomtCpdeKYWZK2Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQGVE/btrIBTZrx9f/0n5ijOtomtCpdeKYWZK2Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQGVE/btrIBTZrx9f/0n5ijOtomtCpdeKYWZK2Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQGVE%2FbtrIBTZrx9f%2F0n5ijOtomtCpdeKYWZK2Xk%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;273&quot; height=&quot;164&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅창에 !slackord 라고 입력을 하면, 위에 로드된 데이터가 Discord 채널에 입력이 되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1eBef/btrIEY6PDIh/RKAjuBH7o3TQS9dzgSfQ10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1eBef/btrIEY6PDIh/RKAjuBH7o3TQS9dzgSfQ10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1eBef/btrIEY6PDIh/RKAjuBH7o3TQS9dzgSfQ10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1eBef%2FbtrIEY6PDIh%2FRKAjuBH7o3TQS9dzgSfQ10%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;106&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 slackbackup 봇이 로드 된 '2022-04-12 오전 1:18 등등등'의 데이터를 입력해 준다.&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;어찌어찌 데이터를 Discord로 옮겨 넣는데 성공했다.&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;</description>
      <category>Software활용</category>
      <category>discord</category>
      <category>slack</category>
      <category>slackord</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/265</guid>
      <comments>https://enumclass.tistory.com/265#entry265comment</comments>
      <pubDate>Mon, 1 Aug 2022 17:01:44 +0900</pubDate>
    </item>
    <item>
      <title>Mac에서 zsh 또는 bash shell을 사용하기</title>
      <link>https://enumclass.tistory.com/264</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결하고자 하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mac에서 terminal shell은 기본적으로 zsh이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 shell을 bash shell로 변경하고자 한다.&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;zsh을 bash shell로 변경하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 mac에서 터미널을 실행 후 다음과 같이 명령어를 실행해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1658818454335&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% echo $SHELL
/bin/zsh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 mac의 터미널 쉘은 zsh을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 bash shell로 변경해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1658818596558&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% chsh -s /bin/bash
Changing shell for user.
Password for user: 
%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 쉘을 chsh 명령어를 사용해서 default shell을 /bin/bash로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 터미널을 종료하고 다시 실행 시켜 보면 다음과 같이 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1658818720649&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
$&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘이 잘 변경 되었는지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1658818824687&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ echo $SHELL
/bin/bash&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;zsh로 돌아가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 실행하면 zsh로 돌아갈 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1658818905342&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ chsh -s /bin/zsh
Changing shell for user.
Password for user: 
$&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Software활용</category>
      <category>bash</category>
      <category>zsh</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/264</guid>
      <comments>https://enumclass.tistory.com/264#entry264comment</comments>
      <pubDate>Tue, 26 Jul 2022 16:02:07 +0900</pubDate>
    </item>
    <item>
      <title>Multipass Disk Size (Memory/Cpu) Change (용량 변경)</title>
      <link>https://enumclass.tistory.com/263</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multipass 인스턴스를 사용하는 도중 아래와 같이 Disk를 거의다 사용하게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1658022024189&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ multipass info master                                   
Name:           master
State:          Running
IPv4:           10.112.125.79
Release:        Ubuntu 20.04.4 LTS
Image hash:     7bd0d8d14260 (Ubuntu 20.04 LTS)
Load:           0.00 0.00 0.00
Disk usage:     4.3G out of 4.7G
Memory usage:   212.3M out of 15.6G
Mounts:         --&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Master Instance의 Disk Size를 증가 시키고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Disk Size 증가 법과 Memory/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;버전 Upgrade&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multipass는 1.10버전 이전에는 별도의 Size 증가 기능을 제공하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 qemu-img를 사용해서 인스턴스 이미지를 수정하였는데, 사용 방법이 개인적으로 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 조금더 찾아본 결과 Multipass 1.10버전 이후 부터는 이런 사이즈 변경 기능(qemu-img를 내부적으로 사용)을 제공하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://multipass.run/docs/modify-an-instance&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://multipass.run/docs/modify-an-instance&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 본 포스팅 내용을 따라하려면 1.10 이후 버전으로 Multipass를 Update 해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;버전 확인하기&lt;/h3&gt;
&lt;pre id=&quot;code_1658022268881&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass --version
multipass   1.9.2
multipassd  1.9.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현 버전은 1.9.2이다. 버전을 업데이트 해보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Snap을 이용한 버전 Update&lt;/h3&gt;
&lt;pre id=&quot;code_1658022350755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo snap refresh multipass
&quot;multipass&quot;에 대한 필수 구성 요소를 사용할 수 있는지 확인
...
snap &quot;multipass&quot; (7478) 서비스 시작                                                                                                                                                     snap &quot;multipass&quot; (7478) 서비스 시작                                                                                                                                                     snap &quot;multipass&quot; (7478) 서비스 시작                                                                                                                                                     
multipass 1.10.0 from Canonical✓ snap 패키지가 새로 고쳐졌습니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'sudo snap refresh multipass' 명령어를 통해 버전을 업데이트 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트 후 패키지를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658022407180&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo snap start multipass
Started.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전을 다시 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1658022424522&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass version
multipass   1.10.0
multipassd  1.10.0&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;인스턴스 정보 변경하기&lt;/h2&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;pre id=&quot;code_1658022501971&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass info master                                   
Name:           master
State:          Running
IPv4:           10.112.125.79
Release:        Ubuntu 20.04.4 LTS
Image hash:     7bd0d8d14260 (Ubuntu 20.04 LTS)
Load:           0.00 0.00 0.00
Disk usage:     4.3G out of 4.7G
Memory usage:   212.3M out of 15.6G
Mounts:         --&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 master가 인스턴스 명이다. 주의해야 할것은 info를 확인 하기 위해서는 해당 인스턴스가 실행 중이어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 Disk Size를 보면 4.7G(5G) 중 4.3G를 사용중인 것을 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 Disk Size를 증가 시켜 보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key정보 확인하기&lt;/h3&gt;
&lt;pre id=&quot;code_1658022599584&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass get --keys
client.gui.autostart
client.gui.hotkey
client.primary-name
local.bridged-network
local.driver
local.master.cpus
local.master.disk
local.master.memory
local.node1.cpus
local.node1.disk
local.node1.memory
local.node2.cpus
local.node2.disk
local.node2.memory
local.passphrase
local.primary.cpus
local.primary.disk
local.primary.memory
local.privileged-mounts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'multipass get --keys'를 통해서 현 multipass의 인스턴스에 대한 keys를 모두 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 master, node1, node2 인스턴스가 존재 한다는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 master의 Disk 정보를 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1658022678021&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass get local.master.disk
5.0GiB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master는 5GiB의 용량을 활용 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 10GiB로 늘려보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;용량 늘리기&lt;/h3&gt;
&lt;pre id=&quot;code_1658022720987&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass set local.master.disk=10240MiB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 알게된 key인 local.master.disk의 value를 set명령어를 통해서 변경해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량 변경단위는 MiB(메가바이트)이다. GiB 변경을 해보려고 했으나 아직 지원하지 않는듯 하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10G = 10240MiB = 1MB * 1024 * 10&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 10G를 계산하고 MiB 단위로 변경해 주었다.&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;pre id=&quot;code_1658022872023&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass get local.master.disk
10.0GiB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key value는 정상적으로 수정되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Master를 실행 후 info를 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1658022907448&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass start master&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1658022918770&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass info master
Name:           master
State:          Running
IPv4:           10.112.125.79
Release:        Ubuntu 20.04.4 LTS
Image hash:     7bd0d8d14260 (Ubuntu 20.04 LTS)
Load:           0.00 0.00 0.00
Disk usage:     4.3G out of 9.5G
Memory usage:   213.8M out of 15.6G
Mounts:         --&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량이 9.5G로 변경되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 shell 접속을 통해 Disk가 확장 되었는지 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1658022961832&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass shell master
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-122-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jul 17 10:37:09 KST 2022

  System load:  0.07              Processes:              196
  Usage of /:   45.6% of 9.51GB   Users logged in:        0
  Memory usage: 5%                IPv4 address for ens3:  10.112.125.79
  Swap usage:   0%                IPv4 address for tunl0: 192.168.219.64

 * Super-optimized for small spaces - read how we shrank the memory
   footprint of MicroK8s to make it the smallest full K8s around.

   https://ubuntu.com/blog/microk8s-memory-optimisation

1 update can be applied immediately.
To see these additional updates run: apt list --upgradable


Last login: Thu Jul 14 00:06:09 2022 from 10.112.125.1&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1658022978049&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ubuntu@master:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            7.9G     0  7.9G   0% /dev
tmpfs           1.6G  1.7M  1.6G   1% /run
/dev/sda1       9.6G  4.4G  5.2G  46% /&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Usage of의 정보나 df를 통해 /dev/sda1의 정보를 보아도 Size가 정상적으로 확장 된 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WhNgS/btrHpP4nlsk/nKtvD0az9jtxkcb9AvkBA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WhNgS/btrHpP4nlsk/nKtvD0az9jtxkcb9AvkBA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WhNgS/btrHpP4nlsk/nKtvD0az9jtxkcb9AvkBA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWhNgS%2FbtrHpP4nlsk%2FnKtvD0az9jtxkcb9AvkBA0%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;692&quot; height=&quot;288&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AWS</category>
      <category>cpu</category>
      <category>Disk</category>
      <category>Memory</category>
      <category>multipass</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/263</guid>
      <comments>https://enumclass.tistory.com/263#entry263comment</comments>
      <pubDate>Sun, 17 Jul 2022 10:58:05 +0900</pubDate>
    </item>
    <item>
      <title>Calico FailedCreatePodSandBox Unauthorized</title>
      <link>https://enumclass.tistory.com/262</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결하고자 하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes를 사용하는 도중에 다음과 같은 오류가 나타났다.&lt;/p&gt;
&lt;pre id=&quot;code_1657547107928&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Events:
  Type     Reason                  Age   From               Message
  ----     ------                  ----  ----               -------
  Normal   Scheduled               70s   default-scheduler  Successfully assigned udacity/nginx-basic-5fbb84747d-4zv64 to node2
  Warning  FailedCreatePodSandBox  70s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;9cc2d017dc9f1bdd51e0fa08beb963b8289d349d367a0fc155e5bd072aff6a01&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  59s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;c49aa0f5fff11f0c448d3e4765cfa06cc313c366cfa96ba539cccdbfe548418a&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷을 찾아보니 Calico를 사용하는 경우 이런일이 종종 발생하는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 FailedCreatePodSandBox 오류는 다음과 같은 종류가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.containiq.com/post/troubleshooting-failed-to-create-pod-sandbox-error&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.containiq.com/post/troubleshooting-failed-to-create-pod-sandbox-error&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Failed Find Plugin&lt;/li&gt;
&lt;li&gt;Failed To assign IP&lt;/li&gt;
&lt;li&gt;Can not Allocated Memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 내가난 오류는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unauthorized&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;분명히 Calico는 정상적으로 작동 중이다.&lt;/p&gt;
&lt;pre id=&quot;code_1657547953462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kube-system   calico-kube-controllers-6766647d54-6pdxm   1/1     Running             0          30h
kube-system   calico-node-j48gp                          1/1     Running             0          30h
kube-system   calico-node-jnpl7                          1/1     Running             0          30h
kube-system   calico-node-rzzp6                          1/1     Running             0          30h
kube-system   coredns-6d4b75cb6d-l5t5f                   1/1     Running             0          30h
kube-system   coredns-6d4b75cb6d-sgw7h                   1/1     Running             0          30h
kube-system   etcd-master                                1/1     Running             0          30h
kube-system   kube-apiserver-master                      1/1     Running             0          30h
kube-system   kube-controller-manager-master             1/1     Running             0          30h
kube-system   kube-proxy-d4jg6                           1/1     Running             0          30h
kube-system   kube-proxy-fvrqj                           1/1     Running             0          30h
kube-system   kube-proxy-qlmtv                           1/1     Running             0          30h
kube-system   kube-scheduler-master                      1/1     Running             0          30h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 새로운 Deployment를 적재하지 못한다.&lt;/p&gt;
&lt;pre id=&quot;code_1657547990311&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Name:           nginx-basic-5fbb84747d-4zv64
Namespace:      udacity
Priority:       0
Node:           node2/10.112.125.243
Start Time:     Mon, 11 Jul 2022 22:19:58 +0900
Labels:         app=nginx-basic
                pod-template-hash=5fbb84747d
Annotations:    &amp;lt;none&amp;gt;
Status:         Pending
IP:             
IPs:            &amp;lt;none&amp;gt;
Controlled By:  ReplicaSet/nginx-basic-5fbb84747d
Containers:
  nginx:
    Container ID:   
    Image:          nginx:1.21.1
    Image ID:       
    Port:           &amp;lt;none&amp;gt;
    Host Port:      &amp;lt;none&amp;gt;
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Environment:    &amp;lt;none&amp;gt;
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-ktz97 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  kube-api-access-ktz97:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       &amp;lt;nil&amp;gt;
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              &amp;lt;none&amp;gt;
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason                  Age   From               Message
  ----     ------                  ----  ----               -------
  Normal   Scheduled               70s   default-scheduler  Successfully assigned udacity/nginx-basic-5fbb84747d-4zv64 to node2
  Warning  FailedCreatePodSandBox  70s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;9cc2d017dc9f1bdd51e0fa08beb963b8289d349d367a0fc155e5bd072aff6a01&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  59s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;c49aa0f5fff11f0c448d3e4765cfa06cc313c366cfa96ba539cccdbfe548418a&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  44s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;45074e195ed08d31de5add165cbf080c1d828950c01bbabeb691b6970fbcf319&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  33s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;4394a9ed6e46ded62774adc455ba9652482690192d545e3b1fa3d19ec82b99ce&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  18s   kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;f8b2a6eedbb3d9315f6ab20a28312ecee2436b5ece0a14955f70ae773b7ccac8&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized
  Warning  FailedCreatePodSandBox  5s    kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;9ac4c41af56e11c3e94e0ebada8a832ee050e69379d1261a60929b08bd38bed3&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 Calico를 삭제하고 재 설치하는 방법이나, kubernetes를 재 기동 시키는 방법 두가지가 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재기동은 많이 귀찮음으로 Calico를 삭제 하는 것을 선택했다.&lt;/p&gt;
&lt;pre id=&quot;code_1657548079969&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl delete -f https://docs.projectcalico.org/manifests/calico.yaml
configmap &quot;calico-config&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;bgpconfigurations.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;bgppeers.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;blockaffinities.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;caliconodestatuses.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;clusterinformations.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;felixconfigurations.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;globalnetworkpolicies.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;globalnetworksets.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;hostendpoints.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;ipamblocks.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;ipamconfigs.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;ipamhandles.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;ippools.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;ipreservations.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;kubecontrollersconfigurations.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;networkpolicies.crd.projectcalico.org&quot; deleted
customresourcedefinition.apiextensions.k8s.io &quot;networksets.crd.projectcalico.org&quot; deleted
clusterrole.rbac.authorization.k8s.io &quot;calico-kube-controllers&quot; deleted
clusterrolebinding.rbac.authorization.k8s.io &quot;calico-kube-controllers&quot; deleted
clusterrole.rbac.authorization.k8s.io &quot;calico-node&quot; deleted
clusterrolebinding.rbac.authorization.k8s.io &quot;calico-node&quot; deleted
daemonset.apps &quot;calico-node&quot; deleted
serviceaccount &quot;calico-node&quot; deleted
deployment.apps &quot;calico-kube-controllers&quot; deleted
serviceaccount &quot;calico-kube-controllers&quot; deleted
poddisruptionbudget.policy &quot;calico-kube-controllers&quot; deleted&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 정상적으로 데몬셋이 지워졌지만..&lt;/p&gt;
&lt;pre id=&quot;code_1657548133120&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Events:
  Type     Reason         Age                 From     Message
  ----     ------         ----                ----     -------
  Normal   Killing        111s                kubelet  Stopping container calico-kube-controllers
  Warning  FailedKillPod  7s (x11 over 111s)  kubelet  error killing pod: failed to &quot;KillPodSandbox&quot; for &quot;959d9864-e296-40a4-b3c9-1b68fd838aef&quot; with KillPodSandboxError: &quot;rpc error: code = Unknown desc = failed to destroy network for sandbox \&quot;a45b14de6745a8b2cb82ff66868f92fb4bc615b345a480b5286b1ed7e454f289\&quot;: error getting ClusterInformation: connection is unauthorized: Unauthorized&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Calico가 지워지는데 실패 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 조금 지나고 다시 재 설치 해보니까.&lt;/p&gt;
&lt;pre id=&quot;code_1657548180530&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
daemonset.apps/calico-node created
serviceaccount/calico-node created
deployment.apps/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
poddisruptionbudget.policy/calico-kube-controllers created&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657548198497&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-6766647d54-jbmlf   1/1     Running   0          30s
kube-system   calico-node-fnf5h                          1/1     Running   0          30s
kube-system   calico-node-gmqkg                          1/1     Running   0          30s
kube-system   calico-node-vbbqn                          1/1     Running   0          30s
kube-system   coredns-6d4b75cb6d-l5t5f                   1/1     Running   0          30h
kube-system   coredns-6d4b75cb6d-sgw7h                   1/1     Running   0          30h
kube-system   etcd-master                                1/1     Running   0          30h
kube-system   kube-apiserver-master                      1/1     Running   0          30h
kube-system   kube-controller-manager-master             1/1     Running   0          30h
kube-system   kube-proxy-d4jg6                           1/1     Running   0          30h
kube-system   kube-proxy-fvrqj                           1/1     Running   0          30h
kube-system   kube-proxy-qlmtv                           1/1     Running   0          30h
kube-system   kube-scheduler-master                      1/1     Running   0          30h&lt;/code&gt;&lt;/pre&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;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;괜히 무섭네...&lt;/p&gt;</description>
      <category>AWS</category>
      <category>Calico</category>
      <category>Kubernetes</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/262</guid>
      <comments>https://enumclass.tistory.com/262#entry262comment</comments>
      <pubDate>Mon, 11 Jul 2022 23:04:21 +0900</pubDate>
    </item>
    <item>
      <title>Multipass를 이용한 Kubernetes 환경 구성</title>
      <link>https://enumclass.tistory.com/261</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;하고자 하는것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ubuntu local computer에 kubernetes환경(1master 2nodes)을 구성하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;local 컴퓨터에서 kubernetes환경을 접근한다.&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;Multipass install 및 Instances 생성&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Multipass를 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://multipass.run/install&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://multipass.run/install&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657438473476&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ sudo snap install multipass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu의 경우 &quot;sudo snap install multipass&quot;를 입력하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1657438507054&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;snap &quot;multipass&quot; (7455) 서비스 시작                                             

multipass1.9.2 from Canonical✓ installed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 multipass가 다 설치 되면, 아래와 같이 명령어를 실행 시켰을 때 나오면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1657438543132&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ multipass list
No instances found.&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;list&amp;nbsp;failed:&amp;nbsp;cannot&amp;nbsp;connect&amp;nbsp;to&amp;nbsp;the&amp;nbsp;multipass&amp;nbsp;socket&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위와 같은 오류가 나온다면 multipass의 socket파일 권한 오류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;$&amp;nbsp;systemctl&amp;nbsp;status&amp;nbsp;snap.multipass.multipassd.service&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1692754729642&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ systemctl status snap.multipass.multipassd.service
● snap.multipass.multipassd.service - Service for snap application multipass.multipassd
     Loaded: loaded (/etc/systemd/system/snap.multipass.multipassd.service; enabled; preset: enabled)
     Active: active (running) since Wed 2023-08-23 10:23:52 KST; 1min 48s ago
   Main PID: 9104 (multipassd)
      Tasks: 23 (limit: 37379)
     Memory: 128.7M
        CPU: 1.118s
     CGroup: /system.slice/snap.multipass.multipassd.service
             ├─9104 /snap/multipass/10244/bin/multipassd --verbosity debug --logger platform
             └─9205 /snap/multipass/10244/usr/sbin/dnsmasq --keep-in-foreground --strict-order --bind-interfaces --pid-file --domain=multipass --local=/multipass/ --except-&amp;gt;

 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;charm-dev&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;docker&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;jellyfin&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;minikube&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;ros-noetic&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: Loading &quot;ros2-humble&quot; v1
 8월 23 10:24:10 theyoung-MS-7D77 multipassd[9104]: gRPC listening on unix:/var/snap/multipass/common/multipass_socket
 8월 23 10:24:11 theyoung-MS-7D77 multipassd[9104]: QIODevice::write (QFile, &quot;/var/snap/multipass/common/cache/multipassd/vault/multipassd-image-records.json&quot;): device not &amp;gt;
 8월 23 10:24:11 theyoung-MS-7D77 multipassd[9104]: Starting Multipass 1.12.2
 8월 23 10:24:11 theyoung-MS-7D77 multipassd[9104]: Daemon arguments: /snap/multipass/10244/bin/multipassd --verbosity debug --logger platform&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 보면 어떤 소켓을 접근 하는지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gRPC&amp;nbsp;listening&amp;nbsp;on&amp;nbsp;unix:/var/snap/multipass/common/multipass_socket&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 소켓에 대한 접근 권한을 확인하자&lt;/p&gt;
&lt;pre id=&quot;code_1692754821825&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ls -al /var/snap/multipass/common/multipass_socket
srw-rw-rw- 1 root root 0  8월 23 10:24 /var/snap/multipass/common/multipass_socket&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root권한과 root그룹 권한을 갖는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 갖고 있는 그룹 권한에 해당 파일을 추가 시켜 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론 root권한 그룹이 아니라면 해당 그룹을 나의 계정에 추가 해주는 방법도 있다)&lt;/p&gt;
&lt;pre id=&quot;code_1692754892024&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ groups
theyoung adm cdrom sudo dip plugdev ssl-cert lpadmin lxd sambashare wireshark docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 계정이 갖고 있는 그룹을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 그룹에 해당 소켓을 추가 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1692754968614&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo chgrp docker /var/snap/multipass/common/multipass_socket&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 list를 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1692755020515&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass list
list failed: The client is not authenticated with the Multipass service.
Please use 'multipass authenticate' before proceeding.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무언가를 더 확인해 달라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 multipass에 접근 가능한 비밀번호를 입력하자.&lt;/p&gt;
&lt;pre id=&quot;code_1692755044048&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo multipass set local.passphrase
Please enter passphrase: 
Please re-enter passphrase:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다시 authentication 처리를 하면&lt;/p&gt;
&lt;pre id=&quot;code_1692755099906&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass authenticate 
Please enter passphrase:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 multipass를 사용할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1692755134531&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ multipass list
No instances found.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Instances 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스는 모두 3개를 생성할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Master하나 Nodes2개이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Instances별 기본 Spec은 아래 링크에 따랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/en/operating-systems/olcne/1.1/relnotes/hosts.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/en/operating-systems/olcne/1.1/relnotes/hosts.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657438631586&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ multipass launch --cpus 4 --mem 16G --name master
Launched: master                                                                
steven@steven-ubuntu:~$ multipass launch --cpus 2 --mem 8G --name node1
Launched: node1                                                                 
steven@steven-ubuntu:~$ multipass launch --cpus 2 --mem 8G --name node2
Launched: node2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 3개의 Instances가 생성 완료 된 후 아래와 같이 정상적으로 실행이 되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1657438669646&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ multipass list                                          
Name                    State             IPv4             Image
master                  Running           10.112.125.79    Ubuntu 20.04 LTS
node1                   Running           10.112.125.159   Ubuntu 20.04 LTS
node2                   Running           10.112.125.243   Ubuntu 20.04 LTS&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&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;Kubernetes Cluster 설치 및 Nodes 등록&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;shell login 하기&lt;/h3&gt;
&lt;pre id=&quot;code_1657438738504&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ multipass shell node1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7x7So/btrGWaz1HEP/7hdMtiDM4KMaXVbUhD04I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7x7So/btrGWaz1HEP/7hdMtiDM4KMaXVbUhD04I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7x7So/btrGWaz1HEP/7hdMtiDM4KMaXVbUhD04I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7x7So%2FbtrGWaz1HEP%2F7hdMtiDM4KMaXVbUhD04I0%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;627&quot; height=&quot;409&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;multipass의 shell 명령어를 이용해서 master, node1, node2에 shell 진입을 해주자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;환경 구성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes환경은 2가지 작업이 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker설치&lt;/li&gt;
&lt;li&gt;Kubernetes 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두가지 작업은 다음 github에 있는 Shell Script를 활용 하겠다. (&lt;span style=&quot;color: #3d3b49;&quot;&gt;Sander van Vugt&lt;span&gt; 선생닝 Repo이다.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3b49;&quot;&gt;master,node1,node2모두에 동일하게 git clone을 진행해 주자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657438971776&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ubuntu@master:~$ git clone https://github.com/sandervanvugt/cka.git
Cloning into 'cka'...
remote: Enumerating objects: 185, done.
remote: Counting objects: 100% (93/93), done.
remote: Compressing objects: 100% (64/64), done.
remote: Total 185 (delta 43), reused 35 (delta 29), pack-reused 92
Receiving objects: 100% (185/185), 344.62 KiB | 2.73 MiB/s, done.
Resolving deltas: 100% (75/75), done.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 master,node1,node2 모두 아래 명령어를 실행 시켜 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1657438998573&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ubuntu@master:~/cka$ ./setup-container.sh 

ubuntu@node2:~/cka$ sudo ./setup-kubetools.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Docker와 Kubernetes가 다 설치 완료 되었다.&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;Kubernetes cluster 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;master에&lt;/b&gt;&lt;/u&gt; Control pane을 만들어 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1657439064148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ubuntu@master:~/cka$ sudo kubeadm init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 설치가 완료 되었다면 아래와 같이 master에서만 명령어를 실행 시켜준다.&lt;/p&gt;
&lt;pre id=&quot;code_1657439098755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Control Pane 설치가 완료 되었음으로 Node를 등록해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;Node1,Node2&lt;/b&gt;&lt;/u&gt;에서는 아래 명령어를 실행해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1657439139716&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo kubeadm join 10.112.125.79:6443 --token 9l0e6o.qj4woevjzw2d6323 \
--discovery-token-ca-cert-hash sha256:d726f1abcd3601d00a0146fe7d2a78cafcf732d16e76f39a6117495e75987828&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 명령어는 설치 마지막에 나오는 값이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2020&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUF2Wk/btrGSyoT4f6/5U2ukSt2sDtrS3Rizvw6H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUF2Wk/btrGSyoT4f6/5U2ukSt2sDtrS3Rizvw6H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUF2Wk/btrGSyoT4f6/5U2ukSt2sDtrS3Rizvw6H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUF2Wk%2FbtrGSyoT4f6%2F5U2ukSt2sDtrS3Rizvw6H1%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;562&quot; height=&quot;234&quot; data-origin-width=&quot;2020&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node 3개가 나오면 Kubernetes 설치를 완료 한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XSMPx/btrGVla49ev/s3A4uaM2zj9SWtEkEPljTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XSMPx/btrGVla49ev/s3A4uaM2zj9SWtEkEPljTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XSMPx/btrGVla49ev/s3A4uaM2zj9SWtEkEPljTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXSMPx%2FbtrGVla49ev%2Fs3A4uaM2zj9SWtEkEPljTk%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;472&quot; height=&quot;96&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Network Plugin 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보면 get nodes의 결과에 Status가 NotReady인것을 확인 할 수있다.&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;a href=&quot;https://kubernetes.io/docs/concepts/cluster-administration/networking/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kubernetes.io/docs/concepts/cluster-administration/networking/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cluster network를 위해서 plugin을 설치해 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;여기서는 calico를 설치 하고자 한다.(version changed) move to weave&lt;br /&gt;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;weave&lt;/p&gt;
&lt;pre id=&quot;code_1675989970902&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml&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;위와 같이 calico를 설치한 후 Status가 모두 Running 및 Ready 상태가 된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw5Sxk/btrGVlB7xv2/Enxf9akCtHEmsjRy3vfdV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw5Sxk/btrGVlB7xv2/Enxf9akCtHEmsjRy3vfdV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw5Sxk/btrGVlB7xv2/Enxf9akCtHEmsjRy3vfdV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw5Sxk%2FbtrGVlB7xv2%2FEnxf9akCtHEmsjRy3vfdV0%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;643&quot; height=&quot;207&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K6131/btrGQwkicgI/rnwmUBHnFcAyWCwmtqvJg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K6131/btrGQwkicgI/rnwmUBHnFcAyWCwmtqvJg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K6131/btrGQwkicgI/rnwmUBHnFcAyWCwmtqvJg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK6131%2FbtrGQwkicgI%2FrnwmUBHnFcAyWCwmtqvJg0%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;693&quot; height=&quot;191&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;500&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;h2 data-ke-size=&quot;size26&quot;&gt;Local에서 Master Cluster 접근하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster에 접근하기 위한 방법은 인증서, ID/PW 등이 있는데, 로컬에 만드는 것이기 때문에 불필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master에 존재하는 ~/.kube/config 파일을 local computer에 있는 ~/.kube/config 파일로 overwrite 시키자.&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;sft를 이용해서 master이하에 있는 ./kube/config 파일을 local computer의 ~/.kube/config로 변경시키고 난 후의 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YiKgh/btrGQ57xBwp/igY73KcLMwpWlTc0abOlo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YiKgh/btrGQ57xBwp/igY73KcLMwpWlTc0abOlo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YiKgh/btrGQ57xBwp/igY73KcLMwpWlTc0abOlo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYiKgh%2FbtrGQ57xBwp%2FigY73KcLMwpWlTc0abOlo1%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;488&quot; height=&quot;308&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Local에서 master cluster를 Control 가능하게 되었다.&lt;/p&gt;</description>
      <category>AWS</category>
      <category>Kubernetes</category>
      <category>multipass</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/261</guid>
      <comments>https://enumclass.tistory.com/261#entry261comment</comments>
      <pubDate>Sun, 10 Jul 2022 16:54:47 +0900</pubDate>
    </item>
    <item>
      <title>AWS 요금 확인 및 리소스 정리하기</title>
      <link>https://enumclass.tistory.com/260</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결할 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에 요금이 계속 과금되고 있는 것을 확인했다. 젠장....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform이 일단 깨지고 나니까 Manual로 리소스를 정리했는데 아래와 같이 비용이 과금되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;1528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpxeph/btrGQbG2AM5/7SAjXrevDSBkLcBDMXsVk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpxeph/btrGQbG2AM5/7SAjXrevDSBkLcBDMXsVk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpxeph/btrGQbG2AM5/7SAjXrevDSBkLcBDMXsVk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpxeph%2FbtrGQbG2AM5%2F7SAjXrevDSBkLcBDMXsVk0%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;516&quot; height=&quot;249&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;1528&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;h2 data-ke-size=&quot;size26&quot;&gt;어느 리소스가 과금하는지 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Management 콘솔 우상단에 사용자 프로필을 클릭하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Billing Dashboard를 갈수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 Bills를 선택하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg3XEc/btrGQDjlobi/8xfg40HuKDKmPKRBsdmpmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg3XEc/btrGQDjlobi/8xfg40HuKDKmPKRBsdmpmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg3XEc/btrGQDjlobi/8xfg40HuKDKmPKRBsdmpmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg3XEc%2FbtrGQDjlobi%2F8xfg40HuKDKmPKRBsdmpmK%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;312&quot; height=&quot;202&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bills우측 Details를 확인하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2698&quot; data-origin-height=&quot;1048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSCesd/btrGS5Mtway/btkYYUl5dsF90wtXHwypDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSCesd/btrGS5Mtway/btkYYUl5dsF90wtXHwypDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSCesd/btrGS5Mtway/btkYYUl5dsF90wtXHwypDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSCesd%2FbtrGS5Mtway%2FbtkYYUl5dsF90wtXHwypDK%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;510&quot; height=&quot;198&quot; data-origin-width=&quot;2698&quot; data-origin-height=&quot;1048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부한다고 만들어&amp;nbsp; 놓은 Relational Database Service가 내돈 11달러를 먹고 있다. 망할...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2692&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zuSQa/btrGTygzji2/eJnwboFtqPgtdkg8kMbSe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zuSQa/btrGTygzji2/eJnwboFtqPgtdkg8kMbSe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zuSQa/btrGTygzji2/eJnwboFtqPgtdkg8kMbSe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzuSQa%2FbtrGTygzji2%2FeJnwboFtqPgtdkg8kMbSe0%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;668&quot; height=&quot;66&quot; data-origin-width=&quot;2692&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;US East Ohio를 가서 과금이 된 리소스를 삭제해 주자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/259&quot;&gt;2022.07.09 - [AWS] - RDS 리소스 삭제하기&lt;/a&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;Service Search를 이용해서 AWS Resource Group으로 이동한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blUIgH/btrGQa2re3u/aghtB6QYPmkkNvSGCKEa71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blUIgH/btrGQa2re3u/aghtB6QYPmkkNvSGCKEa71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blUIgH/btrGQa2re3u/aghtB6QYPmkkNvSGCKEa71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblUIgH%2FbtrGQa2re3u%2FaghtB6QYPmkkNvSGCKEa71%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;472&quot; height=&quot;228&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 Tag Editor를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Regions와 Resource Types를&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- All regions&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- All supported resource types&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHi91F/btrGQ0rNyhq/jTR9UiA9rQYiSviBorFqZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHi91F/btrGQ0rNyhq/jTR9UiA9rQYiSviBorFqZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHi91F/btrGQ0rNyhq/jTR9UiA9rQYiSviBorFqZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHi91F%2FbtrGQ0rNyhq%2FjTR9UiA9rQYiSviBorFqZK%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;527&quot; height=&quot;247&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우하단에 Search Resources를 클릭하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;1152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pPxxw/btrGQoUh3Mu/ePBhpAtys4zsGYsY0ygZd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pPxxw/btrGQoUh3Mu/ePBhpAtys4zsGYsY0ygZd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pPxxw/btrGQoUh3Mu/ePBhpAtys4zsGYsY0ygZd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpPxxw%2FbtrGQoUh3Mu%2FePBhpAtys4zsGYsY0ygZd0%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;585&quot; height=&quot;261&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그런데 이 리소스는 모두 과금이 되는걸까?&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH51Mm/btrGPI6J9GD/uwlP7fEGN7y9z1wFKg1Rn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH51Mm/btrGPI6J9GD/uwlP7fEGN7y9z1wFKg1Rn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH51Mm/btrGPI6J9GD/uwlP7fEGN7y9z1wFKg1Rn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH51Mm%2FbtrGPI6J9GD%2FuwlP7fEGN7y9z1wFKg1Rn0%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;2080&quot; height=&quot;96&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Load Balancing은 비용을 과금할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/products/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aws.amazon.com/ko/products/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Load Balancing을 조회해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭해서 상세를 확인 후 요금탭을 클릭하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I8ez3/btrGSyahMM5/BBP6EHLk60bDdM0uGU3Eu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I8ez3/btrGSyahMM5/BBP6EHLk60bDdM0uGU3Eu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I8ez3/btrGSyahMM5/BBP6EHLk60bDdM0uGU3Eu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI8ez3%2FbtrGSyahMM5%2FBBP6EHLk60bDdM0uGU3Eu1%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;442&quot; height=&quot;249&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 요금이 부과될 수 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5zrhH/btrGQwD9pDR/RlPzYuLKyokxxIccLOh661/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5zrhH/btrGQwD9pDR/RlPzYuLKyokxxIccLOh661/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5zrhH/btrGQwD9pDR/RlPzYuLKyokxxIccLOh661/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5zrhH%2FbtrGQwD9pDR%2FRlPzYuLKyokxxIccLOh661%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;315&quot; height=&quot;301&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간단위 Transaction단위, 연결단위 등.... 복잡하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기왕이면 조회된 모든 리소스 Type별 과금 정보를 보여줬으면 좋겠는데 그렇게 친절하지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령, NAT Gateway는 과금대상이지만 Internet Gateway는 비과금이다. 하지만 Elastic IP는 과금이다.&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;</description>
      <category>AWS</category>
      <category>AWS</category>
      <category>pricing</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/260</guid>
      <comments>https://enumclass.tistory.com/260#entry260comment</comments>
      <pubDate>Sat, 9 Jul 2022 14:46:28 +0900</pubDate>
    </item>
    <item>
      <title>RDS 리소스 삭제하기</title>
      <link>https://enumclass.tistory.com/259</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결하고자 하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDS Cluster의 삭제&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3098&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzzITw/btrGPpsvDgi/isReHoeN6KKwQfNSSL9h80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzzITw/btrGPpsvDgi/isReHoeN6KKwQfNSSL9h80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzzITw/btrGPpsvDgi/isReHoeN6KKwQfNSSL9h80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzzITw%2FbtrGPpsvDgi%2FisReHoeN6KKwQfNSSL9h80%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;3098&quot; height=&quot;352&quot; data-origin-width=&quot;3098&quot; data-origin-height=&quot;352&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;h2 data-ke-size=&quot;size26&quot;&gt;주의 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제를 위해서는 절대로 db cluster를 정지 시키면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에는 db instance가 삭제되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 db cluster를 먼저 정지 했다면, cluster의 상태가 stopped가 될때까지 기다렸다가, 다시 cluster를 start해야 한다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk4fv0/btrGS4GLTGo/4rkOCZckrFrnsFvdfndB8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk4fv0/btrGS4GLTGo/4rkOCZckrFrnsFvdfndB8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk4fv0/btrGS4GLTGo/4rkOCZckrFrnsFvdfndB8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk4fv0%2FbtrGS4GLTGo%2F4rkOCZckrFrnsFvdfndB8K%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;679&quot; height=&quot;244&quot; data-origin-width=&quot;1986&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;삭제하고자 하는 인스턴스를 선택하고 delete를 누른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKevv2/btrGT0cVKZy/Qjeh2Hsd1kG0IVzLl4qlDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKevv2/btrGT0cVKZy/Qjeh2Hsd1kG0IVzLl4qlDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKevv2/btrGT0cVKZy/Qjeh2Hsd1kG0IVzLl4qlDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKevv2%2FbtrGT0cVKZy%2FQjeh2Hsd1kG0IVzLl4qlDk%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;516&quot; height=&quot;221&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delete me를 type하고 삭제시켜 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2196&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brExCR/btrGQECw6N3/zTwDhiekzFzTkwVH9u9v90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brExCR/btrGQECw6N3/zTwDhiekzFzTkwVH9u9v90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brExCR/btrGQECw6N3/zTwDhiekzFzTkwVH9u9v90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrExCR%2FbtrGQECw6N3%2FzTwDhiekzFzTkwVH9u9v90%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;582&quot; height=&quot;136&quot; data-origin-width=&quot;2196&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&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;만약의 경우를 위해서 snapshot을 저장할지 물어본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca6Fji/btrGS318VJv/MssRSb4ZEHYG1BWAuhPjx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca6Fji/btrGS318VJv/MssRSb4ZEHYG1BWAuhPjx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca6Fji/btrGS318VJv/MssRSb4ZEHYG1BWAuhPjx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca6Fji%2FbtrGS318VJv%2FMssRSb4ZEHYG1BWAuhPjx1%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;536&quot; height=&quot;360&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전한 삭제를 원한다면 snapshot을 하지 말자. 돈나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba0MrU/btrGSynL7XI/hBKTVZCQdBGKfYDnplLpU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba0MrU/btrGSynL7XI/hBKTVZCQdBGKfYDnplLpU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba0MrU/btrGSynL7XI/hBKTVZCQdBGKfYDnplLpU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba0MrU%2FbtrGSynL7XI%2FhBKTVZCQdBGKfYDnplLpU1%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;464&quot; height=&quot;395&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터 이하 인스턴스가 모두 삭제되면 cluster는 자동으로 삭제 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2172&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0B2TQ/btrGP42Qoaq/Zot8BlT8pGhIFbRvjqk2Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0B2TQ/btrGP42Qoaq/Zot8BlT8pGhIFbRvjqk2Mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0B2TQ/btrGP42Qoaq/Zot8BlT8pGhIFbRvjqk2Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0B2TQ%2FbtrGP42Qoaq%2FZot8BlT8pGhIFbRvjqk2Mk%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;648&quot; height=&quot;116&quot; data-origin-width=&quot;2172&quot; data-origin-height=&quot;390&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;</description>
      <category>AWS</category>
      <category>AWS</category>
      <category>RDS</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/259</guid>
      <comments>https://enumclass.tistory.com/259#entry259comment</comments>
      <pubDate>Sat, 9 Jul 2022 14:10:36 +0900</pubDate>
    </item>
    <item>
      <title>React JS Old Browser &amp;quot;undefined&amp;quot; Errors</title>
      <link>https://enumclass.tistory.com/258</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;npx create-react-app&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 사용해서 만든 앱으로 old Browser를 지원 하기 위해서 browser list버전을 아무리 내려 보아도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Uncaught&amp;nbsp;TypeError:&amp;nbsp;undefined&amp;nbsp;is&amp;nbsp;not&amp;nbsp;a&amp;nbsp;function&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;한참 후에 알았는데 발생 사유는 Object.assign이 undefined 되어 있기 때문이다.&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;package.json에 있는 browserslist를 통해서 old browser를 설정하는 내용은 다루지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설정을 했음에도 undefined에러나 나는 경우를 여기에서 다룬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bntY9U/btrFqsw8Ako/rCJNKq7on9KZg1o9G7xDp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bntY9U/btrFqsw8Ako/rCJNKq7on9KZg1o9G7xDp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bntY9U/btrFqsw8Ako/rCJNKq7on9KZg1o9G7xDp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbntY9U%2FbtrFqsw8Ako%2FrCJNKq7on9KZg1o9G7xDp1%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;399&quot; height=&quot;381&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&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;&quot;react-scripts&quot;의 버전을 의심해 보아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/create-react-app/tree/main/packages/react-scripts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/facebook/create-react-app/tree/main/packages/react-scripts&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-scripts는 create-react-app을 통해서 만들어진 프로젝트를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webpack과 babel을 통해서 resource 번들링 및 Browser Compatibility를 처리해주는 모듈이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AZwsk/btrFq85NmAj/jwrTHEsPyrGCFPqLZpKhUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AZwsk/btrFq85NmAj/jwrTHEsPyrGCFPqLZpKhUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AZwsk/btrFq85NmAj/jwrTHEsPyrGCFPqLZpKhUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAZwsk%2FbtrFq85NmAj%2FjwrTHEsPyrGCFPqLZpKhUK%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;431&quot; height=&quot;428&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-scripts의 1.1.5와 2.0.0의 차이를 보면 polyfills.js가 2.0.0 이후로 더이상 사용 되지 않는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이로 인해서 특정 old browser에서는 오류가 나는 것으로 짐작된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMAiJi/btrFt1yi30c/fqEOLJrCfTYs1MJv6c24b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMAiJi/btrFt1yi30c/fqEOLJrCfTYs1MJv6c24b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMAiJi/btrFt1yi30c/fqEOLJrCfTYs1MJv6c24b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMAiJi%2FbtrFt1yi30c%2FfqEOLJrCfTYs1MJv6c24b0%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;407&quot; height=&quot;333&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 5.0.1의 최신 버전을 1.1.5로 변경 후 테스트를 해보면 기존 old browser에서 정상적으로 작동하는 것을 확인할 수 있다.&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;만약에 Object.assign이 문제라는게 확실 하다면 아래와 같이 처리해 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'object-assign' package를 추가 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1655883477383&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;test&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;private&quot;: true,
  &quot;dependencies&quot;: {
    &quot;@testing-library/jest-dom&quot;: &quot;^5.16.4&quot;,
    &quot;@testing-library/react&quot;: &quot;^13.3.0&quot;,
    &quot;@testing-library/user-event&quot;: &quot;^13.5.0&quot;,
    &quot;react&quot;: &quot;^18.2.0&quot;,
    &quot;react-app-polyfill&quot;: &quot;^3.0.0&quot;,
    &quot;react-dom&quot;: &quot;^18.2.0&quot;,
    &quot;react-scripts&quot;: &quot;5.0.1&quot;,
    &quot;web-vitals&quot;: &quot;^2.1.4&quot;,
    &quot;object-assign&quot;: &quot;4.1.1&quot;
  },
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;react-scripts start&quot;,
    &quot;build&quot;: &quot;react-scripts build&quot;,
    &quot;test&quot;: &quot;react-scripts test&quot;,
    &quot;eject&quot;: &quot;react-scripts eject&quot;
  },
  &quot;eslintConfig&quot;: {
    &quot;extends&quot;: [
      &quot;react-app&quot;,
      &quot;react-app/jest&quot;
    ]
  },
  &quot;browserslist&quot;: [
      &quot;chrome 39&quot;
    ]
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Package 추가 후&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;js파일 하나를 만들어서 index.js 가장 앞에 import 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK87oy/btrFt9J1vRi/lDgYNlSImk3NrbrIkm58J1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK87oy/btrFt9J1vRi/lDgYNlSImk3NrbrIkm58J1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK87oy/btrFt9J1vRi/lDgYNlSImk3NrbrIkm58J1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK87oy%2FbtrFt9J1vRi%2FlDgYNlSImk3NrbrIkm58J1%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;621&quot; height=&quot;91&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCh3bl/btrFqrd4Joe/F6KKiiWHFdWZtIkbiNnpP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCh3bl/btrFqrd4Joe/F6KKiiWHFdWZtIkbiNnpP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCh3bl/btrFqrd4Joe/F6KKiiWHFdWZtIkbiNnpP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCh3bl%2FbtrFqrd4Joe%2FF6KKiiWHFdWZtIkbiNnpP0%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;476&quot; height=&quot;145&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&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;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;node_moduls 폴더를 모두 삭제&lt;/li&gt;
&lt;li&gt;package-lock.json 파일 삭제&lt;/li&gt;
&lt;li&gt;npm install&lt;/li&gt;
&lt;/ol&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;아주 혹시나 해서 polyfill을 적용하고 react-scripts를 최신 버전으로 변경해 봤지만 오류는 동일했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이스북은 어떻게 지원하는지 알아보려고 했는데, 지원되지 않는 브라우저로 손절 했다.&lt;/p&gt;
&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;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ch5iR/btrFsZOBwMp/QM5BgRcrWHuD3SlJE1uxJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ch5iR/btrFsZOBwMp/QM5BgRcrWHuD3SlJE1uxJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ch5iR/btrFsZOBwMp/QM5BgRcrWHuD3SlJE1uxJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCh5iR%2FbtrFsZOBwMp%2FQM5BgRcrWHuD3SlJE1uxJ0%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;686&quot; height=&quot;150&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;old browser는 이제 그만 했으면 좋겠는데, 안될듯 ㅎㅎㅎ&lt;/p&gt;</description>
      <category>Web</category>
      <category>chrome</category>
      <category>ec5</category>
      <category>old browser</category>
      <category>React</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/258</guid>
      <comments>https://enumclass.tistory.com/258#entry258comment</comments>
      <pubDate>Wed, 22 Jun 2022 15:35:14 +0900</pubDate>
    </item>
    <item>
      <title>kubectl 1.24 error: exec plugin: invalid apiVersion &amp;quot;client.authentication.k8s.io/v1alpha1&amp;quot;</title>
      <link>https://enumclass.tistory.com/257</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;eks로 만든 cluster를 kubectl이 config map을 로드하지 못할 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랫만에 eks로 cluster를 만든 후 kubectl을 통해 aws의 kubernetes config를 불러 오려고 했는데 아래와 같이 오류가 났다.&lt;/p&gt;
&lt;pre id=&quot;code_1654944018088&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ kubectl version
error: exec plugin: invalid apiVersion &quot;client.authentication.k8s.io/v1alpha1&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 kubectl 12.4.x 버전에서 eks와 연동해서 사용할 경우 발생하는 오류라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/aws/aws-cli/issues/6920&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/aws/aws-cli/issues/6920&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 사람들이 kubectl 버전을 12.3.6으로 내리고 문제가 해결 되었다고 한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;kubectl v12.3.6을 다운받는다.&lt;/h3&gt;
&lt;pre id=&quot;code_1654944162259&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ curl -LO https://dl.k8s.io/release/v1.23.6/bin/linux/amd64/kubectl
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   154  100   154    0     0    631      0 --:--:-- --:--:-- --:--:--   631
100 44.4M  100 44.4M    0     0  21.7M      0  0:00:02  0:00:02 --:--:-- 25.5M&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;다운받은 kubectl을 기존 kubectl 실행 파일과 변경해 준다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo&amp;nbsp;install&amp;nbsp;-o&amp;nbsp;root&amp;nbsp;-g&amp;nbsp;root&amp;nbsp;-m&amp;nbsp;0755&amp;nbsp;kubectl&amp;nbsp;/usr/local/bin/kubectl&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654944203364&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl 
steven@steven-ubuntu:~$ kubectl version
Client Version: version.Info{Major:&quot;1&quot;, Minor:&quot;23&quot;, GitVersion:&quot;v1.23.6&quot;, GitCommit:&quot;ad3338546da947756e8a88aa6822e9c11e7eac22&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2022-04-14T08:49:13Z&quot;, GoVersion:&quot;go1.17.9&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}
Server Version: version.Info{Major:&quot;1&quot;, Minor:&quot;21+&quot;, GitVersion:&quot;v1.21.12-eks-a64ea69&quot;, GitCommit:&quot;d4336843ba36120e9ed1491fddff5f2fec33eb77&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2022-05-12T18:29:27Z&quot;, GoVersion:&quot;go1.16.15&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;aws eks config map을 최신화 시켜준다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1654944339276&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ aws eks update-kubeconfig --region &amp;lt;&amp;lt;region_name&amp;gt;&amp;gt; --name &amp;lt;&amp;lt;cluster_name&amp;gt;&amp;gt;
Updated context arn:aws:eks:us-east-2:144965779334:cluster/udacity-cluster in /home/steven/.kube/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;~/.kube/config 디렉토리에 eks의 context가 업데이트 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl config get-context&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1654944471273&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ kubectl config get-contexts
CURRENT   NAME                                                         CLUSTER                                                      AUTHINFO                                                     NAMESPACE
*         arn:aws:eks:us-east-2:144965779334:cluster/udacity-cluster   arn:aws:eks:us-east-2:144965779334:cluster/udacity-cluster   arn:aws:eks:us-east-2:144965779334:cluster/udacity-cluster&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl get node&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1654944502215&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;steven@steven-ubuntu:~$ kubectl get node
NAME                                        STATUS   ROLES    AGE   VERSION
ip-10-100-3-85.us-east-2.compute.internal   Ready    &amp;lt;none&amp;gt;   27m   v1.21.12-eks-5308cf7&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AWS</category>
      <category>EKS</category>
      <category>Kubernetes</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/257</guid>
      <comments>https://enumclass.tistory.com/257#entry257comment</comments>
      <pubDate>Sat, 11 Jun 2022 19:49:04 +0900</pubDate>
    </item>
    <item>
      <title>Visual Studio Code Live Server SPA url mapping 설정 하기</title>
      <link>https://enumclass.tistory.com/256</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/235&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.09.05 - [Web] - Vanilla javascript URL Router 만들기 (web components)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 포스트를 보면 다음과 같은 말을 했었습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;url에 사용자가 직접 넣으면 오류가 날것이다. 해당 서비스 접근을 통해서 해당 url에 화면이 잘 나오는 것을 분명히 확인했는데 어떤 차이일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그것은 해당 url이 server에 request를 했느냐의 차이가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 언급했듯이 SPA는 기본적으로 여러개의 url page를 하나의 url로 call 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 request mapping을 browser url에 직접 넣게 되면 오류를 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 해당 request를 받는 server쪽에서 url을 어떤 resource와 mapping해야하는지 알 수 없기 때문입니다.&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;그래서 nodejs experss 같은 server를 직접 사용하는 경우에는 모든 request를 index.html로 return하도록 설정하고 개발을 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 visual studio code의 live server를 이용해서 쉽게 해결할 방법은 없을까요?&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;Live server setting을 수정해 줍니다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAQFHZ/btrvdvXNnUb/KPeKwPFYequlWnR0jKmlk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAQFHZ/btrvdvXNnUb/KPeKwPFYequlWnR0jKmlk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAQFHZ/btrvdvXNnUb/KPeKwPFYequlWnR0jKmlk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAQFHZ%2FbtrvdvXNnUb%2FKPeKwPFYequlWnR0jKmlk1%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;1844&quot; height=&quot;560&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설치되어 있는 live Server페이지에서 설정 버튼을 click해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNQtEw/btrveLTAM85/3IekFYAuRlXjLXZMFCfmC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNQtEw/btrveLTAM85/3IekFYAuRlXjLXZMFCfmC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNQtEw/btrveLTAM85/3IekFYAuRlXjLXZMFCfmC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNQtEw%2FbtrveLTAM85%2F3IekFYAuRlXjLXZMFCfmC1%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;406&quot; height=&quot;255&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장설정을 선택해 주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zM7w9/btrveMyaX1R/NgVhKEYnq8dEsZz6IwRj31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zM7w9/btrveMyaX1R/NgVhKEYnq8dEsZz6IwRj31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zM7w9/btrveMyaX1R/NgVhKEYnq8dEsZz6IwRj31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzM7w9%2FbtrveMyaX1R%2FNgVhKEYnq8dEsZz6IwRj31%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; alt=&quot;Live Server settings&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;188&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;settings.json에서 편집을 선택해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ADLuC/btrveMEVG4y/dfhQnucZOyc3H3WaXlkT01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ADLuC/btrveMEVG4y/dfhQnucZOyc3H3WaXlkT01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ADLuC/btrveMEVG4y/dfhQnucZOyc3H3WaXlkT01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FADLuC%2FbtrveMEVG4y%2FdfhQnucZOyc3H3WaXlkT01%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;418&quot; height=&quot;274&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;liveServer.settings.file&quot;: &quot;index.html&quot;&lt;/blockquote&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0v51T/btrviJgqIOt/9NPkGQE7pcH5NZ6qyySmmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0v51T/btrviJgqIOt/9NPkGQE7pcH5NZ6qyySmmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0v51T/btrviJgqIOt/9NPkGQE7pcH5NZ6qyySmmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0v51T%2FbtrviJgqIOt%2F9NPkGQE7pcH5NZ6qyySmmk%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;587&quot; height=&quot;230&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우하단에 저곳을 눌러서 서버를 정지시켜 주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcka8S/btrvbg08u9g/hYrttZ5mcErvE5TdKgcvg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcka8S/btrvbg08u9g/hYrttZ5mcErvE5TdKgcvg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcka8S/btrvbg08u9g/hYrttZ5mcErvE5TdKgcvg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcka8S%2Fbtrvbg08u9g%2FhYrttZ5mcErvE5TdKgcvg1%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;293&quot; height=&quot;125&quot; data-origin-width=&quot;389&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Go Live를 click해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제부터 어떤 url을 browser에 넣어도 index.html을 Live Server가 불러주게 됩니다.&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;아참 하나더! 꼭 html에서 src link를 상대경로가 아닌 절대 경로로 변경해 주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 html에&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1646577420542&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;./App.js&quot; type=&quot;module&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정이 되어있다면&lt;/p&gt;
&lt;pre id=&quot;code_1646577436907&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;/App.js&quot; type=&quot;module&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 절대 경로로 파일을 link하게 해주세요.&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;설정 Test 해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하기 github는 Router Component를 Test해보기 위한 Demo입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/RouterComponent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/RouterComponent&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clone 하시면 아래와 같이 6개의 파일을 보실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4SCuZ/btrviIaIumF/OPxHcOzqO2meAKbFDkN9ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4SCuZ/btrviIaIumF/OPxHcOzqO2meAKbFDkN9ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4SCuZ/btrviIaIumF/OPxHcOzqO2meAKbFDkN9ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4SCuZ%2FbtrviIaIumF%2FOPxHcOzqO2meAKbFDkN9ZK%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;126&quot; height=&quot;148&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.html을 live server에서 실행 시켜보세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbUOT/btrvkGqcMJG/6jDmYr5HVNAidhKfS0kTMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbUOT/btrvkGqcMJG/6jDmYr5HVNAidhKfS0kTMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbUOT/btrvkGqcMJG/6jDmYr5HVNAidhKfS0kTMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbUOT%2FbtrvkGqcMJG%2F6jDmYr5HVNAidhKfS0kTMk%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;443&quot; height=&quot;354&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c40lgp/btrvbh6MQpl/tzWd5KxUCNriyvJF4fem11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c40lgp/btrvbh6MQpl/tzWd5KxUCNriyvJF4fem11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c40lgp/btrvbh6MQpl/tzWd5KxUCNriyvJF4fem11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc40lgp%2Fbtrvbh6MQpl%2FtzWd5KxUCNriyvJF4fem11%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;294&quot; height=&quot;242&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://127.0.0.1:5500/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 browser에 직접 넣으셨을 때 위에처럼 나오면 정상입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ziMZK/btrviHCU1Jq/ObdkXdCTkbjgJcDViP3Jm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ziMZK/btrviHCU1Jq/ObdkXdCTkbjgJcDViP3Jm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ziMZK/btrviHCU1Jq/ObdkXdCTkbjgJcDViP3Jm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FziMZK%2FbtrviHCU1Jq%2FObdkXdCTkbjgJcDViP3Jm0%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;315&quot; height=&quot;185&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://127.0.0.1:5500/superman&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도 직접 넣어 보세요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yj9Rd/btrveK8fp6H/WJen5TR09PNLvsBoah5r1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yj9Rd/btrveK8fp6H/WJen5TR09PNLvsBoah5r1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yj9Rd/btrveK8fp6H/WJen5TR09PNLvsBoah5r1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyj9Rd%2FbtrveK8fp6H%2FWJen5TR09PNLvsBoah5r1K%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;317&quot; height=&quot;230&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이제 마음편히 Visual Studio code에서 SPA를 개발할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 위의 github코드가 어떻게 작동하는지는 맨처음 링크해둔 url을 참고하세요.&lt;/p&gt;</description>
      <category>Web</category>
      <category>Live Server</category>
      <category>Router</category>
      <category>spa</category>
      <category>Visual Studio Code</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/256</guid>
      <comments>https://enumclass.tistory.com/256#entry256comment</comments>
      <pubDate>Sun, 6 Mar 2022 23:52:17 +0900</pubDate>
    </item>
    <item>
      <title>Intellij $'\r': command not found 오류 (LF, CR)</title>
      <link>https://enumclass.tistory.com/255</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux나 mac 또는 windows에서 사용하는 shell script를 intellij에서 실행하려고 하면 아래와 같은 오류가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1646108735742&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;build.sh: line 2: $'\r': command not found
build.sh: line 4: $'\r': command not found
error: invalid flag: softdrink/module-info.java
Usage: javac &amp;lt;options&amp;gt; &amp;lt;source files&amp;gt;
use --help for a list of possible options
 : no such file or directory
build.sh: line 9: $'\r': command not found&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발생 사유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux/mac에서 사용하는 파일의 format과 windows에서 사용하는 format이 달라서 나는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서 한 라인의 종료를 어떻게 표시하느냐에 따라서 위와 같은 오류가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unix : Line Feed 방식 (LF)으로 라인을 분리한다. \n&lt;/li&gt;
&lt;li&gt;mac classic : Single Carriage Return 방식 (CR)으로 라인을 분리한다. \r&lt;/li&gt;
&lt;li&gt;windows : Line Feed와 Single Carriage Return (CR/LF) 을 모두 사용한다. \r\n&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;윈도우에 맞게 파일 format을 수정해주는 일은 귀찮다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;그래서 Intellij에서는 Change Line separators 기능을 제공해준다.&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;span style=&quot;background-color: #ffffff;&quot;&gt;대응 방법&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;아래 이미지를 보면 CR방식의 파일을 CRLF방식으로 보고 script를 실행시키자 오류가 나는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이런 오류가 나는지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1646116163076&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ od -c build.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위는 octal dump 명령어인 od 명령어를 사용할 수 있다는 전제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;od명령어는 unix나 linux, windows에서 일반적으로 사용 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1646116295936&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0000000   /   b   i   n   /   r   m       -   r   f       o   u   t   p
0000020   u   t  \r  \n  \r  \n   m   k   d   i   r       -   p       o
0000040   u   t   p   u   t   /   m   l   i   b  \r  \n  \r  \n   m   k
0000060   d   i   r       -   p       o   u   t   p   u   t   /   c   l
0000100   a   s   s   e   s  \r  \n   j   a   v   a   c       -   d
0000120   o   u   t   p   u   t   /   c   l   a   s   s   e   s       `
0000140   f   i   n   d       s   o   f   t   d   r   i   n   k       -
0000160   n   a   m   e       *   .   j   a   v   a   `  \r  \n   j   a
0000200   r       -   c       -   f       o   u   t   p   u   t   /   m
0000220   l   i   b   /   s   o   f   t   d   r   i   n   k   .   j   a
0000240   r       -   C       o   u   t   p   u   t   /   c   l   a   s
0000260   s   e   s       .  \r  \n   /   b   i   n   /   r   m       -
0000300   r   f       o   u   t   p   u   t   /   c   l   a   s   s   e
0000320   s  \r  \n  \r  \n   m   k   d   i   r       -   p       o   u&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 hex로 글자를 확인해 보면 \r\n이 사용되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 script 오류를 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'\r':&amp;nbsp;command&amp;nbsp;not&amp;nbsp;found&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 인식하지 못하는 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일 script 실행기는 \n은 인식이 가능한 것으로 보임으로 LF로 파일로 변환시켜서 실행 시키면 될 듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFV2Rg/btruO97oXGU/oXbxp5BOrZXkwB3fdo4xek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFV2Rg/btruO97oXGU/oXbxp5BOrZXkwB3fdo4xek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFV2Rg/btruO97oXGU/oXbxp5BOrZXkwB3fdo4xek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFV2Rg%2FbtruO97oXGU%2FoXbxp5BOrZXkwB3fdo4xek%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;484&quot; height=&quot;454&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우하단에 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;Change Line separators 기능을 이용해서 LF로 파일 포멧을 수정해 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1646116578572&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ od -c build.sh
0000000   /   b   i   n   /   r   m       -   r   f       o   u   t   p
0000020   u   t  \n  \n   m   k   d   i   r       -   p       o   u   t
0000040   p   u   t   /   m   l   i   b  \n  \n   m   k   d   i   r
0000060   -   p       o   u   t   p   u   t   /   c   l   a   s   s   e
0000100   s  \n   j   a   v   a   c       -   d       o   u   t   p   u
0000120   t   /   c   l   a   s   s   e   s       `   f   i   n   d
0000140   s   o   f   t   d   r   i   n   k       -   n   a   m   e
0000160   *   .   j   a   v   a   `  \n   j   a   r       -   c       -&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 \r\n 으로 되어있던 라인종료를 '\n'으로 변경한 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실행해 보면 잘 작동하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1333&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BUSGE/btrutlaJzz0/hSilBKAKpywvtT3XTGmDiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BUSGE/btrutlaJzz0/hSilBKAKpywvtT3XTGmDiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BUSGE/btrutlaJzz0/hSilBKAKpywvtT3XTGmDiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBUSGE%2FbtrutlaJzz0%2FhSilBKAKpywvtT3XTGmDiK%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;602&quot; height=&quot;105&quot; data-origin-width=&quot;1333&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>intellij</category>
      <category>CR</category>
      <category>LF</category>
      <category>LFCR</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/255</guid>
      <comments>https://enumclass.tistory.com/255#entry255comment</comments>
      <pubDate>Tue, 1 Mar 2022 15:38:30 +0900</pubDate>
    </item>
    <item>
      <title>JavaFX Unrecognized option: --module-path</title>
      <link>https://enumclass.tistory.com/254</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 환경에서 Maven의 javafx-maven-plugin을 사용하면 javafx 모듈 설정을 별도로 해주지 않아도 실행 시킬수가 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/168&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.05.28 - [JAVA] - javaFX java version 11이상에서 실행하기&lt;/a&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;그런데 IDE의 실행이 java 11버전 이상인데도 불구하고 하기와같이 오류가 나면서 실행이 되지 않는 경우가 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Unrecognized option: --module-path&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 java 8과 java 11혹은 그 이상 버전의 runtime환경이 꼬여서 이렇게 될 수 있다.&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;Windows의 Java Configuration을 수정해 주자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JAVA_HOME 기준으로 java를 사용하는 방식으로 windows 환경을 사용한다면 상관이 없지만, Orcle의 JRE를 사용하는 경우는 java version이 IDE와 Conflict가 날수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nVjmY/btruxkiJ2At/y2HgmxOYioKDF3HWahZARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nVjmY/btruxkiJ2At/y2HgmxOYioKDF3HWahZARk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nVjmY/btruxkiJ2At/y2HgmxOYioKDF3HWahZARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnVjmY%2FbtruxkiJ2At%2Fy2HgmxOYioKDF3HWahZARk%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; alt=&quot;Configure Java&quot; loading=&quot;lazy&quot; width=&quot;244&quot; height=&quot;424&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Configure Java를 실행 시켜주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s52FN/btruwJBH8GX/Cwr8iIeyRu0FK8WKQiZqfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s52FN/btruwJBH8GX/Cwr8iIeyRu0FK8WKQiZqfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s52FN/btruwJBH8GX/Cwr8iIeyRu0FK8WKQiZqfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs52FN%2FbtruwJBH8GX%2FCwr8iIeyRu0FK8WKQiZqfK%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;346&quot; height=&quot;333&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 1.8 version을 사용하고 있는것을 볼수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 버튼을 눌러서 우리가 사용하고자 하는 java 11을 추가해 주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOR4dd/btruITpVys2/TyYWK4IfzWtAF4suPGkkvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOR4dd/btruITpVys2/TyYWK4IfzWtAF4suPGkkvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOR4dd/btruITpVys2/TyYWK4IfzWtAF4suPGkkvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOR4dd%2FbtruITpVys2%2FTyYWK4IfzWtAF4suPGkkvK%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;1472&quot; height=&quot;413&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위와 같이 기존 1.8 사용을 모두 uncheck하고 11버전에 사용을 check해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Version 확인방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새롭게 명령 프롬프트를 실행하고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646023817086&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java -version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위의 명령어를 실행하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZiHKG/btruHx1JVTm/HC5vTHnzYPZO3LAWuiokNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZiHKG/btruHx1JVTm/HC5vTHnzYPZO3LAWuiokNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZiHKG/btruHx1JVTm/HC5vTHnzYPZO3LAWuiokNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZiHKG%2FbtruHx1JVTm%2FHC5vTHnzYPZO3LAWuiokNk%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;628&quot; height=&quot;138&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 11 version이 default java로 설정된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;831&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/evaq07/btruAr2KR0Z/x2Z487v2GJcINxN0cJFQqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/evaq07/btruAr2KR0Z/x2Z487v2GJcINxN0cJFQqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/evaq07/btruAr2KR0Z/x2Z487v2GJcINxN0cJFQqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fevaq07%2FbtruAr2KR0Z%2Fx2Z487v2GJcINxN0cJFQqK%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; alt=&quot;mvn javafx:run&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;355&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;831&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 javafx의 plugin을 이용해서 실행시킬 수 있다.&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;Maven javafx Plugin 설정방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;maven을 사용해서 module 설정하지 않고 plugin으로 javafx를 실행 시키는 방법은 아래 link를 참고하면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://openjfx.io/openjfx-docs/#maven&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://openjfx.io/openjfx-docs/#maven&lt;/a&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <category>JavaFX</category>
      <category>maven</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/254</guid>
      <comments>https://enumclass.tistory.com/254#entry254comment</comments>
      <pubDate>Mon, 28 Feb 2022 13:58:07 +0900</pubDate>
    </item>
    <item>
      <title>Count Array Pairs Divisible by K</title>
      <link>https://enumclass.tistory.com/253</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/count-array-pairs-divisible-by-k/&quot;&gt;Count Array Pairs Divisible by K&lt;/a&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;integer의 배열이 nums가 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 nums에서 2개의 pair를 선택해서 곱한 결과가 k로 나뉘어지는 경우가 몇 번인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, pair가 (1,2)이면 (2,1)과 동일하다.&amp;nbsp;&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;h3 data-ke-size=&quot;size23&quot;&gt;Brute force 접근&lt;/h3&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nums = [1,2,3,4,5], k = 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 주어지면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1,2],[1,3],[1,4],[1,5],[2,3],[2,4],[2,5],[3,4],[3,5],[4,5]&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;2,3,4,5,6,8,10,12,15,20&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중에서 2,4,6,8,10,12,20이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 경우의 수를 따지고 k로 % 연산을 진행할 경우 최대 $ O{lengthOfNums^2} $ 이 된다.&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;GCD를 이용한 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 GCD가 무었이고 어떻게 개발하는 지는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/184&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.06.27 - [Problem Solving] - Greatest common divisor (gcd) 최대 공약수 구하기, Euclidean algorithm 유클리드 알고리즘&lt;/a&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2개의 pair의 곱이 k로 나뉘어 지는가?&lt;/li&gt;
&lt;li&gt;그 만들어진 곱이 k의 구성원을 포함하고 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 예를 들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 (9 * 10)의 페어가 있다고 생각해 보자. 이 페어는 90을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ 90 = 3 * 3 * 2 * 5 $&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;90은 3 2개와 2 하나 그리고 5하나로 이루어 진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;90은 3으로도 나누어질 수 있고 3두개로도 나누어질 수 있다. 3 두개 2한개로도 나누어질 수 있도 있고 3두개 2하나 5하나로도 나누어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 90을 구성하는 값들로만 나누어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 k가 저 구성원에 속한다면 90이라는 페어는 k로 나누어진다고 생각할 수 있다.&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;이런 속성을 이용해서 다음과 같이 k와 pair의 관계를 생각해 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzstc/btrtTMynlWm/avFnR1DBShkf2jcujMrmKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzstc/btrtTMynlWm/avFnR1DBShkf2jcujMrmKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzstc/btrtTMynlWm/avFnR1DBShkf2jcujMrmKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkzstc%2FbtrtTMynlWm%2FavFnR1DBShkf2jcujMrmKK%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;221&quot; height=&quot;211&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 k값이 70이고 pair중 하나가 60이라고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;70으로 나누어지는 pair를 찾기위해서 60은 어떤 소인수를 갖는 상대방을 찾아야 하는가?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qeggo/btrtIOEiUgg/tI1yrG000r63pKE6bngtD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qeggo/btrtIOEiUgg/tI1yrG000r63pKE6bngtD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qeggo/btrtIOEiUgg/tI1yrG000r63pKE6bngtD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQeggo%2FbtrtIOEiUgg%2FtI1yrG000r63pKE6bngtD0%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;341&quot; height=&quot;311&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 하나 소인수로 7을 갖고있는 상대방이 있기만 하면 된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;k를 구성하는 소인수는 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 k를 구성하는 모든 소인수의 곱을 구해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 7,5,2로 주어진 70이 있는데 이 것은 10*7 일수도 있고 14*5 일수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 2개의 곱으로 70을 만들어낼 수 있는 모든 경우의 수를 찾고 pair와 비교해서 나머지 부족한 곱을 찾으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명이 조금 이상한데 아래 내용을 보면 이해하기 쉬울 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r57zy/btrtG7SeBw6/BpwkO7X4mWQY8uqX3DrF80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r57zy/btrtG7SeBw6/BpwkO7X4mWQY8uqX3DrF80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r57zy/btrtG7SeBw6/BpwkO7X4mWQY8uqX3DrF80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr57zy%2FbtrtG7SeBw6%2FBpwkO7X4mWQY8uqX3DrF80%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;551&quot; height=&quot;191&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;70으로 나누어지는 곱은 77과 30이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐 하면 77의 구성 요소에 7이 있고, 30의 구성요소에 10(5*2)이 있기 때문이다.&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;구성요소를 이용해서 2개의 pair 곱 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k인 70의 2개 pair는 어떻게 찾을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부터 70까지 모든 값을 %로 모듈레이션 해주면 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function getDivisor(k){
    let arr = new Array();

    for(let i = 1; i &amp;lt;= k; i++){
        if(k % i === 0){
            arr.push(i);
        }
    }
    return arr;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;70을 divisor하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1,&amp;nbsp;&amp;nbsp;2,&amp;nbsp;&amp;nbsp;5,&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;7, 10&lt;/span&gt;, 14, 35, 70]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 얻을 수 있습니다. pair a가 7를 갖어 갔으면 pair b는 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;이제 pair a의 값이 77일때 77이 어떤 구성요소를 70과 공유하는지를 알아야 한다.&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;2개 값의 GCD 계산하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 2값의 공통 곱값을 찾는 방법은 GCD가 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function gcd(a,b){ //a가 크고, b가 작은거
    if(b === 0) return a;
    if(a &amp;lt; b) {
        return gcd(b,a);
    } else {
        return gcd(b, a%b)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세한 내용은 위의 링크를 참고 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 공통수를 찾고 k를 나누면 우리가 목표로 하는 pair b의 구성요소를 찾을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;let val = Math.floor(k / gcd(k, num));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k가 70이고 gcd(70, 77)은 7임으로 우리가 찾아야할 구성요소는 10이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 10을 구성요소로 갖는 nums의 갯수를 찾기만 하면 답이 된다. 그런데 10은 무슨 구성요소일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10도 결국은 [1,&amp;nbsp;&amp;nbsp;2,&amp;nbsp;&amp;nbsp;5,&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;7, 10&lt;/span&gt;, 14, 35, 70] 이 구성요소 주 하나가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 어떤 주어진 num과 70의 구성 요소간에 share하는 값을 미리 저장해 놓는다면 계산하기 편하다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;for(const divisor of divisors){
    if(num % divisor === 0){
        counters[divisor]++;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 주어진 값이 77이고 77은 7과 10으로 나누어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counter[7]++;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counter[10]++;로 표현 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 10을 갖고있는 pair a가 들어온다면 7을 갖고 있는 num의 갯수를 더하면 답이되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7을 갖고 있는 pair a가 들어오면 10을 갖고 있는 num의 갯수를 더하면 답이된다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;result += counters[val];&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/theyoung/38e2b3307f706ee925fe1a63cade149f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/theyoung/38e2b3307f706ee925fe1a63cade149f&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1645358346403&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var coutPairs = function(nums, k) {
    let divisors = getDivisor(k);
    let counters = new Array(k+1).fill(0);
    let result = 0;

    for(const num of nums){
        let val = Math.floor(k / gcd(k, num));
        result += counters[val];

        for(const divisor of divisors){
            if(num % divisor === 0){
                counters[divisor]++;
            }
        }
        console.log(counters)
    }

    return result;

};


function gcd(a,b){ //a가 크고, b가 작은거
    if(b === 0) return a;
    if(a &amp;lt; b) {
        return gcd(b,a);
    } else {
        return gcd(b, a%b)
    }
}

function getDivisor(k){
    let arr = new Array();

    for(let i = 1; i &amp;lt;= k; i++){
        if(k % i === 0){
            arr.push(i);
        }
    }
    return arr;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도는 기본적으로 nums를 순회함으로 $ O(N) $ , GCD의 Time complexity는 log(min(a,b))임으로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ O(N * log(min(a,b))) $ 가 된다. 여기에 divisor를 순회함으로 $ O(N * log(min(a,b)) * numberOfdivisors) $가 최종 시간 복잡도가 된다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>Count Array Pairs Divisible by K</category>
      <category>GCD</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/253</guid>
      <comments>https://enumclass.tistory.com/253#entry253comment</comments>
      <pubDate>Sun, 20 Feb 2022 21:04:23 +0900</pubDate>
    </item>
    <item>
      <title>Intellij Spring boot Hot Swap / Hot Deploy 설정하기</title>
      <link>https://enumclass.tistory.com/252</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intellij 2021버전 이후 Intellij에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring boot java class 파일이 수정되었을 때 Hot Swap or Hot Deploy를 진행하게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Maven or Gradle 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Hot Swap 설정을 위해서는 Maven 또는 Gradle에&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;spring-boot-devtools를 추가해 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;maven인경우&lt;/p&gt;
&lt;pre id=&quot;code_1645021667212&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-devtools&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle인경우&lt;/p&gt;
&lt;pre id=&quot;code_1645021770301&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-devtools:2.6.3'&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;Intellij 설정&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Mh0G/btrtsVKYFBs/MApgmvM7tSiVyWSLfUyjv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Mh0G/btrtsVKYFBs/MApgmvM7tSiVyWSLfUyjv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Mh0G/btrtsVKYFBs/MApgmvM7tSiVyWSLfUyjv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Mh0G%2FbtrtsVKYFBs%2FMApgmvM7tSiVyWSLfUyjv1%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; alt=&quot;intellij hotswap&quot; loading=&quot;lazy&quot; width=&quot;1467&quot; height=&quot;1062&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file &amp;gt; settings &amp;gt; Advanced Settings &amp;gt; Allow auto-make to start....&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 checkbox에 check해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에까지 세팅후에 Intellij를 재실행 해 주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2911&quot; data-origin-height=&quot;1825&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b19ajc/btrtq9iECJx/V4bLdaUmcKXDym3S2pRqVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b19ajc/btrtq9iECJx/V4bLdaUmcKXDym3S2pRqVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b19ajc/btrtq9iECJx/V4bLdaUmcKXDym3S2pRqVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb19ajc%2Fbtrtq9iECJx%2FV4bLdaUmcKXDym3S2pRqVk%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; alt=&quot;intellij hotswap&quot; loading=&quot;lazy&quot; width=&quot;2911&quot; height=&quot;1825&quot; data-origin-width=&quot;2911&quot; data-origin-height=&quot;1825&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 수정 후 저장을 하면 위와 같이 자동으로 Hot Swap 되는 것을 확인할 수 있다.&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;Intellij 기능 중에&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1056&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nsIP2/btrtzaM86yY/Bn7vXOrDEAKSedfaGCZq8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nsIP2/btrtzaM86yY/Bn7vXOrDEAKSedfaGCZq8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nsIP2/btrtzaM86yY/Bn7vXOrDEAKSedfaGCZq8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnsIP2%2FbtrtzaM86yY%2FBn7vXOrDEAKSedfaGCZq8K%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; alt=&quot;intellij hotswap&quot; loading=&quot;lazy&quot; width=&quot;1476&quot; height=&quot;1056&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1056&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Debugger HotSwap기능이 있다. 해당기능이 공식적인&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.jetbrains.com/help/idea/altering-the-program-s-execution-flow.html#reload_classes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jetbrains.com/help/idea/altering-the-program-s-execution-flow.html#reload_classes&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HotSwap 기능이긴 한데, 생각처럼 작동 되진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>intellij</category>
      <category>hotswap</category>
      <category>intellij</category>
      <category>SpringBoot</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/252</guid>
      <comments>https://enumclass.tistory.com/252#entry252comment</comments>
      <pubDate>Wed, 16 Feb 2022 23:36:08 +0900</pubDate>
    </item>
    <item>
      <title>Javascript Generator &amp;amp; Iterator 설명 및 게임 sample</title>
      <link>https://enumclass.tistory.com/251</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;Javascript로 개발을 하면서 generator 기능을 활용해볼 기회가 거의 없었는데, 고민해보니까 재미있는 Sample를 만들 수 있어서 포스트를 남겨 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 포스트를 보시는 분들도 Generator가 이렇게 쓰일 수도 있구나 하는 생각을 하실 수 있는 기회가 되었으면 좋겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Generator가 무엇인지 알아본다&lt;/li&gt;
&lt;li&gt;Iterator가 무엇인지 알아본다&lt;/li&gt;
&lt;li&gt;Generator를 이용해서 베스킨라비스 31 게임을 만들어 본다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generator란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generator는 iterable protocol과 iterator protocol을 따르는 일종의 데이터 생성 Object입니다.&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://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol&lt;/a&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;(혹시 잘못 이해한거라면 알려주세요~ 겸허히 고치겠습니다.)&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;iterable protocol&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array와 Map과 같이 기본적으로 iterable 가능한 객체는 내부에 기본적으로 iterator 기능을 제공하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES2015 대상으로 정의된 Array의 typescript의 내용을 보면 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644906021624&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface ArrayConstructor {
...
    /**
     * Creates an array from an iterable object.
     * @param arrayLike An array-like object to convert to an array.
     * @param mapfn A mapping function to call on every element of the array.
     * @param thisArg Value of 'this' used to invoke the mapfn.
     */
    from&amp;lt;T, U&amp;gt;(arrayLike: ArrayLike&amp;lt;T&amp;gt;, mapfn: (v: T, k: number) =&amp;gt; U, thisArg?: any): U[];
...
}

interface Array&amp;lt;T&amp;gt; {
    /** Iterator */
    [Symbol.iterator](): IterableIterator&amp;lt;T&amp;gt;;

    /**
     * Returns an iterable of key, value pairs for every entry in the array
     */
    entries(): IterableIterator&amp;lt;[number, T]&amp;gt;;

    /**
     * Returns an iterable of keys in the array
     */
    keys(): IterableIterator&amp;lt;number&amp;gt;;

    /**
     * Returns an iterable of values in the array
     */
    values(): IterableIterator&amp;lt;T&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array Constructor에서 iterable object를 만들고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 내부적으로 Symbol.iterator가 존재하고 Iterable을 제공가능한 entries, keys, values가 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 말하자면 [Symbol.iterator] 가 제공 가능하다면 iterable 프로토콜을 따른다고 볼수 있는것 같습니다.&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;iterator protocol&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 제공된 iterable object는 iterator protocol을 따라야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;next() 메서드가 제공되어야 한다.&lt;/li&gt;
&lt;li&gt;next() 메서드의 결과값은
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;done : iterator 시퀀스가 완료 되었는지 표시&lt;/li&gt;
&lt;li&gt;value : 현재 시퀀스의 특정 부분 결과 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2가지 조건을 따르는 Custom Iterator Class&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 2가지 조건을 만족하는 class 하나를 만들어 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644907574441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CustomIterator {
    constructor(start = 0, step = 1, end = 100){
        this.start = start;
        this.step = step;
        this.end = end;
    }

    [Symbol.iterator](){
        return this;
    }

    next() {
        this.start += this.step
        return this.start &amp;gt;= this.end ? {done:true,value:undefined} : {done:false,value: this.start}
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom itoerator는 [sysmbo.iterator]를 제공함으로써 iterable을 만족시키고, next기능을 제공함으로써 iterator protocol을 제공 하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;step 값만큼 start에서부터 end까지 jump하면서 숫자를 return하는 class입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 아래와 같이 실행해 보면 iterator 작동이 정상적으로 진행 되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644907659158&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const itor = new CustomIterator(0,2,100);

console.log([...itor])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre id=&quot;code_1644907709851&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
   2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22,
  24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44,
  46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66,
  68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88,
  90, 92, 94, 96, 98
]&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;그럼 Generator는 위 두가지를 따르는지 알아볼까요?&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;Generator의 Iterator 정의&lt;/h2&gt;
&lt;pre id=&quot;code_1644907994075&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Generator&amp;lt;T = unknown, TReturn = any, TNext = unknown&amp;gt; extends Iterator&amp;lt;T, TReturn, TNext&amp;gt; {
    // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
    next(...args: [] | [TNext]): IteratorResult&amp;lt;T, TReturn&amp;gt;;
    return(value: TReturn): IteratorResult&amp;lt;T, TReturn&amp;gt;;
    throw(e: any): IteratorResult&amp;lt;T, TReturn&amp;gt;;
    [Symbol.iterator](): Generator&amp;lt;T, TReturn, TNext&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 generator의 타입스크립트입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Iterator를 extends하고 있는 것을 확인할 수있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해서 iterable을 제공하고 있고, next 메서드를 통해서 iterator protocol을 따르는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Generator는 custom Iterator의 역할을 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Generator를 통해서 Iterator 처리를 하는 Sample코드를 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644908201326&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generator() {
    yield 1;
    yield 2;
    yield 3;
  }
  
  const gen = generator(); // &quot;Generator { }&quot;
  
  console.log([...gen]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는&lt;/p&gt;
&lt;pre id=&quot;code_1644908215185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ 1, 2, 3 ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iterator custom class를 직접 만드는 것 보다는 generator를 만드는게 편하겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 generator를 iterator 대용으로 많이 사용 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단지 이게 이유일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 generator의 구조에 대해서 알아보면서 다른 방식으로 사용이 가능하다는 것을 확인해 보겠습니다.&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;Generator 구조&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;new 가 없다&lt;/h3&gt;
&lt;pre id=&quot;code_1644908359715&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generator() {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 조금 특이한 모양이 보이지 않나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function 옆에 '*' 이 별이 붙어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 별을 붙임으로써 generator function이라는 것을 확인 시켜 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해서 사용자는 new Instance를 더이상 활용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 function에 대한 instance를 생성하려고 하면 new 키워드를 사용해야 했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generator는 new가 없이&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1644908472564&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let gen = generator(31);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 generator function을 직접 call함으로써 instance를 얻어낼 수 있습니다.&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;yield 키워드를 제공한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yield 키워드는 오직 generator만을 위해서 만들어진 키워드 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generator안에서 pause와 resume 기능을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 next() 메서드를 call하면 yield가 있는 곳까지만 실행을 하고 결과 값을 돌려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 또 next() 메서드를 call하면 또 다음 yield가 있는 곳까지만 작동합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644908840516&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generator() {
    console.log(&quot;next를 한번 불렀어요&quot;);
    yield 1;
    console.log(&quot;next를 두번 불렀어요&quot;);
    yield 2;
    console.log(&quot;next를 세번 불렀어요&quot;);
    yield 3;
    console.log(&quot;next를 네번 불렀어요&quot;);
  }
  
  const gen = generator(); // &quot;Generator { }&quot;
  
  console.log(gen.next());
  console.log(gen.next());
  console.log(gen.next());
  console.log(gen.next());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 실행시킨 결과는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644908861543&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;next를 한번 불렀어요
{ value: 1, done: false }
next를 두번 불렀어요
{ value: 2, done: false }
next를 세번 불렀어요
{ value: 3, done: false }
next를 네번 불렀어요
{ value: undefined, done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next를 4번 부른 후에 드디어 done이 true가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 next메서드는 파라메터를 넣을 수 있습니다.&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;이제 지금까지 알아본 Generator 기능을 통해서 베스킨라빈스 31을 만들어 보겠습니다.&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;베스킨라빈스 31 게임 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nodejs를 기본적으로 사용한다고 생각하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;혹시 이거 무슨 게임인지 모르는 분 없겠죠? 1부터 3까지의 숫자를 말해서 숫자를 sum해가면서 31이라는 숫자를 말하는 사람이 지는 게임입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 사용자의 입력을 받을 package를 install하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;npm install prompt-sync&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;위의 패키지를 인스톨 후에 &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1644909071379&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import prompt from &quot;prompt-sync&quot;
const prom = new prompt();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 모듈을 로드해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644909105456&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generator(limit=31){
    let count = 0;
    while(count &amp;lt; limit){

    }
    console.log(&quot;User failed = &quot; + count)
    return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 31을 기준으로 while문을 진입하면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터가 1에서 부터 3까지 선택을 하고
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약에 해당 선택이 31을 초과하면 컴퓨터가 진다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자가 1에서 3까지 선택을 한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약에 사용자가 31을 초과하면 사람이 진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 기준으로 코드를 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 컴퓨터가 1에서 3까지 선택할 수 있도록 코드를 만들어 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644909484759&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const computer = Math.floor((Math.random() * 10) % 3) + 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택된 값은 count에 더해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644909501982&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;count += computer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더해진 값이 limit즉 31을 포함해서 넘어가면 컴퓨터의 패배가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644909542841&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        if(limit &amp;lt;= count) {
            console.log(&quot;Computer failed = &quot; + count)
            return count;
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넘어가지 않았다면 사용자가 1에서 3사이에 입력을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644909663119&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let userInput = yield count;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 어떻게 yield로 입력을 할 수 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yield를 만나면 count의 값을 next() 메서드를 콜한 결과값으로 return합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 다시 next를 call할때 우리는 method를 넣을 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZp653/btrtq89SQ53/06Ik7Tpn8xNMPUXUIeCA3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZp653/btrtq89SQ53/06Ik7Tpn8xNMPUXUIeCA3k/img.png&quot; data-alt=&quot;iterator and generator&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZp653/btrtq89SQ53/06Ik7Tpn8xNMPUXUIeCA3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZp653%2Fbtrtq89SQ53%2F06Ik7Tpn8xNMPUXUIeCA3k%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; alt=&quot;javascript iterator and generator&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;299&quot; data-origin-width=&quot;2124&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;iterator and generator&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 사용자가 첫번째 next를 call하면 컴퓨터가 3을 선택하고, 사용자가 2를 선택하고 next()를 콜하면 기존 3+2값으로 5가 입력되고 다시 컴퓨터가 2를 선택하면 7이라는 값이 결과로 return되게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다소 헷깔릴 수 있지만 작동하는 코드를 보면 이럴수도 있구나! 할껍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644910432871&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        let userInput = yield count;
        console.log(&quot;=====  User Input   ====&amp;gt; &quot; + userInput)
        count += userInput&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자로 부터 입력받은 값은 다시 count에 sum이 되고 만약에 31을 넘어서게 되면 사용자의 패배가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 모두 합치면&lt;/p&gt;
&lt;pre id=&quot;code_1644910449393&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* generator(limit=31){
    let count = 0;
    while(count &amp;lt; limit){
        const computer = Math.floor((Math.random() * 10) % 3) + 1;
        console.log(&quot;=====  computer Input   ====&amp;gt; &quot; + computer)
        count += computer;
        if(limit &amp;lt;= count) {
            console.log(&quot;Computer failed = &quot; + count)
            return count;
        }

        let userInput = yield count;
        console.log(&quot;=====  User Input   ====&amp;gt; &quot; + userInput)
        count += userInput
    }
    console.log(&quot;User failed = &quot; + count)
    return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 generator function을 이용해서 사용자입력을 받아보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644910516834&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let gen = generator(31);
let lastObj = gen.next();
let done = lastObj.done;
let lastValue = lastObj.value;

while(!done){
    console.log(&quot;Current Value = &quot; + lastValue)
    const result = parseInt(prom(&quot;what is your number?&quot;));
    lastObj = gen.next(result);
    lastValue = lastObj.value;
    done = lastObj.done;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임의 시작은 컴퓨터 부터임으로 파라메터가 없이 next()를 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 generator가 종료되기 전까지 지속적으로 사용자 입력을 받을 수 있도록 done을 flag를 활용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 prompt 기능을 이용해서 사용자의 입력을 next에 파라메터로 넣어줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 yield의 멋있는 기능인데요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #009a87;&quot;&gt;(이후 코드)&lt;/span&gt; = yield &lt;span style=&quot;background-color: #f3c000;&quot;&gt;(이전 코드)&lt;/span&gt;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;이전 코드&lt;/span&gt;의 결과 값이 next()의 return 값이 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next(파라메터값) 의 파라메터 값이 &lt;span style=&quot;background-color: #009a87;&quot;&gt;(이후 코드)&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 합쳐서 실행 시켜 보면 아래와 같이 컴퓨터와의 배스킨라빈스 31 게임을 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644910772176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=====  computer Input   ====&amp;gt; 3
Current Value = 3   
what is your number?3
=====  User Input   ====&amp;gt; 3
=====  computer Input   ====&amp;gt; 2
Current Value = 8
what is your number?3
=====  User Input   ====&amp;gt; 3
=====  computer Input   ====&amp;gt; 3
Current Value = 14
what is your number?3
=====  User Input   ====&amp;gt; 3
=====  computer Input   ====&amp;gt; 1
Current Value = 18
what is your number?3
=====  User Input   ====&amp;gt; 3
=====  computer Input   ====&amp;gt; 3
Current Value = 24
what is your number?3
=====  User Input   ====&amp;gt; 3
=====  computer Input   ====&amp;gt; 3
Current Value = 30
what is your number?3
=====  User Input   ====&amp;gt; 3
User failed = 33&lt;/code&gt;&lt;/pre&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;full 소스는 여기에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1644910969866&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;javascript generator 기능 검증&quot; data-og-description=&quot;javascript generator 기능 검증. GitHub Gist: instantly share code, notes, and snippets.&quot; data-og-host=&quot;gist.github.com&quot; data-og-source-url=&quot;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&quot; data-og-url=&quot;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6zzzm/hyNqArEvVc/pX6B2HK5bIGMEKpBKYoWJK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gist.github.com/theyoung/ec26ec8d5408664118e0217b4ab677f7&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6zzzm/hyNqArEvVc/pX6B2HK5bIGMEKpBKYoWJK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&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;javascript generator 기능 검증&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;javascript generator 기능 검증. GitHub Gist: instantly share code, notes, and snippets.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gist.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;</description>
      <category>Web</category>
      <category>generator</category>
      <category>Iterable</category>
      <category>iterator</category>
      <category>JavaScript</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/251</guid>
      <comments>https://enumclass.tistory.com/251#entry251comment</comments>
      <pubDate>Tue, 15 Feb 2022 16:43:16 +0900</pubDate>
    </item>
    <item>
      <title>Android BaseAdapter vs RecyclerAdapter 작동 원리 (RecyclerView 개발)</title>
      <link>https://enumclass.tistory.com/250</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 ListView로 만들어져 있던 List를 Recycler View로 만들어 본다.&lt;/li&gt;
&lt;li&gt;BaseAdapter와 RecyclerAdapter의 작동원리를 알아본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하기 링크가 본 포스트의 선행입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.05 - [Android] - Android ViewModel &amp;amp; ListView 사용하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(흠... Compose 강좌 만들려고 간단히 기본 강좌 만드는 목적이었는데 이제 Recycler View라니 갈길이 너무 멀군요)&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;Gradle 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 그렇듯이 Jebpack을 사용하기 위해서 관련된 라이브러리를 Gradle에 적용을 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/jetpack/androidx/releases/recyclerview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/jetpack/androidx/releases/recyclerview&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 링크를 참조해서 아래와 같이 app 폴더 하위에 build.gradle을 업데이트 해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644400303557&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;....

dependencies {

...
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우상단 sync를 진행합니다.&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;Recycler Fragment 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 TestFragment -&amp;gt; SecondFragment -&amp;gt; ThirdFragment의 순서로 작동을 했는데 아래와 같이 secondFragment와 동일한 recycleFragment를 만들고자 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3360&quot; data-origin-height=&quot;3524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ugWUh/btrsVJKqAfF/6WEuINjU518lzFHM9kTHak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ugWUh/btrsVJKqAfF/6WEuINjU518lzFHM9kTHak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ugWUh/btrsVJKqAfF/6WEuINjU518lzFHM9kTHak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FugWUh%2FbtrsVJKqAfF%2F6WEuINjU518lzFHM9kTHak%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; alt=&quot;recyclerView&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;686&quot; data-origin-width=&quot;3360&quot; data-origin-height=&quot;3524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 보면 ListView와 동일한 RecyclerView가 있는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9DDIO/btrsYcSp7Zb/RGucQRVj0ZPcyeb2OlH6x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9DDIO/btrsYcSp7Zb/RGucQRVj0ZPcyeb2OlH6x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9DDIO/btrsYcSp7Zb/RGucQRVj0ZPcyeb2OlH6x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9DDIO%2FbtrsYcSp7Zb%2FRGucQRVj0ZPcyeb2OlH6x1%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; alt=&quot;fragment&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;576&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 추가 Fragment를 생성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명은 RecycleFragment.kt로 생성해 주고, layout은 fragment_recycle.xml을 만들어 줍니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Fragment layout xml 수정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 fragment_recycle.xml코드를 재 사용해서 수정하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment_recycle.xml&lt;/p&gt;
&lt;pre id=&quot;code_1644400905211&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.SecondFragment&quot;&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;TextView
        android:id=&quot;@+id/textView2&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:text=&quot;@string/fragment_second&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button2&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginTop=&quot;32dp&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2&quot;
        app:layout_constraintTop_toTopOf=&quot;@+id/textView2&quot; /&amp;gt;

    &amp;lt;ListView
        android:id=&quot;@+id/listview&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_marginTop=&quot;64dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/button2&quot; /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 TextView의 Id와 기존 ListView를 RecyclerView로 변경해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644400958417&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;

    tools:context=&quot;.RecycleFragment&quot;&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;TextView
        android:id=&quot;@+id/textView2R&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:text=&quot;@string/fragment_recycle&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button2&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginTop=&quot;32dp&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2R&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2R&quot;
        app:layout_constraintTop_toTopOf=&quot;@+id/textView2R&quot; /&amp;gt;

    &amp;lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/listviewR&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_marginTop=&quot;64dp&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.LinearLayoutManager&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2R&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2R&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/button2&quot;
        app:spanCount=&quot;1&quot;
        /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가장 중요한것이&lt;/p&gt;
&lt;pre id=&quot;code_1644401178869&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/listviewR&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_marginTop=&quot;64dp&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.LinearLayoutManager&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2R&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2R&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/button2&quot;
        app:spanCount=&quot;1&quot;
        /&amp;gt;&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;기존 ListView를 삭제하고 RecyclerView 로 변환 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;layout을 만들었으니 만든 layout을 navigation에 연결해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;navigation을 잊으신 그대는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/245&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment Navigation with Action&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기를 참고하세요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1r24M/btrsVJX6yFm/4I8YhV8DTdJKwkjvyML1L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1r24M/btrsVJX6yFm/4I8YhV8DTdJKwkjvyML1L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1r24M/btrsVJX6yFm/4I8YhV8DTdJKwkjvyML1L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1r24M%2FbtrsVJX6yFm%2F4I8YhV8DTdJKwkjvyML1L1%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; alt=&quot;fragment navigation&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;724&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존과 동일하게 recycleFragment를 만들어 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arguments와 Action도 동일하게 만들어주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 xml로 해야할 부분이 많이 끝났습니다.&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;BaseAdapter vs RecycleView Adapter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 ListView에서는 BaseAdapter를 사용해서 ListViewAdpater를 만들었던거 기억나시나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.05 - [Android] - Android ViewModel &amp;amp; ListView 사용하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RecyclerView는 전용 Adapter를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교해볼까요?&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;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;BaseAdapter&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;RecyclerView Adapter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;class&amp;nbsp;ListViewAdapter(val&amp;nbsp;context&amp;nbsp;:&amp;nbsp;Context,&amp;nbsp;var&amp;nbsp;list:&amp;nbsp;DataViewModel)&amp;nbsp;:&amp;nbsp;BaseAdapter()&amp;nbsp;{&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;class&amp;nbsp;RecycleViewAdapter(val&amp;nbsp;context&amp;nbsp;:&amp;nbsp;Context,&amp;nbsp;var&amp;nbsp;list:&amp;nbsp;DataViewModel)&amp;nbsp;:&amp;nbsp;RecyclerView.Adapter&amp;lt;RecycleViewAdapter.Holder&amp;gt;()&amp;nbsp;{&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;getCount()&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;getItemCount()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;getItem()&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;onBindViewHolder()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;getView()&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;onCreateViewHolder()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Holder를 만드는건 개발자 마음&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;class&amp;nbsp;Holder(val&amp;nbsp;item&amp;nbsp;:&amp;nbsp;View)&amp;nbsp;:&amp;nbsp;RecyclerView.ViewHolder(item)&amp;nbsp;{&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두개의 Adapter 를 상속받으면 반듯이 만들어야 하는 코드 입니다.&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;특히 RecyclerView는 get대신에 on으로 시작하는 Methods명을 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;on은 일반적으로 Event 발생에 따른 Callback처리를 할때 on으로 시작하는 Method를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 OnKeyListener, OnclickListener 등이 가장 대표적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 get은 언제 사용될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getContext(), getClass() 등 어떤 데이터를 개발자가 의도한 특정 시점에 얻어올때 사용하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이점을 보면 두 Adapter를 제공할때 배경을 상상해 볼 수 있습니다.&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;BaseAdapter가 동작하는 방법은?&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;BaseAdapter를 만들던 시기는&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1303&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFts6s/btrsXtAfhcg/p9pfKsioBSPmJXfEuVS4b1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFts6s/btrsXtAfhcg/p9pfKsioBSPmJXfEuVS4b1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFts6s/btrsXtAfhcg/p9pfKsioBSPmJXfEuVS4b1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFts6s%2FbtrsXtAfhcg%2Fp9pfKsioBSPmJXfEuVS4b1%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;636&quot; height=&quot;68&quot; data-origin-width=&quot;1303&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API Level1 즉, 최초 안드로이드 코드인 2008년도에 추가된 Class입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 아무래도 Event Base의 개발 방식보다는 선형적인 개발방식이 더 직관적이었을 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 간단히 ListView.java 코드를 확인해 보면 아래와 같이 getCount로 얻어온 모든 사이즈 만큼 startPosition에서 모든 View를 getView하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
        int maxHeight, int disallowPartialChildPosition) {
    final ListAdapter adapter = mAdapter;
    if (adapter == null) {
        return mListPadding.top + mListPadding.bottom;
    }

    // Include the padding of the list
    ..............
    int i;
    View child;

    // mItemCount - 1 since endPosition parameter is inclusive
    endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;
    final AbsListView.RecycleBin recycleBin = mRecycler;
    final boolean recyle = recycleOnMeasure();
    final boolean[] isScrap = mIsScrap;

    for (i = startPosition; i &amp;lt;= endPosition; ++i) {
        child = obtainView(i, isScrap);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저위에 obtainView에서 i position을 바탕으로 getView하는 Code를 AbsListView.java에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644412721274&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    View obtainView(int position, boolean[] outMetadata) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, &quot;obtainView&quot;);

        outMetadata[0] = false;

        // Check whether we have a transient state view. Attempt to re-bind the
        // data and discard the view if we fail.
        final View transientView = mRecycler.getTransientStateView(position);
        if (transientView != null) {
            final LayoutParams params = (LayoutParams) transientView.getLayoutParams();

            // If the view type hasn't changed, attempt to re-bind the data.
            if (params.viewType == mAdapter.getItemViewType(position)) {
                final View updatedView = mAdapter.getView(position, transientView, this);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 StartPosition이 0일경우 마지막 Size까지의 모든 View가 생성되게 됩니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Recycler가 작동하는 방법은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용을 이해하기 위해서는 GAP_WORK라는 것을 이해해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;/**
 * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
 * RenderThread but before the next frame begins. We schedule prefetch work in this window.
 */
static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT &amp;gt;= 21;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 보자면 UI Thread에서 RenderThread로 화면 랜더링을 위해서 Frame을 넘겨주고 나면 아주 잠시 idle 시간이 남는다고 합니다. 해당 시간에 화면에 표현할 prefetch를 스케쥴 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드를 보면서 내용을 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RecyclerView.java소스의 내용입니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mLayoutOrScrollCounter = 0;
    mIsAttached = true;
    mFirstLayoutComplete = mFirstLayoutComplete &amp;amp;&amp;amp; !isLayoutRequested();
    if (mLayout != null) {
        mLayout.dispatchAttachedToWindow(this);
    }
    mPostedAnimatorRunner = false;

    if (ALLOW_THREAD_GAP_WORK) {
        // Register with gap worker
        mGapWorker = GapWorker.sGapWorker.get();
        if (mGapWorker == null) {
            mGapWorker = new GapWorker();

            // break 60 fps assumption if data from display appears valid
            // NOTE: we only do this query once, statically, because it's very expensive (&amp;gt; 1ms)
            Display display = ViewCompat.getDisplay(this);
            float refreshRate = 60.0f;
            if (!isInEditMode() &amp;amp;&amp;amp; display != null) {
                float displayRefreshRate = display.getRefreshRate();
                if (displayRefreshRate &amp;gt;= 30.0f) {
                    refreshRate = displayRefreshRate;
                }
            }
            mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
            GapWorker.sGapWorker.set(mGapWorker);
        }
        mGapWorker.add(this);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보자면 displayRefreshRate를 바탕으로 Frame Interval을 계산합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Gapworker를 실행하는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GAPworker는 Thread로 작동하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final class GapWorker implements Runnable {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GapWorker가 Frame Interval 사이에 화면에 표현할 View or Hoder를 미리 preFetch하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 PreFetch가 작동하는 코드를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// Populate task list from prefetch data...
mTasks.ensureCapacity(totalTaskCount);
int totalTaskIndex = 0;
for (int i = 0; i &amp;lt; viewCount; i++) {
    RecyclerView view = mRecyclerViews.get(i);
    if (view.getWindowVisibility() != View.VISIBLE) {
        // Invisible view, don't bother prefetching
        continue;
    }

    LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
    final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
            + Math.abs(prefetchRegistry.mPrefetchDy);
    for (int j = 0; j &amp;lt; prefetchRegistry.mCount * 2; j += 2) {
        final Task task;
        if (totalTaskIndex &amp;gt;= mTasks.size()) {
            task = new Task();
            mTasks.add(task);
        } else {
            task = mTasks.get(totalTaskIndex);
        }
        final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];

        task.immediate = distanceToItem &amp;lt;= viewVelocity;
        task.viewVelocity = viewVelocity;
        task.distanceToItem = distanceToItem;
        task.view = view;
        task.position = prefetchRegistry.mPrefetchArray[j];

        totalTaskIndex++;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GapWorker가 작동하는 코드인데요. ListView와 다른점이 보이나요?&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; viewCount; i++) {
    RecyclerView view = mRecyclerViews.get(i);
    if (view.getWindowVisibility() != View.VISIBLE) {
        // Invisible view, don't bother prefetching
        continue;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;viewCount를 갖어오고 화면에 해당 내용이 보이지 않는다면 preFetching하지 말아라 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 필요한 부분만 prefetching을 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위에서 말한 idle time 즉, deadline 시간안에 prefetch 못한것은 다음 prefetch시간을 기다리게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;RecyclerView.Recycler recycler = view.mRecycler;
RecyclerView.ViewHolder holder;
try {
    view.onEnterLayoutOrScroll();
    holder = recycler.tryGetViewHolderForPositionByDeadline(
            position, false, deadlineNs);

    if (holder != null) {
        if (holder.isBound() &amp;amp;&amp;amp; !holder.isInvalid()) {
            // Only give the view a chance to go into the cache if binding succeeded
            // Note that we must use public method, since item may need cleanup
            recycler.recycleView(holder.itemView);
        } else {
            // Didn't bind, so we can't cache the view, but it will stay in the pool until
            // next prefetch/traversal. If a View fails to bind, it means we didn't have
            // enough time prior to the deadline (and won't for other instances of this
            // type, during this GapWorker prefetch pass).
            recycler.addViewHolderToRecycledViewPool(holder, false);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 본 코드의 flow가 100% 정확하지 않을 수는 있습니다. 그러나 분명한 것은 ListView의 작동방식이 선형적이라고 하면 Recycle View는 View의 가시성과 frame rate를 계산한 idle deadline시간을 바탕으로 병렬적인 작동을 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 작동 방식 차이가 recycle view가 기존 ListView보다 메모리 및 작동 시간에 더 많은 이점을 갖는 다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와같은 행위를 ViewHodler Pattern이라고 한다고 합니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;눈에 보이는 View 객체만 만들것인가?&lt;/li&gt;
&lt;li&gt;데이터의 fetch가 일어나는 시점을 언제로 잡을 것인가?&lt;/li&gt;
&lt;li&gt;View와 Data의 분리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Recycler는 Hodler와 Data의 binding이 분리되어 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RecyclerView Adapter 개발하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 RecyclerView.Adapter를 상속받는 RecycleViewAdapter.kt를 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 작동방법은 기존 ListView와 동일한 방식으로 만들겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644669470509&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RecycleViewAdapter(val context : Context, var list: DataViewModel) : RecyclerView.Adapter&amp;lt;RecycleViewAdapter.Holder&amp;gt;() {

    val layoutInflater : LayoutInflater = LayoutInflater.from(context)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        return Holder.inflateHolder(parent, layoutInflater)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.bind(list.getMessagesAt(position),position,this)
    }

    override fun getItemCount(): Int {
        return list.size
    }


    class Holder(val item : View) : RecyclerView.ViewHolder(item) {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RecycleViewAdapter는 Fragement의 context와 ViewModel을 파라메터로 받습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;context : LayoutInflater를 만들기 위해서 갖어 왔습니다. livedata 부분에서 언급하겠지만 context는 ViewGroup에서 받을 수 있습니다. 이해를 위해서 넣었습니다.&lt;/li&gt;
&lt;li&gt;list : view에 bind할 데이터입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 앞서 비교했던 기본 메서드 3개를 만들면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;onCreateViewHolder : Hodler View 객체를 만드는 부분입니다. View만 만들었지 Data를 Bind하지 않은 상태입니다.&lt;/li&gt;
&lt;li&gt;onBindViewHolder : 위에서 만들어진 View Holder 객체에 특정 position의 Data를 Binding하는 부분입니다.&lt;/li&gt;
&lt;li&gt;getItemCount : RecyclerView에 몇개의 객체가 있는지 알려주는 부분입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기본적인 부분을 만들었음으로 ViewHolder를 상속한 Holder 객체에 다음 두가지 기능을 만들어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View를 담고있는 Holder 객체를 만들어야 합니다.&lt;/li&gt;
&lt;li&gt;Holder 객체에 특정 Position의 Data를 Bind해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;View를 담고있는 Holder 객체를 만들자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Holder라는 객체가 onCreateViewHolder에게 불리워 졌을때 새로운 View객체를 갖는 Holder를 생성하도록 하여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 java에서 Static과 가장 근접한 효과를 내는 compainon object를 사용하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644670577842&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RecycleViewAdapter(val context : Context, var list: DataViewModel) : RecyclerView.Adapter&amp;lt;RecycleViewAdapter.Holder&amp;gt;() {
...
    class Holder(val item : View) : RecyclerView.ViewHolder(item) {
        companion object {
            fun inflateHolder(parent: ViewGroup, layoutInflater: LayoutInflater) : Holder {
                val view = layoutInflater.inflate(R.layout.element_single, parent, false)
                return Holder(view)
            }
        }
        
        fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
            item.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text = str
        }
....
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Holder.inflateHolder method를 호출하면 layoutinflater를 통해서 R.layout.element_single xml layout 을 로드해 오도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 R.layout.element_single은 ListView를 만들때 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.05 - [Android] - Android ViewModel &amp;amp; ListView 사용하기&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;Inflate를 통해서 View객체를 생성합니다. 이때 만들어진 객체는 Row한건의 UI를 표현하기위한 것입니다.&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;Data Bind 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 View도 만들었으니 해당 View에 Data를 Bind해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설명한것 처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onCreateViewHoder에서 View를 만들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 특정 Position이 표현될 것 같은 시점에 Data Prefetch를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 불리는 event가 onBindViewHolder 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onbindViewHolder에는 Position정보가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Position정보로 Bind해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644672728931&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class RecycleViewAdapter(val context : Context, var list: DataViewModel) : RecyclerView.Adapter&amp;lt;RecycleViewAdapter.Holder&amp;gt;() {

...
    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.bind(list.getMessagesAt(position),position,this)
    }
...
    class Holder(val item : View) : RecyclerView.ViewHolder(item) {
...

        fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
            item.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text = str
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onBindViewHolder에 파라메터로 앞서 만들어 놓은 Holder 객체가 들어옵니다. 해당 Holder객체에 Bind할 Position정보를 바탕으로 Hoder의 Bind를 호출합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;holder.bind(list.getMessagesAt(position),position,this)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 호출하면 holder객체 내 bind가 불리워 집니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;item.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text = str&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 이용해서 holder안에 있는 view에 text_string view를 얻어오고 그곳에 str정보를 주입합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이렇게까지만 하면 List가 화면에 표시되는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfXdkN/btrs8BrGPlr/WIvyNyuKdPHHiGm70OvlLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfXdkN/btrs8BrGPlr/WIvyNyuKdPHHiGm70OvlLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfXdkN/btrs8BrGPlr/WIvyNyuKdPHHiGm70OvlLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfXdkN%2Fbtrs8BrGPlr%2FWIvyNyuKdPHHiGm70OvlLK%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; alt=&quot;recyclerview result&quot; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;436&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7hal/btrtb2ow2sk/eBAEEkRGDWqkM4QNEjc5S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7hal/btrtb2ow2sk/eBAEEkRGDWqkM4QNEjc5S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7hal/btrtb2ow2sk/eBAEEkRGDWqkM4QNEjc5S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7hal%2Fbtrtb2ow2sk%2FeBAEEkRGDWqkM4QNEjc5S0%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; alt=&quot;recyclerview result&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;478&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&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;원래 List View의 모습과는 다릅니다. 그 이유는 ListView에 기본적으로 제공되는 theme가 Recycler View에는 동일하게 제공 되지 않기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 리소스에 drawable&amp;gt;recycle_item.xml을 만들어서 selector를 만들었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1644673232353&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;selector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:exitFadeDuration=&quot;@android:integer/config_mediumAnimTime&quot;&amp;gt;
    &amp;lt;item&amp;gt;
        &amp;lt;shape android:shape=&quot;rectangle&quot; &amp;gt;
            &amp;lt;stroke
                android:width=&quot;1dp&quot;
                android:color=&quot;#FF000000&quot; /&amp;gt;

            &amp;lt;solid android:color=&quot;#00FFFFFF&quot; /&amp;gt;

            &amp;lt;padding android:left=&quot;10dp&quot;
                android:right=&quot;10dp&quot;
                android:top=&quot;10dp&quot;
                android:bottom=&quot;10dp&quot; /&amp;gt;
        &amp;lt;/shape&amp;gt;
    &amp;lt;/item&amp;gt;
&amp;lt;/selector&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 부분은 별도로 설명할 수 있는 기회가 있다면 별도로 포스트를 남기겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 데이터 Bind까지 완료 되었으니 Holder에 Event를 추가해서 데이터를 삭제하거나 thirdFragment로 넘어가는 부분을 만들어 보겠습니다.&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;click event 처리하기&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;
class RecycleViewAdapter(val context : Context, var list: DataViewModel) : RecyclerView.Adapter&amp;lt;RecycleViewAdapter.Holder&amp;gt;() {

    val layoutInflater : LayoutInflater = LayoutInflater.from(context)

    fun deleteAt(position: Int){
        list.removeDataAt(position)
        notifyDataSetChanged()
    }
...

    class Holder(val item : View) : RecyclerView.ViewHolder(item) {
        companion object {
            fun inflateHolder(parent: ViewGroup, layoutInflater: LayoutInflater) : Holder {
...
            }
        }

        fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
...
            item?.setOnClickListener() {
                val str = it.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text.toString()
                Toast.makeText(item.context, &quot;clicked &quot; + str, Toast.LENGTH_SHORT).show()
                val action = RecycleFragmentDirections.actionRecycleFragmentToThirdFragment(str)
                it.findNavController().navigate(action)
                true
            }
            item?.setOnLongClickListener() {
                Toast.makeText(item.context, &quot;long clicked&quot; + position, Toast.LENGTH_SHORT).show()
                weak?.deleteAt(position)
                true
            }
        }

    }
}&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;위에서 deleteAt은 ViewModel에서 특정 position에 있는 데이터를 삭제하고 notifyDataSetChanged 메서드를 이용해서 리스트를 Refresh 시키는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;click event처리는 bind 내에서 정리했습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;lateinit var weak : RecycleViewAdapter

fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
    weak = WeakReference(adapter).get()!!
    item.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text = str
    item?.setOnClickListener() {
        val str = it.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text.toString()
        Toast.makeText(item.context, &quot;clicked &quot; + str, Toast.LENGTH_SHORT).show()
        val action = RecycleFragmentDirections.actionRecycleFragmentToThirdFragment(str)
        it.findNavController().navigate(action)
        true
    }
    item?.setOnLongClickListener() {
        Toast.makeText(item.context, &quot;long clicked&quot; + position, Toast.LENGTH_SHORT).show()
        weak?.deleteAt(position)
        true
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용은 기존 Listview와 동일하기 때문에 자세하게 다루지 않겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 WeakReference에 대한 설명을 하고자 합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;        fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
            weak = WeakReference(adapter).get()!!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 객체를 long click하면 삭제를 하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 처리하기 위해서는 RecycleViewAdapter Class에서 만든 deleteAt 메서드를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Bind를 처리할때 Holder의 Outer Class인 RecycleViewAdapter 객체를 받게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 WeakReference를 통해서 해당 객체를 다시 받게 되는데요. 이렇게 하는 이유는 Referencing counter때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMnpB0/btrs59JYGaQ/VrYdIQIcKT0Uclk1hrKiKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMnpB0/btrs59JYGaQ/VrYdIQIcKT0Uclk1hrKiKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMnpB0/btrs59JYGaQ/VrYdIQIcKT0Uclk1hrKiKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMnpB0%2Fbtrs59JYGaQ%2FVrYdIQIcKT0Uclk1hrKiKK%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; alt=&quot;class reference&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;201&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A Class와 B Class가 서로를 참조할때 A Class가 종료되었음에도 B Class가 어디에 살아있다면 A class는 메모리에서 삭제되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Memory Leak이 됩니다. 자세한 내용은 아래를 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2020.03.27 - [Android] - android memory leak 처리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 위의 코드는 다음과 같이 변경하면 모든것이 해결됩니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;//        lateinit var weak : RecycleViewAdapter

        fun bind(str: String,position: Int, adapter: RecycleViewAdapter){
//            weak = WeakReference(adapter).get()!!
            val weak = adapter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 전역변수를 지양하고 bind 메서드 Scope 내에서만 참조를 유지하게 한다면 문제가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별것 아닌 부분이지만 이와같이 Inner class를 사용할때는 상호 참조를 주의해서 개발 진행하는 것이 필요합니다.&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은 아래를 참조하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/802157ddd41f125bf3c85369b1abffa823ce2a95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/802157ddd41f125bf3c85369b1abffa823ce2a95&lt;/a&gt;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Android</category>
      <category>listview</category>
      <category>RecyclerView</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/250</guid>
      <comments>https://enumclass.tistory.com/250#entry250comment</comments>
      <pubDate>Sat, 12 Feb 2022 23:02:43 +0900</pubDate>
    </item>
    <item>
      <title>EKS prometheus and grafana 설치하기</title>
      <link>https://enumclass.tistory.com/249</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EKS Cluster를 만든다&lt;/li&gt;
&lt;li&gt;prometheus-grafana를 install한다&lt;/li&gt;
&lt;li&gt;grafana를 실행한다&lt;/li&gt;
&lt;/ul&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;eks cluster를 만들기 위해서는 다음과 같이 4가지 install이 선행되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 기준으로 install하겠다.&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;package 관리 tool은 choco를 설치 해야한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://chocolatey.org/install#individual&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://chocolatey.org/install#individual&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;windows power shell을 administrator 모드로 실행 한다. (마우스 우측 관리자 권한으로 실행)&lt;/p&gt;
&lt;pre id=&quot;code_1644158633118&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 붙여넣기 하고 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644158680818&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ choco
Chocolatey v0.10.15
Please run 'choco -?' or 'choco &amp;lt;command&amp;gt; -?' for help menu.&lt;/code&gt;&lt;/pre&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;aws-cli install하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://awscli.amazonaws.com/AWSCLIV2.msi&quot;&gt;https://awscli.amazonaws.com/AWSCLIV2.msi&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 다운로드 받아서 install 한다&lt;/p&gt;
&lt;pre id=&quot;code_1644158026247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C:\&amp;gt; aws --version

aws-cli/2.4.5 Python/3.8.8 Windows/10 exe/AMD64 prompt/off&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정상적으로 실행되면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aws configure를 통해서 사용자를 등록하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1644158940914&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ aws configure&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러기 위해서는 IAM User를 등록하고 마지막에 나오는 access key와 secret key를 등록해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;a href=&quot;https://console.aws.amazon.com/iam/home#/policies/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FAdministratorAccess&quot;&gt;AdministratorAccess&lt;/a&gt; 권한이 있는 User가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster가 만들어진 이후에는 EKS 권한만 필요한데, Cluster를 만들기 위해서는 Admin이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 등록이 완료 되었으면&lt;/p&gt;
&lt;pre id=&quot;code_1644158963983&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ aws sts get-caller-identity
{
    &quot;UserId&quot;: &quot;AIDASDQFCH6DBX2IL7Y5B&quot;,
    &quot;Account&quot;: &quot;144965779334&quot;,
    &quot;Arn&quot;: &quot;arn:aws:iam::144965779334:user/Admin&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 현재 사용자의 user 정보를 알수 있다.&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;kubectl을 설치한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;choco를 통해서 install하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1644158766360&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;choco install kubernetes-cli&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1644158816355&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl version
Client Version: version.Info{Major:&quot;1&quot;, Minor:&quot;19&quot;, GitVersion:&quot;v1.19.3&quot;, GitCommit:&quot;1e11e4a2108024935ecfcb2912226cedeafd99df&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2020-10-14T12:50:19Z&quot;, GoVersion:&quot;go1.15.2&quot;, Compiler:&quot;gc&quot;, Platform:&quot;windows/amd64&quot;}
Server Version: version.Info{Major:&quot;1&quot;, Minor:&quot;21+&quot;, GitVersion:&quot;v1.21.5-eks-bc4871b&quot;, GitCommit:&quot;5236faf39f1b7a7dabea8df12726f25608131aa9&quot;, GitTreeState:&quot;clean&quot;, BuildDate:&quot;2021-10-29T23:32:16Z&quot;, GoVersion:&quot;go1.16.8&quot;, Compiler:&quot;gc&quot;, Platform:&quot;linux/amd64&quot;}&lt;/code&gt;&lt;/pre&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;Eksctl 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로그램을 설치하기 전에 eksctl이 설치 되어있는지 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(aws cli 설치하면 같이 설치 되었던거 같은데 기억이 정확하지 않다)&lt;/p&gt;
&lt;pre id=&quot;code_1644159137341&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ eksctl version
0.82.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 위에처럼 eksctl이 없다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/ko_kr/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-eksctl.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/ko_kr/ko_kr/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-eksctl.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1644159160134&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chocolatey install -y eksctl&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;h3 data-ke-size=&quot;size23&quot;&gt;Helm을 설치한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://helm.sh/docs/intro/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://helm.sh/docs/intro/install/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1644157841224&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;Installing Helm&quot; data-og-description=&quot;Learn how to install and get running with Helm.&quot; data-og-host=&quot;helm.sh&quot; data-og-source-url=&quot;https://helm.sh/docs/intro/install/&quot; data-og-url=&quot;https://helm.sh/docs/intro/install/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rRZ2c/hyNjHMt8Au/Wys0siz1FAqCmxOeD9Gt20/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://helm.sh/docs/intro/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://helm.sh/docs/intro/install/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rRZ2c/hyNjHMt8Au/Wys0siz1FAqCmxOeD9Gt20/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;Installing Helm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to install and get running with Helm.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;helm.sh&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 cygwin을 사용하였음으로 windows install이 필요하다&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;choco install kubernetes-helm&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1644158860431&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ helm version
version.BuildInfo{Version:&quot;v3.7.2&quot;, GitCommit:&quot;663a896f4a815053445eec4153677ddc24a0a361&quot;, GitTreeState:&quot;clean&quot;, GoVersion:&quot;go1.16.10&quot;}&lt;/code&gt;&lt;/pre&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;eks cluster 생성하기&lt;/h3&gt;
&lt;pre id=&quot;code_1644160517047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ eksctl create cluster --name prometheus-grafana-cluster&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 cluster를 생성하면 하기와 같이 cluster를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 default로 cluster를 실행하면 kubernetes api가 public으로 공개 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1644159290071&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2022-02-06 22:46:02 [ℹ]  eksctl version 0.82.0
2022-02-06 22:46:02 [ℹ]  using region us-east-2
2022-02-06 22:46:03 [ℹ]  setting availability zones to [us-east-2c us-east-2a us-east-2b]
2022-02-06 22:46:03 [ℹ]  subnets for us-east-2c - public:192.168.0.0/19 private:192.168.96.0/19
2022-02-06 22:46:03 [ℹ]  subnets for us-east-2a - public:192.168.32.0/19 private:192.168.128.0/19
2022-02-06 22:46:03 [ℹ]  subnets for us-east-2b - public:192.168.64.0/19 private:192.168.160.0/19
2022-02-06 22:46:03 [ℹ]  nodegroup &quot;ng-e158f753&quot; will use &quot;&quot; [AmazonLinux2/1.21]
2022-02-06 22:46:03 [ℹ]  using Kubernetes version 1.21
2022-02-06 22:46:03 [ℹ]  creating EKS cluster &quot;prometheus-grafana-cluster&quot; in &quot;us-east-2&quot; region with managed nodes
2022-02-06 22:46:03 [ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
2022-02-06 22:46:03 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-east-2 --cluster=prometheus-grafana-cluster'
2022-02-06 22:46:03 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster &quot;prometheus-grafana-cluster&quot; in &quot;us-east-2&quot;
2022-02-06 22:46:03 [ℹ]  CloudWatch logging will not be enabled for cluster &quot;prometheus-grafana-cluster&quot; in &quot;us-east-2&quot;
2022-02-06 22:46:03 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=us-east-2 --cluster=prometheus-grafana-cluster'    
2022-02-06 22:46:03 [ℹ]
2 sequential tasks: { create cluster control plane &quot;prometheus-grafana-cluster&quot;,
    2 sequential sub-tasks: {
        wait for control plane to become ready,
        create managed nodegroup &quot;ng-e158f753&quot;,
    }
2022-02-06 23:04:56 [✔]  saved kubeconfig as &quot;C:\\cygwin64\\home\\home\\.kube\\config&quot;
2022-02-06 23:04:56 [ℹ]  no tasks
2022-02-06 23:04:56 [✔]  all EKS cluster resources for &quot;prometheus-grafana-cluster&quot; have been created
2022-02-06 23:04:57 [ℹ]  nodegroup &quot;ng-e158f753&quot; has 2 node(s)
2022-02-06 23:04:57 [ℹ]  node &quot;ip-192-168-7-247.us-east-2.compute.internal&quot; is ready
2022-02-06 23:04:57 [ℹ]  node &quot;ip-192-168-87-103.us-east-2.compute.internal&quot; is ready
2022-02-06 23:04:57 [ℹ]  waiting for at least 2 node(s) to become ready in &quot;ng-e158f753&quot;
2022-02-06 23:04:57 [ℹ]  nodegroup &quot;ng-e158f753&quot; has 2 node(s)
2022-02-06 23:04:57 [ℹ]  node &quot;ip-192-168-7-247.us-east-2.compute.internal&quot; is ready
2022-02-06 23:04:57 [ℹ]  node &quot;ip-192-168-87-103.us-east-2.compute.internal&quot; is ready
2022-02-06 23:05:00 [ℹ]  kubectl command should work with &quot;C:\\cygwin64\\home\\home\\.kube\\config&quot;, try 'kubectl get nodes'
2022-02-06 23:05:00 [✔]  EKS cluster &quot;prometheus-grafana-cluster&quot; in &quot;us-east-2&quot; region is ready&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 권한이 없다고 나오면 IAM User의 권한이 &lt;a href=&quot;https://console.aws.amazon.com/iam/home#/policies/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FAdministratorAccess&quot;&gt;AdministratorAccess&lt;/a&gt; 가 있는지 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster를 만들때 Cloudformation을 사용해야 해서 사실상 Admin 권한이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cluster가 다 생성이 되었다면 'eksctl get cluster'와 'kubectl get nodes'로 잘 만들어 졌는지 확인 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1647769086409&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ eksctl get cluster
2022-03-20 18:36:32 [ℹ]  eksctl version 0.82.0
2022-03-20 18:36:32 [ℹ]  using region us-east-2
NAME            REGION          EKSCTL CREATED
eksctl-demo     us-east-2       True&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647769099273&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get nodes
NAME                                           STATUS   ROLES    AGE     VERSION
ip-192-168-38-154.us-east-2.compute.internal   Ready    &amp;lt;none&amp;gt;   2m48s   v1.21.5-eks-9017834
ip-192-168-76-219.us-east-2.compute.internal   Ready    &amp;lt;none&amp;gt;   2m49s   v1.21.5-eks-9017834&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;pre id=&quot;code_1644159384709&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ aws eks --region us-east-2 update-kubeconfig --name prometheus-grafana-cluster
Updated context arn:aws:eks:us-east-2:144965779334:cluster/prometheus-grafana-cluster in C:\Users\home\.kube\config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 실행해 주자. kubeconfig에 방금 생성된 cluster 정보를 업데이트 시켜주는 명령어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;namespace로 monitoring을 생성해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1644159442526&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl create namespace monitoring
namespace/monitoring created&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;h2 data-ke-size=&quot;size26&quot;&gt;helm으로 prometheus &amp;amp; grafana를 설치하자.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/prometheus-community/helm-charts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/prometheus-community/helm-charts&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1644159547123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 명령어를 통해서 repo를 helm에 등록해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1644159586666&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ helm repo update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository&amp;nbsp; 정보를 update해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 바로 install을 진행해도 되지만 무슨 사유인지 values.yaml을 별도로 정해줘야지 작동이 잘되었다. 그래서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/grafana/helm-charts/blob/main/charts/grafana/values.yaml&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/grafana/helm-charts/blob/main/charts/grafana/values.yaml&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기있는 파일을 values.yaml 파일로 복사해서 helm 명령어가 실행될 위치에다 copy해주자.&lt;/p&gt;
&lt;pre id=&quot;code_1644159703651&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ helm install prometheus prometheus-community/kube-prometheus-stack -f &quot;values.yaml&quot; --namespace monitoring&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행을 하면&lt;/p&gt;
&lt;pre id=&quot;code_1644159715605&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NAME: prometheus
LAST DEPLOYED: Sun Feb  6 23:08:03 2022
NAMESPACE: monitoring
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
  kubectl --namespace monitoring get pods -l &quot;release=prometheus&quot;

Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create &amp;amp; configure Alertmanager and Prometheus instances using the Operator.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 나오면 정상이다.&lt;/p&gt;
&lt;pre id=&quot;code_1644159730545&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl --namespace monitoring get pods -l &quot;release=prometheus&quot;
NAME                                                   READY   STATUS    RESTARTS   AGE
prometheus-kube-prometheus-operator-6cd54566dc-bppj2   1/1     Running   0          107s
prometheus-kube-state-metrics-66c645dc8c-77c4g         1/1     Running   0          107s
prometheus-prometheus-node-exporter-g64v8              1/1     Running   0          107s
prometheus-prometheus-node-exporter-hxbht              1/1     Running   0          107s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 pods가 정상적으로 작동함을 확인할 수 있다.&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;grafana가 실행되는 것을 확인하자&lt;/h2&gt;
&lt;pre id=&quot;code_1644159828117&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl port-forward service/prometheus-grafana 3000:80 --namespace monitoring&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grafana의 포트를 80번에서 local 컴퓨터 3000포트로 변경해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1644159866014&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Forwarding from 127.0.0.1:3000 -&amp;gt; 3000
Forwarding from [::1]:3000 -&amp;gt; 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나오면 local computer 3000번 포트로 데이터를 port-forward 한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 브라우저를 열어서 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://127.0.0.1:3000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 브라우저에 작성하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v7Q9J/btrsysoXs5t/19rUqI7o9jnOUl6d26kOFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v7Q9J/btrsysoXs5t/19rUqI7o9jnOUl6d26kOFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v7Q9J/btrsysoXs5t/19rUqI7o9jnOUl6d26kOFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv7Q9J%2FbtrsysoXs5t%2F19rUqI7o9jnOUl6d26kOFK%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; alt=&quot;eks grafana&quot; loading=&quot;lazy&quot; width=&quot;1001&quot; height=&quot;755&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;755&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id는 admin&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pw는 prom-operator&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHfhOz/btrsJK9sIXV/agVnhslw2eKTsRRpAWILEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHfhOz/btrsJK9sIXV/agVnhslw2eKTsRRpAWILEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHfhOz/btrsJK9sIXV/agVnhslw2eKTsRRpAWILEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHfhOz%2FbtrsJK9sIXV%2FagVnhslw2eKTsRRpAWILEk%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; alt=&quot;efs grafana dashbaord&quot; loading=&quot;lazy&quot; width=&quot;990&quot; height=&quot;449&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;449&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;h2 data-ke-size=&quot;size26&quot;&gt;삭제는 필수다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/delete-cluster.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/eks/latest/userguide/delete-cluster.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1644160536194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ eksctl delete cluster --name prometheus-grafana-cluster&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eks는 시간당 돈이 나간다. 꼭 삭제해 주자.&lt;/p&gt;</description>
      <category>AWS</category>
      <category>EKS</category>
      <category>grafana</category>
      <category>prometheus</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/249</guid>
      <comments>https://enumclass.tistory.com/249#entry249comment</comments>
      <pubDate>Mon, 7 Feb 2022 00:12:26 +0900</pubDate>
    </item>
    <item>
      <title>kubernetes 명령어 모음</title>
      <link>https://enumclass.tistory.com/136</link>
      <description>&lt;h3 id=&quot;pod&quot; data-ke-size=&quot;size23&quot;&gt;Pod&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;현재 네임스페이스의 pods 리스트&amp;nbsp;&lt;/td&gt;
&lt;td&gt;kubectl get pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모든 네임스페이드 대상 pods 리스트&lt;/td&gt;
&lt;td&gt;kubectl get pods --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;현재 네임스페이스와 상세 정보&lt;/td&gt;
&lt;td&gt;kubectl get pods -o wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kube-system 네임스페이스 pods&lt;/td&gt;
&lt;td&gt;kubectl get -n kube-system pods -a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;my-pod 대상 yaml 출력&lt;/td&gt;
&lt;td&gt;kubectl get pod my-pod -o yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pod 상세 정보&lt;/td&gt;
&lt;td&gt;kubectl describe pod/&amp;lt;pod name&amp;gt;&lt;br /&gt;&lt;br /&gt;ex) kubectl describe pod/weave-net-2hn7s -n kube-system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List all pods with labels&lt;/td&gt;
&lt;td&gt;kubectl get pods --show-labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List running pods&lt;/td&gt;
&lt;td&gt;kubectl get pods --field-selector=status.phase=Running --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watch pods&lt;/td&gt;
&lt;td&gt;kubectl get pods -n &amp;lt;namespace&amp;gt; --watch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List pods and containers&lt;/td&gt;
&lt;td&gt;kubectl get pods --all-namespaces -o='custom-columns=PODS:.metadata.name,CONTAINERS:.spec.containers[*].name'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List pods, containers and images&lt;/td&gt;
&lt;td&gt;kubectl get pods --all-namespaces -o='custom-columns=PODS:.metadata.name,CONTAINERS:.spec.containers[*].name,Images:.spec.containers[*].image'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale out Deployment&lt;/td&gt;
&lt;td&gt;kubectl scale --replicas=3 ds &amp;lt;name&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 namespace pods상세 보기&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl describe pods -n audacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;resources-deletion&quot; data-ke-size=&quot;size23&quot;&gt;Resources Deletion&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delete pod&lt;/td&gt;
&lt;td&gt;kubectl delete pod/&amp;lt;pod-name&amp;gt; -n &amp;lt;my-namespace&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yaml 파일로 만들어진 pod, deployment, service 등 삭제&lt;/td&gt;
&lt;td&gt;kubectl delete -f &amp;lt;nameofyaml&amp;gt;.yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yaml 파일로 만들어진 pod, deployment, service 등 생성&lt;/td&gt;
&lt;td&gt;kubectl apply -f &amp;lt;nameofyam&amp;gt;.yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete pod by force&lt;/td&gt;
&lt;td&gt;kubectl delete pod/&amp;lt;pod-name&amp;gt; --grace-period=0 --force&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete pods by labels&lt;/td&gt;
&lt;td&gt;kubectl delete pod -l &amp;lt;key&amp;gt;=&amp;lt;label&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete deployments by labels&lt;/td&gt;
&lt;td&gt;kubectl delete deployment -l &amp;lt;key&amp;gt;=&amp;lt;label&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete all resources filtered by labels&lt;/td&gt;
&lt;td&gt;kubectl delete pods,services -l &amp;lt;key&amp;gt;=&amp;lt;label&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete resources under a namespace&lt;/td&gt;
&lt;td&gt;kubectl -n &amp;lt;namespace&amp;gt; delete po,svc --all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eks cluster 삭제&lt;/td&gt;
&lt;td&gt;eksctl delete cluster --name &amp;lt;&amp;lt;클러스터 명&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;예) eksctl delete cluster --name eksctl-demo&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;service&quot; data-ke-size=&quot;size23&quot;&gt;Service&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List all services&lt;/td&gt;
&lt;td&gt;kubectl get services --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List service endpoints&lt;/td&gt;
&lt;td&gt;kubectl get endpoints --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get service detail in YAML&lt;/td&gt;
&lt;td&gt;kubectl get service &amp;lt;servicename&amp;gt; -n &amp;lt;namespace&amp;gt; -o yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get service cluster ip&lt;/td&gt;
&lt;td&gt;kubectl get service &amp;lt;servicename&amp;gt; -n &amp;lt;namespace&amp;gt; -o go-template='{{.spec.clusterIP}}'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get service cluster port&lt;/td&gt;
&lt;td&gt;kubectl get service &amp;lt;servicename&amp;gt; -n &amp;lt;namespace&amp;gt; -o go-template='{{(index .spec.ports 0).port}}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 deployment를 clusterip로 expose하기&lt;/td&gt;
&lt;td&gt;kubectl expose deployment &amp;lt;&amp;lt;deployment name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;expose&amp;nbsp;deployment&amp;nbsp;canary-v1&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 service를 yaml로 확인하기&lt;/td&gt;
&lt;td&gt;kubectl get service &amp;lt;&amp;lt;service name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt; -o yaml&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;get&amp;nbsp;service&amp;nbsp;canary-v1&amp;nbsp;-n&amp;nbsp;udacity&amp;nbsp;-o&amp;nbsp;yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;events-metrics&quot; data-ke-size=&quot;size23&quot;&gt;Events &amp;amp; Metrics&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;View all events&lt;/td&gt;
&lt;td&gt;kubectl get events --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List Events sorted by timestamp&lt;/td&gt;
&lt;td&gt;kubectl get events --sort-by=.metadata.creationTimestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;namespace-security&quot; data-ke-size=&quot;size23&quot;&gt;Namespace &amp;amp; Security&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 326px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;List authenticated contexts&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl config get-contexts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Load context from config file&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl get cs --kubeconfig &amp;lt;kubeconfig file&amp;gt;.yml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 90px;&quot;&gt;
&lt;td style=&quot;height: 90px;&quot;&gt;기본 config 파일 수정&lt;/td&gt;
&lt;td style=&quot;height: 90px;&quot;&gt;export KUBECONFIG=&quot;&amp;lt;&amp;lt;config 파일 위치&amp;gt;&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;ex) &lt;br /&gt;export KUBECONFIG=&quot;C:\\Users\\home\\.kube\config&quot;&lt;br /&gt;kubectl config view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Switch context&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl config use-context &amp;lt;cluster-name&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Delete the specified context&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl config delete-context &amp;lt;cluster-name&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;List all namespaces defined&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl get namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;namespace 생성&lt;/td&gt;
&lt;td style=&quot;height: 56px;&quot;&gt;kubectl create namespace &amp;lt;&amp;lt;namespace_name&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;예)kubectl&amp;nbsp;create&amp;nbsp;namespace&amp;nbsp;monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;기본 namespace 변경하기&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl config set-context --current --namespace=&amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;config&amp;nbsp;set-context&amp;nbsp;--current&amp;nbsp;--namespace=udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;List certificates&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl get csr --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;aws IAM User 변경&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;kubectl&amp;nbsp;edit&amp;nbsp;configmap&amp;nbsp;aws-auth&amp;nbsp;-n&amp;nbsp;kube-system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;cofigmap 조회&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;kubectl&amp;nbsp;get&amp;nbsp;configmap&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;configmap 상세 조회&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;kubectl&amp;nbsp;describe&amp;nbsp;configmap/canary-config-v1&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;Intermediate Commands&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run curl test temporarily&lt;/td&gt;
&lt;td&gt;kubectl run --rm mytest --image=yauritux/busybox-curl -it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run wget test temporarily&lt;/td&gt;
&lt;td&gt;kubectl run --rm mytest --image=busybox -it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run nginx deployment with 2 replicas&lt;/td&gt;
&lt;td&gt;kubectl run my-nginx --image=nginx --replicas=2 --port=80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set namespace preference&lt;/td&gt;
&lt;td&gt;kubectl config set-context $(kubectl config current-context) --namespace=&amp;lt;ns1&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List everything&lt;/td&gt;
&lt;td&gt;kubectl get all --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get all services&lt;/td&gt;
&lt;td&gt;kubectl get service --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show nodes with labels&lt;/td&gt;
&lt;td&gt;kubectl get nodes --show-labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validate YAML file with dry run&lt;/td&gt;
&lt;td&gt;kubectl create --dry-run --validate -f &amp;lt;YAML File&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kubectl run shell command&lt;/td&gt;
&lt;td&gt;kubectl exec -it &amp;lt;podname&amp;gt; -- ls -l /etc/hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get system conf via configmap&lt;/td&gt;
&lt;td&gt;kubectl -n kube-system get cm kubeadm-config -o yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get deployment YAML&lt;/td&gt;
&lt;td&gt;kubectl -n &amp;lt;namespace&amp;gt; get deployment &amp;lt;deploymentname&amp;gt; -o yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain resource&lt;/td&gt;
&lt;td&gt;kubectl explain &amp;lt;resource&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open a bash terminal in a pod&lt;/td&gt;
&lt;td&gt;kubectl exec -it &amp;lt;pod&amp;gt; -n &amp;lt;namespace&amp;gt; sh&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;kubectl &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #0086b3;&quot;&gt;exec&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt; --stdin --tty hello-8445fd55cb-s9bps -- /bin/sh&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check pod environment variables&lt;/td&gt;
&lt;td&gt;kubectl exec &amp;lt;pod&amp;gt; -n &amp;lt;namespace&amp;gt; env&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get pods sorted by restart count&lt;/td&gt;
&lt;td&gt;kubectl get pods --sort-by='.status.containerStatuses[0].restartCount' --all-namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List all container images&lt;/td&gt;
&lt;td&gt;kubectl get pods &amp;ndash;all-namespaces -o jsonpath=&amp;ldquo;{..image}&amp;rdquo; | tr -s &amp;lsquo;[[:space:]]&amp;rsquo; &amp;lsquo;\n&amp;rsquo; | sort | uniq -d&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 id=&quot;intermediate-commands&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Registry Commands&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 72px;&quot;&gt;
&lt;td style=&quot;height: 72px;&quot;&gt;업로드할 registry image tag&lt;/td&gt;
&lt;td style=&quot;height: 72px;&quot;&gt;docker image tag &amp;lt;image name&amp;gt;:&amp;lt;image version&amp;gt; &amp;lt;registry url&amp;gt;:&amp;lt;registry port&amp;gt;/&amp;lt;name&amp;gt;/&amp;lt;image name&amp;gt;:&amp;lt;image version&amp;gt;&lt;br /&gt;&lt;br /&gt;ex)docker image tag image-name:10.1.1 mysite.registry.com:4000/myname/image-name:10.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;registry에 image push&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;docker image push mysite.registry.com:4000/myname/image-name:10.1.1&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;diable https registry&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;//docker&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&quot;insecure-registries&quot;&amp;nbsp;:&amp;nbsp;[&quot;myregistrydomain.com:5000&quot;]&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//podman v2&lt;br /&gt;[[registry]]&lt;br /&gt;location = &quot;myregistrydomain.com:5000&quot;&lt;br /&gt;insecure&amp;nbsp;=&amp;nbsp;true&lt;br /&gt;&lt;br /&gt;snap : /var/snap/docker/current/config/deamon.json&lt;br /&gt;podman: /etc/containers/registries.conf&lt;br /&gt;default: /etc/docker/deamon.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;download the custom registry image&lt;/td&gt;
&lt;td&gt;&lt;span&gt;docker pull myregistrydomain.com:5000/myname/image-name:10.1.1&lt;/span&gt;&lt;br /&gt;podman pull myregistrydomain.com:5000/myname/image-name:10.1.1&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;h3 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;Logs Commands&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;특정 pod logs 확인&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;kubectl logs pod/ops-view-655b7b5cc8-6kh7l -n udacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&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 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;user management Commands&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;특정 namespace serviceaccount 가져오기&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl get serviceaccount -n udacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;service account 상세 보기&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl describe serviceaccount kube-ops-view -n udacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;service account 만들기&lt;/td&gt;
&lt;td&gt;kubectl describe serviceaccount &amp;lt;&amp;lt;account name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;describe&amp;nbsp;serviceaccount&amp;nbsp;ops-view&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clusterrolebinding 만들기&lt;br /&gt;(role과 service account 연결)&lt;br /&gt;&lt;a href=&quot;https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;kubectl create clusterrolebinding &amp;lt;&amp;lt;binding name&amp;gt;&amp;gt; --clusterrole=&amp;lt;&amp;lt;target role&amp;gt;&amp;gt; --serviceaccount &amp;lt;&amp;lt;target service account&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;name space&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;create&amp;nbsp;clusterrolebinding&amp;nbsp;ops-view-binding&amp;nbsp;--clusterrole=cluster-admin&amp;nbsp;--serviceaccount&amp;nbsp;udacity:ops-view&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clusterrolebinding 상세 보기&lt;/td&gt;
&lt;td&gt;kubectl describe clusterrolebinding &amp;lt;&amp;lt;binding name&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;describe&amp;nbsp;clusterrolebinding&amp;nbsp;ops-view-binding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;role binding된 service account를 특정 pod에 붙이기 위해서는 yml spec이하에 serviceAccountName을 기입하고 재 apply 해야함&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;apiVersion&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;apps/v1&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;Deployment&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;ops-view&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;udacity&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;spec&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;replicas&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;1&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;selector&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;matchLabels&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;ops-view&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;labels&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;ops-view&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;spec&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;serviceAccountName&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;ops-view&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;containers&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;ops-view&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;image&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;hjacobs/kube-ops-view&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;ports&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; - &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;containerPort&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #b5cea8;&quot;&gt;8080&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;restartPolicy&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;Always&lt;/span&gt;&lt;/div&gt;
&lt;/div&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 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;Deployment&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 60px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;특정 namespace deployment 가져오기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl describe deployment/nginx-basic -n udacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;특정 deployment pods 삭제하기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl delete deployment/nginx-basic -n udacity&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;특정 deployment의 docker version update하기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;kubectl set image deployment &amp;lt;&amp;lt;deployment name&amp;gt;&amp;gt; &amp;lt;&amp;lt;container name&amp;gt;&amp;gt;=&amp;lt;&amp;lt;image:version&amp;gt;&amp;gt; --record -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl set image deployment nginx-rolling nginx=nginx:1.21.1 --record -n udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollout 상태 확인하기&lt;/td&gt;
&lt;td&gt;kubectl rollout status deployment/&amp;lt;&amp;lt;name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;rollout&amp;nbsp;status&amp;nbsp;deployment/nginx-rolling&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollout 일시정지 시키기&lt;/td&gt;
&lt;td&gt;kubectl rollout pause deployment/&amp;lt;&amp;lt;name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;namespace&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;kubectl&amp;nbsp;rollout&amp;nbsp;pause&amp;nbsp;deployment/nginx-rolling&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollout 재실행&lt;/td&gt;
&lt;td&gt;kubectl&amp;nbsp;rollout&amp;nbsp;resume&amp;nbsp;deployment/nginx-rolling&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollout 취소&lt;/td&gt;
&lt;td&gt;kubectl&amp;nbsp;rollout&amp;nbsp;undo&amp;nbsp;deployment/nginx-rolling&amp;nbsp;-n&amp;nbsp;udacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollout 기록 보기&lt;/td&gt;
&lt;td&gt;kubectl rollout history deployment/nginx-rolling -n udacity&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;Secrets&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 60px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Secrets list 갖어오기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #263238; color: #eceff1;&quot;&gt;kubectl get secrets -A&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Secrets 삭제하기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;kubectl delete secrets &amp;lt;&amp;lt;name&amp;gt;&amp;gt; -n &amp;lt;&amp;lt;name space&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;예)kubectl delete secrets additional-scrape-configs -n monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;secret 추가하기&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;kubectl create secret generic &amp;lt;&amp;lt;name&amp;gt;&amp;gt; --from-file=&amp;lt;&amp;lt;file명&amp;gt;&amp;gt; --namespace &amp;lt;&amp;lt;name space&amp;gt;&amp;gt;&lt;br /&gt;예) kubectl create secret generic additional-scrape-configs --from-file=prometheus-additional.yaml --namespace monitoring&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 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;Helm 명령어&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;helm의 모든 리스트&lt;/td&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;helm list -A&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;helm package 삭제&lt;/td&gt;
&lt;td&gt;helm&amp;nbsp;uninstall&amp;nbsp;prometheus-blackbox-exporter&amp;nbsp;-n&amp;nbsp;monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;helm package install&lt;/td&gt;
&lt;td&gt;helm install &amp;lt;&amp;lt;name&amp;gt;&amp;gt; &amp;lt;&amp;lt;package-name&amp;gt;&amp;gt; -f &quot;&amp;lt;&amp;lt;config file&amp;gt;&amp;gt;&quot; --namespace &amp;lt;&amp;lt;name space&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;예) helm install prometheus-blackbox-exporter prometheus-community/prometheus-blackbox-exporter -f &quot;blackbox_values.yaml&quot; --namespace monitoring&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 id=&quot;intermediate-commands&quot; data-ke-size=&quot;size23&quot;&gt;eks 명령어&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;aws에 eks cluster 정보를 local kubernetes에 업데이트 한다&lt;/td&gt;
&lt;td&gt;aws eks update-kubeconfig --region &amp;lt;&amp;lt;region명&amp;gt;&amp;gt; --name &amp;lt;&amp;lt;cluster명&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;예)aws&amp;nbsp;eks&amp;nbsp;update-kubeconfig&amp;nbsp;--region&amp;nbsp;us-east-2&amp;nbsp;--name&amp;nbsp;udacity-cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&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;</description>
      <category>AWS</category>
      <category>command</category>
      <category>K8S</category>
      <category>Kubernetes</category>
      <category>명령어</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/136</guid>
      <comments>https://enumclass.tistory.com/136#entry136comment</comments>
      <pubDate>Sun, 6 Feb 2022 02:02:49 +0900</pubDate>
    </item>
    <item>
      <title>AWS CLI 명령어 모음 (계속)</title>
      <link>https://enumclass.tistory.com/117</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;aws-cli&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 360px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;명령어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 180px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 180px;&quot;&gt;작동중인 instance에서 public ip 얻어오기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 180px;&quot;&gt;$&amp;nbsp;aws&amp;nbsp;ec2&amp;nbsp;describe-instances&amp;nbsp;--filters&amp;nbsp;Name=instance-state-name,Values=running&amp;nbsp;--query&amp;nbsp;Reservations[*].Instances[*].PublicIpAddress&lt;br /&gt;&lt;br /&gt;[ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;34.220.110.131&quot; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;] &lt;br /&gt;]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws에 사용자 등록하기 default&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws configure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;추가사용자 등록&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws configure --profile &amp;lt;profile-name&amp;gt;&lt;br /&gt;&lt;br /&gt;명령어 실행시 기본 사용자 정의하기&lt;br /&gt;export AWS_PROFILE=&amp;lt;&amp;lt;eks-user&amp;gt;&amp;gt;&lt;br /&gt;or&lt;br /&gt;export AWS_PROFILE=&amp;lt;&amp;lt;defualt&amp;gt;&amp;gt;&lt;br /&gt;or&lt;br /&gt;(windows)&lt;br /&gt;setx AWS_PROFILE &amp;lt;&amp;lt;eks-user&amp;gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;현재 설정 정보&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws configure list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;모든 프로파일 리스트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;asw configure list-profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;현 사용자 Region 변경하기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws configure set region us-east-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws iam user 정보 얻어오기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws iam list-users&lt;br /&gt;or&lt;br /&gt;aws iam list-users --profile &amp;lt;profile-name&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;현재 사용중인 iam&amp;nbsp; 정보 얻기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws sts get-caller-identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws regions&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;aws ec2 describe-regions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;aws zones&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;aws ec2 describe-availability-zones --region &amp;lt;&amp;lt;region name&amp;gt;&amp;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;h2 data-ke-size=&quot;size26&quot;&gt;eksctl&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 360px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;명령어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eks 만들기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eksctl create cluster --name &amp;lt;&amp;lt;eksctl-demo&amp;gt;&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eks 상태보기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eksctl utils describe-stacks --region=&amp;lt;&amp;lt;us-east-2&amp;gt;&amp;gt; --cluster=&amp;lt;&amp;lt;eksctl-demo&amp;gt;&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eks 삭제&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eksctl delete cluster &amp;lt;&amp;lt;eksctl-demo&amp;gt;&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eks cluster 리스트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;eksctl get cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;nbsp;&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;</description>
      <category>AWS</category>
      <category>AWS</category>
      <category>CLI</category>
      <category>eksctl</category>
      <category>명령어</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/117</guid>
      <comments>https://enumclass.tistory.com/117#entry117comment</comments>
      <pubDate>Sun, 6 Feb 2022 01:51:59 +0900</pubDate>
    </item>
    <item>
      <title>Android ViewModel &amp;amp; ListView 사용하기</title>
      <link>https://enumclass.tistory.com/248</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewModel을 사용해서 fragment 작동 동안 데이터를 유지하고 화면에 표시한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ViewModel 사용법을 배운다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ListView를 통해서 List형 Data를 표현하고 인터렉션 하는 것을 배운다&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;a href=&quot;https://enumclass.tistory.com/244&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment 설정하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/245&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment Navigation with Action&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/246&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.25 - [Android] - Android Fragment with Arguments&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.03 - [Android] - Android View Binding (뷰 바인딩)&lt;/a&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;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;ViewModel 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewModel은 안드로이드 기본 API에 포함되어있지 않습니다. jetpack의 lifecycle package의 구성요소로 포함되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/jetpack/androidx/releases/lifecycle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/jetpack/androidx/releases/lifecycle&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;viewmodel을 app 하위에 있는 build.gradle에 포함시켜 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ds0mNJ/btrstWKfQPd/YPKJlwUXweUUuWJAyX7J70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ds0mNJ/btrstWKfQPd/YPKJlwUXweUUuWJAyX7J70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ds0mNJ/btrstWKfQPd/YPKJlwUXweUUuWJAyX7J70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fds0mNJ%2FbtrstWKfQPd%2FYPKJlwUXweUUuWJAyX7J70%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;1020&quot; height=&quot;274&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Gradle Sync를 시켜주면 사용을 위한 준비는 끝이 났습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643983031283&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {

...
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
...
}&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;ViewModel Class 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;com.example.fragmentsetup.data package에 DataviewModel.kt를 생성해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643983120029&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup.data

import androidx.lifecycle.ViewModel

public class DataViewModel : ViewModel() {

    val lists : MutableList&amp;lt;String&amp;gt; = ArrayList&amp;lt;String&amp;gt;()
    val size get() = lists.size

    public fun addMessage(message:String){
        lists.add(message)
    }

    public fun getMessages() : List&amp;lt;String&amp;gt;{
        return lists.toList()
    }

    public fun getMessagesAt(idx : Int) : String{
        return lists.get(idx)
    }

    public fun removeDataAt(index:Int){
        lists.removeAt(index)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data를 저장하게될 MutableList와&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 추가시킬 수 있는 addMessage&lt;/li&gt;
&lt;li&gt;데이터를 ImmutableList로 갖어올 getMessage&lt;/li&gt;
&lt;li&gt;하나의 데이터만을 갖어올 getMessageAt&lt;/li&gt;
&lt;li&gt;특정 위치의 데이터를 삭제할 removeDataAt&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드를 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 ViewModel Class를 사용해 보겠습니다.&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;ViewModel을 Fragment에 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 화면인 TestFragment.kt에 다음 내용을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643983436783&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {
...
    lateinit var viewModel: DataViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
...
        viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)

        binding.button.setOnClickListener {
...
        }

        return binding.root
    }

...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;viewModel을 onCreateView이후에 생성할 예정임으로 lateinit을 지정해 주었습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;lateinit var viewModel: DataViewModel&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onCreateView에서 DataViewModel을 인스턴스화 해줘야 합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 인스턴스 생성코드와는 많이 다릅니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;ViewModel을 깊게 알아보자&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ViewModel을 lifecycle과 연동시키기 위한 ViewModelStoreOwner를 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,

....
// Lazily recreated from NonConfigurationInstances by getViewModelStore()
private ViewModelStore mViewModelStore;
private ViewModelProvider.Factory mDefaultFactory;

@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException(&quot;Your activity is not yet attached to the &quot;
                + &quot;Application instance. You can't request ViewModel before onCreate call.&quot;);
    }
    ensureViewModelStore();
    return mViewModelStore;
}

@SuppressWarnings(&quot;WeakerAccess&quot;) /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}
....&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 ViewModelStore는 내부적으로 HashMap을 사용해서 해당 lifecycle에서 사용되는 ViewModel을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 코드를 붙여놓은 이유는 viewModel의 Lifecycle을 명확히 하기 위해서 입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, ComponentActivity가 instance 종료되면 해당 클래스에서 유지하는 attribute인 mViewModelStore도 같이 삭제 되게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;viewModelStore에 get을 통해 얻어온 modelClass를 instance 생성해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1643984829585&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public open class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
) {
   ....
   
    public open operator fun &amp;lt;T : ViewModel&amp;gt; get(key: String, modelClass: Class&amp;lt;T&amp;gt;): T {
        var viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel)
            return viewModel as T
        } else {
            @Suppress(&quot;ControlFlowWithEmptyBody&quot;)
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        viewModel = if (factory is KeyedFactory) {
            factory.create(key, modelClass)
        } else {
            factory.create(modelClass)
        }
        store.put(key, viewModel)
        return viewModel
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 주의 깊게 봐야할 부분은 store.put &amp;lt;- 이 부분입니다. 앞서서 설명한 ViewModelStore에 key value hashmap을 통해서 put되는 것을 확인할 수있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 hashmap은 thread safe하지 않습니다. viewModel은 thread-safe하게 design되지 않은것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해서 관련 문서를 찾아 보았는데, 구글에서도 thread에서 사용하지 말고 Main Thread 하나에서만 사용하라고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;By design,&amp;nbsp;&lt;br /&gt;Android View objects are not thread-safe&lt;br /&gt;. An app is expected to create, use, and destroy UI objects, all on the main thread. If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/topic/performance/threads#:~:text=By%20design%2C%20Android%20View%20objects,crashes%2C%20and%20other%20undefined%20misbehavior.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/topic/performance/threads#:~:text=By%20design%2C%20Android%20View%20objects,crashes%2C%20and%20other%20undefined%20misbehavior.&lt;/a&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;viewModel이 얼마나 오래 살수있는지에 대한 Scope입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {
....

viewModel = ViewModelProvider(this).get(DataViewModel::class.java)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 코드의 차이점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에 코드는 Fragment가 위치하는 Activity가 살아있는 동안 ViewModel이 삭제 되지 않습니다.&lt;/li&gt;
&lt;li&gt;아래 코드는 Fragment가 살아있는 동안만 ViewModel이 살수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점을 이용해서 우리는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Activity 이하에 있는 모든 fragments는 동일 ViewModel을 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;Data Size가 큰 Activity 또는 Fragement가 Destroy되지 않는 다면 메모리 Leak이 될 수 있다&lt;/li&gt;
&lt;/ul&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://enumclass.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2020.03.27 - [Android] - android memory leak 처리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context를 다른 컴포넌트에 위임할 때는 WeakReferece를 사용하세요.&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;ViewModel에 Data 추가하기&lt;/h2&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;450&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZGgdC/btrsrUT3ghi/ILOXLb393dilaGjpUTwSxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZGgdC/btrsrUT3ghi/ILOXLb393dilaGjpUTwSxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZGgdC/btrsrUT3ghi/ILOXLb393dilaGjpUTwSxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZGgdC%2FbtrsrUT3ghi%2FILOXLb393dilaGjpUTwSxk%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;268&quot; height=&quot;221&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment_test에서 Next버튼을 누르면 EditText에 있는 값을 viewModel에 넣도록 해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {
...
    lateinit var viewModel: DataViewModel

    override fun onCreateView(
...
    ): View? {
...
        binding.button.setOnClickListener {
            val str = binding.editTextTextPersonName.text.toString() ?: &quot;hello&quot;
            viewModel.addMessage(str)
...
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;viewModel.addMessage를 통해서 간단히 데이터를 넣는 것을 확인 할 수있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터는 이제 fragement_second와 fragment_third에서도 공유가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 ViewModelProvider를 생성할때 fragment lifecycle이 아닌 activity lifecycle에 연동을 시켰기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 'requireActivity'를 사용했다는 것을 잊지 마세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 방법으로 SecondFragement.kt에도 viewModel을 생성해 주겠습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class SecondFragment : Fragment() {
    lateinit var viewModel : DataViewModel
    var _binding : FragmentSecondBinding? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
//        val view = inflater.inflate(R.layout.fragment_second, container, false)
        _binding = FragmentSecondBinding.inflate(inflater, container, false)
        val view = binding.root

        viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)
        val list = viewModel.getMessages()
        list.forEach { str -&amp;gt;
            Log.d(this.javaClass.name, str)
        }
        
        ....&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 존재하던 findbyid를 view binding으로 변경하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 앞서와 동일한 방식으로 viewModel을 가져왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 혼동하지 말아야 할 것은&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;viewModel = ViewModelProvider(requireActivity()).get(DataViewModel::class.java)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 코드를 썻다고 해서 TestFragment와 다른 viewModel을 갖어온것이 아니라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 Activity lifecycle의 동일한 viewModel을 갖어 왔다는 것을 잊지 마세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 그냥 get만 하면 안될까요? 네 안됩니다. get을 위해서는 ViewModelProvider를 얻어와야 하는데 해당 Provider가 Activity에 종속되기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위까지 만들고 코드를 돌려보면&lt;/p&gt;
&lt;pre id=&quot;code_1643986779606&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        val list = viewModel.getMessages()
        list.forEach { str -&amp;gt;
            Log.d(this.javaClass.name, str)
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용으로인해서 LogCat에 TestFragement에서 입력한 값이 삭제 되지 않고 쌓이는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 LogCat에 log로 보여지는 내용들을 ListView에 보여주는 일만 남았습니다.&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;Listview 설정하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3208&quot; data-origin-height=&quot;2324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mrr0q/btrswVdlxep/AfpAr0tguwc8X2tsBlbpj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mrr0q/btrswVdlxep/AfpAr0tguwc8X2tsBlbpj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mrr0q/btrswVdlxep/AfpAr0tguwc8X2tsBlbpj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmrr0q%2FbtrswVdlxep%2FAfpAr0tguwc8X2tsBlbpj1%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;802&quot; height=&quot;581&quot; data-origin-width=&quot;3208&quot; data-origin-height=&quot;2324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment_text(왼쪽)에서 Next버튼을 누르면 TextEdit에 있던 데이터가 fragment_second(오른쪽)로 넘어가서 지금까지 Next로 저장했던 Data를 표시하는 부분을 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment_second.xml을 열어서&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmuamT/btrstXbisEX/4LP4BIA6wnbDtQ0etHvIsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmuamT/btrstXbisEX/4LP4BIA6wnbDtQ0etHvIsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmuamT/btrstXbisEX/4LP4BIA6wnbDtQ0etHvIsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmuamT%2FbtrstXbisEX%2F4LP4BIA6wnbDtQ0etHvIsk%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;448&quot; height=&quot;367&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼 하단에 Listview를 삽입하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643986489902&quot; class=&quot;xml&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.SecondFragment&quot;&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;TextView
        android:id=&quot;@+id/textView2&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:text=&quot;@string/fragment_second&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button2&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_marginTop=&quot;32dp&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2&quot;
        app:layout_constraintTop_toTopOf=&quot;@+id/textView2&quot; /&amp;gt;

    &amp;lt;ListView
        android:id=&quot;@+id/listview&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_marginTop=&quot;64dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView2&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/button2&quot; /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 요즘은 recycleview를 많이 사용하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 안드로이드 List 화면의 근본은 ListView니까 Listview를 사용했습니다. 이후에 MVVM 모델을 반영하면서 RecycleView로 변경하겠습니다.&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;ListView를 위한 개체를 만들자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Listview는 ListView에 표시될 여러개의 개체가 리스트 형태로 표현되는 방식으로 여러개의 데이터를 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 하기위해서 하나의 개체에 대한 Layout을 만들어 줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;layout으로 element_sigle.xml을 하나 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;249&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr8JkW/btrswWXt1sa/6goGaXvSUzRgtOevz1kja1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr8JkW/btrswWXt1sa/6goGaXvSUzRgtOevz1kja1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr8JkW/btrswWXt1sa/6goGaXvSUzRgtOevz1kja1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr8JkW%2FbtrswWXt1sa%2F6goGaXvSUzRgtOevz1kja1%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;249&quot; height=&quot;128&quot; data-origin-width=&quot;249&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 개체는 TextView를 이용해서 앞서 fragment_text.xml에서 넘어온 데이터를 표현할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgejOQ/btrswWiRXlC/zMF3xlXFhF9fdvt9ESjBq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgejOQ/btrswWiRXlC/zMF3xlXFhF9fdvt9ESjBq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgejOQ/btrswWiRXlC/zMF3xlXFhF9fdvt9ESjBq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgejOQ%2FbtrswWiRXlC%2FzMF3xlXFhF9fdvt9ESjBq1%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;644&quot; height=&quot;543&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LinearLayout하나에 TextView하나를 설정해 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TextView의 id는 text_string이라고 지정하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643986489903&quot; class=&quot;xml&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:id=&quot;@+id/directions&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;horizontal&quot;
    android:background=&quot;?attr/selectableItemBackground&quot;
    &amp;gt;

    &amp;lt;TextView
        android:id=&quot;@+id/text_string&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_margin=&quot;8dp&quot;
        android:layout_weight=&quot;1&quot;
        android:lineSpacingExtra=&quot;8sp&quot;
        android:text=&quot;TextView&quot;
        android:textSize=&quot;24sp&quot;
        android:textStyle=&quot;bold&quot; /&amp;gt;
&amp;lt;/LinearLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Listview와 그 Listview안에 표시할 객체까지 구조를 잡았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위의 layout을 사용하는 List부분을 작성해 보겠습니다.&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;ListViewAdapter 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;com.example.fragmentsetup에 ListViewAdapter를 만들어 줍니다. 해당 view는 BaseAdapter를 생성 받으면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Adapter가 다양하게 있지만, 역시 안드로이드 List의 근본 Adapter는 BaseAdapter겠죠!&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

....

class ListViewAdapter(val context : Context, var list: DataViewModel) : BaseAdapter() {

    val layoutInflater : LayoutInflater = LayoutInflater.from(context)

    override fun getCount(): Int {
        return list.size
    }

    override fun getItem(idx : Int): String {
        return list.getMessagesAt(idx)
    }

    override fun getItemId(idx: Int): Long {
        return idx.toLong()
    }

    override fun getView(position: Int, convertView: View?, container: ViewGroup?): View? {
        var view : View?

        if (convertView == null){
            view = layoutInflater.inflate(R.layout.element_single,container,false)
        } else {
            view = convertView
        }

        view?.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string)?.text = list.getMessagesAt(position)

        return view;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 간단한 Adapter를 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 Method는 다음 2가지 입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;getCount : 목록에 표시될 전체 갯수를 알려줍니다.&lt;/li&gt;
&lt;li&gt;getView : ListView에 붙이게 할 개체를 갖어옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List를 만들때 위의 두가지만 생각합면 됩니다. 물론 view holder라는 것을 만들어서 리스트 개체를 별도 관리 해야겠지만, 화면에 리스트를 표시하기위해 반듯이 필요한 행위가 아니라는 점을 이해하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class ListViewAdapter(val context : Context, var list: DataViewModel) : BaseAdapter() {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ListViewAdapter에서 2가지 파라메터를 받았는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째는 Adapter를 사용할 Fragment 또는 Activity의 context입니다. 이를 이용해서 layoutInfater를 활용 할 수있게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;val layoutInflater : LayoutInflater = LayoutInflater.from(context)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;override fun getView(position: Int, convertView: View?, container: ViewGroup?): View? {
    var view : View?

    if (convertView == null){
        view = layoutInflater.inflate(R.layout.element_single,container,false)
    } else {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getView가 불리워질때 현재 만들 리스트의&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;position : 몇번째 개체를 만드는지 알려줌으로써 목록 데이터에서 몇번째를 갖어 오면 되는지 알 수 있게 됩니다.&lt;/li&gt;
&lt;li&gt;convertView : 만약 앞서서 이미 만든적이 있는 개체라면 해당 View resource를 재활용 하게 합니다.&lt;/li&gt;
&lt;li&gt;container : 해당 view가 붙게될 parent layout view를 갖어오게 되는데 여기서는 ListView를 갖어오게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보를 알려주게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 정보를 바탕으로 저희는 viewModel에서 정보를 얻어와서 화면에 표시하게 만들어 줄 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;view?.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string)?.text = list.getMessagesAt(position)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 viewModel에서 특정 position의 String Data를 목록 객체 중 text_string이라고하는 id를 갖는 View에 데이터를 넣어주는 행위를 하게 합니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;ViewModel 데이터를 삭제하자&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpkzdM/btrsyG0HcU6/9sgkZYhT8PRPJxQWjIn51k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpkzdM/btrsyG0HcU6/9sgkZYhT8PRPJxQWjIn51k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpkzdM/btrsyG0HcU6/9sgkZYhT8PRPJxQWjIn51k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpkzdM%2FbtrsyG0HcU6%2F9sgkZYhT8PRPJxQWjIn51k%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;334&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 특정 Data를 longClick하면 목록에서 삭제하는 기능을 만들어 보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 만든 getView에 다음과 같은 코드를 넣어주세요&lt;/p&gt;
&lt;pre id=&quot;code_1643988027396&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    override fun getView(position: Int, convertView: View?, container: ViewGroup?): View? {

...
        view?.setOnLongClickListener() {
            Toast.makeText(context, &quot;long clicked&quot; + position, Toast.LENGTH_SHORT).show()
            deleteAt(position)
            true
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 위치의 개체를 롱클릭하면 사용자 입력이 몇번째 개체에서 되었음을 알 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 position의 데이터를 작세하는 기능을 만들어 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643988278840&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ListViewAdapter(val context : Context, var list: DataViewModel) : BaseAdapter() {

...

    private fun deleteAt(idx: Int){
        list.removeDataAt(idx)
        notifyDataSetChanged()
    }
 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 deleteAt 메서드를 부르면 ViewModel에서 특정 영역의 데이터를 삭제 하고 notifyDataSetchanged()를 통해서, 해당 Adapter의 데이터가 새로 생성되어야 함을 공지하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다시 목록을 그리게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 notifyDataSetchanged는 이후에 MVVM live data를 통해서 최적화 될 것입니다.&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;목록 개체를 click하면 Data를 ThirdFragment로 넘겨주자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 onLongClick을 알아보았는데요. 같은 방법으로 onClick도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 개체 Element를 클릭하면 ThirdFragment로 Data를 넘겨주는 기능을 간단하게 만들어 주겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/246&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.25 - [Android] - Android Fragment with Arguments&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 자세한 설명은 이미 한 부분이라 자세한 설명없이 수정된 부분만 공유하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IHa98/btrsysIhIqm/1c97MUtfn2H0CgjfGDQ5kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IHa98/btrsysIhIqm/1c97MUtfn2H0CgjfGDQ5kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IHa98/btrsysIhIqm/1c97MUtfn2H0CgjfGDQ5kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIHa98%2FbtrsysIhIqm%2F1c97MUtfn2H0CgjfGDQ5kk%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;440&quot; height=&quot;394&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation graph에서 thirdFragment에 Arguments를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Arguments를 통해서 Action에 데이터를 포함해서 넘겨줍니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;view?.setOnClickListener() {
    val str = it.findViewById&amp;lt;TextView&amp;gt;(R.id.text_string).text.toString()
    Toast.makeText(context, &quot;clicked &quot; + str, Toast.LENGTH_SHORT).show()
    val action = SecondFragmentDirections.actionSecondFragmentToThirdFragment(str)
    it.findNavController().navigate(action)
    true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 데이터를 받는 ThirdFragment에서 Arguments를 얻어와서 textView에 표시합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class ThirdFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_third, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button3).setOnClickListener {
            view.findNavController().popBackStack()
        }
        val param = ThirdFragmentArgs.fromBundle(requireArguments()).str
        view.findViewById&amp;lt;TextView&amp;gt;(R.id.textView3).text = param
        return view

    }

}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;이렇게 작동합니다.&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3360&quot; data-origin-height=&quot;1924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8eP76/btrstLhZIhE/gTLxMke8pvDQfQkV4puMQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8eP76/btrstLhZIhE/gTLxMke8pvDQfQkV4puMQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8eP76/btrstLhZIhE/gTLxMke8pvDQfQkV4puMQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8eP76%2FbtrstLhZIhE%2FgTLxMke8pvDQfQkV4puMQk%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;840&quot; height=&quot;481&quot; data-origin-width=&quot;3360&quot; data-origin-height=&quot;1924&quot;/&gt;&lt;/span&gt;&lt;/figure&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;마지막으로 on click이나 longclick과 같은 합수들은 lamda를 이용한 파라메터 입력으로 처리도 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643989083616&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        view?.setOnLongClickListener() {
            Toast.makeText(context, &quot;long clicked&quot; + position, Toast.LENGTH_SHORT).show()
            deleteAt(position)
            true
        }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1643989093748&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        val lamda : (v:View)-&amp;gt; Unit = {
            Toast.makeText(context, &quot;elements deleted&quot;, Toast.LENGTH_SHORT).show()
            deleteAt(position)
            true
        }
        view?.setOnLongClickListener(lamda)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두개의 작동은 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lamda를 파라메터로 사용할 경우 작동을 외부에서 주입을 통해 처리할 수 있는 유연성을 얻을 수 있습니다.&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;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/bd68b4ed58be24010fb79368ce9585e913a9e88a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/bd68b4ed58be24010fb79368ce9585e913a9e88a&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;ListView로 만든것을 Recycler View Adapter로 재 개발해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.12 - [Android] - Android BaseAdapter vs RecyclerAdapter 작동 원리 (RecyclerView 개발)&lt;/a&gt;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Android</category>
      <category>Baseadapter</category>
      <category>Lamda</category>
      <category>listview</category>
      <category>onclick</category>
      <category>ViewModel</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/248</guid>
      <comments>https://enumclass.tistory.com/248#entry248comment</comments>
      <pubDate>Sat, 5 Feb 2022 00:39:35 +0900</pubDate>
    </item>
    <item>
      <title>Android View Binding (뷰 바인딩)</title>
      <link>https://enumclass.tistory.com/247</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View Binding을 적용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/244&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment 설정하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/245&quot;&gt;2022.01.24 - [Android] - Android Fragment Navigation with Action&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/246&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.25 - [Android] - Android Fragment with Arguments&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 진행한 posts의 연속입니다. 마지막 포스트에있는 아래 소스 다운받고 보시면 더 편하실거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&lt;/a&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;findViewById의 단점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 만든 TestFragment.kt 파일을 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1643847913300&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_test, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener {
            val str = view.findViewById&amp;lt;EditText&amp;gt;(R.id.editTextTextPersonName).text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            view.findNavController().navigate(action)
        }
        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R.layout.fragment_test라고 하는 fragment에서 root view를 갖어 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 root view에서 onClick Event를 listen할 button을 ID로 갖어와서 Button으로 Class Casting후 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 크게 2가지 문제점이 발생하게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Not Null Safe : 만약 R.id.button이라는 ID가 없다면 setOnclickListener가 Null Point Exception이 발생하게 된다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener{....}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Not Type Safe : Casting Class가 Button이 아니라면? 개발자 실수에 의해 오류가 발생하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1643848705162&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;view.findViewById&amp;lt;Button&amp;gt;&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;viewBinding 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app 하위에 있는 build.gradle에 buildfeatures를 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643852959694&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android {
...

    buildFeatures {
        viewBinding true
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle을 수정 후 sync &amp;gt; clean Project &amp;gt; RebuildProject를 진행해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dW6eXd/btrscOsaMBx/fKXn7Dxi59vsOzqJsyKKn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dW6eXd/btrscOsaMBx/fKXn7Dxi59vsOzqJsyKKn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dW6eXd/btrscOsaMBx/fKXn7Dxi59vsOzqJsyKKn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdW6eXd%2FbtrscOsaMBx%2FfKXn7Dxi59vsOzqJsyKKn1%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;1494&quot; height=&quot;367&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 3가지를 진행하고 나면 Project Perspective에서 Activity 및 Fragment binding이 XML 명과 비슷하게 build 된것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 확인이 안되면 view bindind이 안된다고 생각하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5akpX/btrsqCRsoqr/usxZMe7c2RE52FN0MuEYF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5akpX/btrsqCRsoqr/usxZMe7c2RE52FN0MuEYF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5akpX/btrsqCRsoqr/usxZMe7c2RE52FN0MuEYF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5akpX%2FbtrsqCRsoqr%2FusxZMe7c2RE52FN0MuEYF0%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;502&quot; height=&quot;428&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;428&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fragment에 Binding 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestFragment.kt에 있는 binding 설정을 변경해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 보았던 원본 소스는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643853457634&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_test, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener {
            val str = view.findViewById&amp;lt;EditText&amp;gt;(R.id.editTextTextPersonName).text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            view.findNavController().navigate(action)
        }
        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 소스를 preBuilt된 binding소스를 이용해서 변경해 볼 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 TestFragement와 연결된 xml인 R.layout.fragment_test에서 파생된 build 파일인 FragmentTestBinding을 연결 시켜야합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643862931385&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestFragment : Fragment() {
    var _binding: FragmentTestBinding? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? { ....&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;var _binding: FragmentTestBinding? = null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와같이 작성한 이유는 이후에 onDestroyView를 통해서 null처리가 필요하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 문제는 binding하는 코드 자체에 null일경우 null exception이 가능한 코드가 될 가능성이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643863843221&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        _binding = FragmentTestBinding.inflate(inflater,container,false)
        _binding.button.setOnClickListener {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 코드를 보면 _binding에서 바로 button이라고 하는 view 객체를 접근하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 null able로 _binding이 선언되었기 때문에 잠재적으로 null exception을 발생할 여지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 임시 방편으로 null check를 넣어 줄순 있지만, 좋지 않은 방법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1643863956640&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        _binding = FragmentTestBinding.inflate(inflater,container,false)
        _binding?.button?.setOnClickListener {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;pre id=&quot;code_1643864057971&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    var _binding: FragmentTestBinding? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        _binding = FragmentTestBinding.inflate(inflater,container,false)
        binding.button.setOnClickListener {
            val str = binding.editTextTextPersonName.text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            binding.root.findNavController().navigate(action)
        }

        return binding.root
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기와 같이 binding get() 을 사용해서 null이 없음을 보여주게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643864196900&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        binding.button.setOnClickListener {
            val str = binding.editTextTextPersonName.text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            binding.root.findNavController().navigate(action)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 binding한 코드를 보면 findviewbyid와는 다르게 button을 직접 access하는 것을 볼수 있습니다. 이를 통해서 특정 view의 객체에 더빠른 접근과 typecating으로 부터 오는 잠재적 오류를 사전에 막을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 부분이 남았는데요. 바로 onDestroyView의 사용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 시점에 _binding = null 을 사용해서 객체를 해제해주는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643864284008&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(this.javaClass.name,&quot;finish view&quot;)
        _binding = null
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment는 activity의 lifecycle을 활용하지만 정확히는 activity의 한파트로써 독립적인 라이프 사이클을 갖게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/difference-between-a-fragment-and-an-activity-in-android/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.geeksforgeeks.org/difference-between-a-fragment-and-an-activity-in-android/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 activity와 다르게 onDestroyView와 onDestroy 둘다 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 onDestroy는 Class finish이후 불특정한 시점에 불리게 됨으로 Leak을 발생시킬 가능성이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 화면에서 View가 사라지는 순간 _binding을 null화 함으로써 GC시 clear가능상 상태로 만들어 줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 _binding = null을 처리해주지 않으면 아래와 같이 Leak의 대상이 되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eE4iYD/btrsfWYLlBZ/UAfnBgeEGhkaCl43oN4yM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eE4iYD/btrsfWYLlBZ/UAfnBgeEGhkaCl43oN4yM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eE4iYD/btrsfWYLlBZ/UAfnBgeEGhkaCl43oN4yM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeE4iYD%2FbtrsfWYLlBZ%2FUAfnBgeEGhkaCl43oN4yM1%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;505&quot; height=&quot;740&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1635&quot;/&gt;&lt;/span&gt;&lt;/figure&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;전제 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1643865605850&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import android.util.Log
import android.util.Log.*
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.navigation.findNavController
import com.example.fragmentsetup.databinding.FragmentTestBinding
import java.util.logging.Level.INFO
import java.util.logging.Logger


/**
 * A simple [Fragment] subclass.
 * Use the [TestFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class TestFragment : Fragment() {
    var _binding: FragmentTestBinding? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        _binding = FragmentTestBinding.inflate(inflater,container,false)
        binding.button.setOnClickListener {
            val str = binding.editTextTextPersonName.text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            binding.root.findNavController().navigate(action)
        }

        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(this.javaClass.name,&quot;finish view&quot;)
        _binding = null
    }

}&lt;/code&gt;&lt;/pre&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;1888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzR2rT/btrsrVQ3Wby/g5Ekm4At8lUcTjdV1Ekrbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzR2rT/btrsrVQ3Wby/g5Ekm4At8lUcTjdV1Ekrbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzR2rT/btrsrVQ3Wby/g5Ekm4At8lUcTjdV1Ekrbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzR2rT%2FbtrsrVQ3Wby%2Fg5Ekm4At8lUcTjdV1Ekrbk%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;426&quot; height=&quot;750&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;1888&quot;/&gt;&lt;/span&gt;&lt;/figure&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;소스는 아래에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/63ade4463beda013c96a7a85f7b48ce757f4ebc5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/63ade4463beda013c96a7a85f7b48ce757f4ebc5&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643865753310&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;GitHub - theyoung/fragmentsetup&quot; data-og-description=&quot;Contribute to theyoung/fragmentsetup development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/63ade4463beda013c96a7a85f7b48ce757f4ebc5&quot; data-og-url=&quot;https://github.com/theyoung/fragmentsetup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/imiEX/hyNhYmojZI/Sve0kRQfy8KwO47yoRdIHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/63ade4463beda013c96a7a85f7b48ce757f4ebc5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/63ade4463beda013c96a7a85f7b48ce757f4ebc5&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/imiEX/hyNhYmojZI/Sve0kRQfy8KwO47yoRdIHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245');&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;GitHub - theyoung/fragmentsetup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to theyoung/fragmentsetup 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;p data-ke-size=&quot;size16&quot;&gt;ViewModel을 활용해서 ListView를 표현해 보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.05 - [Android] - Android ViewModel &amp;amp; ListView 사용하기&lt;/a&gt;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Android</category>
      <category>findviewbyid</category>
      <category>fragment</category>
      <category>ViewBinding</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/247</guid>
      <comments>https://enumclass.tistory.com/247#entry247comment</comments>
      <pubDate>Thu, 3 Feb 2022 14:25:34 +0900</pubDate>
    </item>
    <item>
      <title>Android Fragment with Arguments</title>
      <link>https://enumclass.tistory.com/246</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation을 이용한 Fragment의 이동시 Arguments를 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/244&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment 설정하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/245&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment Navigation with Action&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두개까지 완료 했다면 이제는 어떻게 Fragment사이 Action과 함께 Arguments를 넘길 수 있는지 알아 보고자 한다.&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;build.gradle에 plugin 등록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arguments의 전달을 위해서는 컴파일시 build 되는 파일이 필요하다.&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;우선 Top Level build.gradle을 열어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643119121297&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath &quot;com.android.tools.build:gradle:7.0.4&quot;
        classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20&quot;

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        def nav_version = &quot;2.3.5&quot;
        classpath &quot;androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version&quot;
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이&lt;/p&gt;
&lt;pre id=&quot;code_1643119146678&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        def nav_version = &quot;2.3.5&quot;
        classpath &quot;androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 넣고 gradle sync를 진행해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle sync가 완료 되었다면 이후에 app build.gradle로 이동해서&lt;/p&gt;
&lt;pre id=&quot;code_1643119195024&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'androidx.navigation.safeargs.kotlin'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plug in&amp;nbsp; 위치에&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;id 'androidx.navigation.safeargs.kotlin'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 추가해 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/jetpack/androidx/releases/navigation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/jetpack/androidx/releases/navigation&lt;/a&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;이후에 꼭! 반듯이! gradle sync를 한번더 해주자.&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;Navigation에 Argument 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 만들어 놓은 navigation_graph.xml을 열어주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CUmoD/btrrLLIj0SP/K9bzDvy5NLIZ3TNbw94cw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CUmoD/btrrLLIj0SP/K9bzDvy5NLIZ3TNbw94cw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CUmoD/btrrLLIj0SP/K9bzDvy5NLIZ3TNbw94cw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCUmoD%2FbtrrLLIj0SP%2FK9bzDvy5NLIZ3TNbw94cw0%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;605&quot; height=&quot;453&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보는것 처럼 testFragment에서 input value를 넣어주고 Next 버튼을 누르면 해당 value가 secondFragment에 넘겨주도록 설정을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한것은 파라메터를 누가 받을 것인가이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 secondFragment가 해당 값을 얻을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 secondFragment에 argument를 하나 추가해줄 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sOZfd/btrrLNGajXh/m5FtKge5Smgrut9Ushaw4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sOZfd/btrrLNGajXh/m5FtKge5Smgrut9Ushaw4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sOZfd/btrrLNGajXh/m5FtKge5Smgrut9Ushaw4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsOZfd%2FbtrrLNGajXh%2Fm5FtKge5Smgrut9Ushaw4K%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;658&quot; height=&quot;518&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;secondFragment를 클릭하고 우측에 arguments에 '+'를 클릭하고 String 타입에 seValue라고 하는 변수명을 정의해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 하고 저장을 해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B1X2e/btrrDNHVdp2/XkkXTwtxS4y0oGM6BotDDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B1X2e/btrrDNHVdp2/XkkXTwtxS4y0oGM6BotDDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B1X2e/btrrDNHVdp2/XkkXTwtxS4y0oGM6BotDDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB1X2e%2FbtrrDNHVdp2%2FXkkXTwtxS4y0oGM6BotDDk%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;365&quot; height=&quot;307&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장완료 후에 프로젝트 Perspective를 Project로 변환해 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 아래와 같이 Class 파일이 Generated되었는지 확인해야하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말하지만 navigation arg는 compile time에 빌드되는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uGHg3/btrrDwNouff/hIdf2XHjJqrL0ptutmWbt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uGHg3/btrrDwNouff/hIdf2XHjJqrL0ptutmWbt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uGHg3/btrrDwNouff/hIdf2XHjJqrL0ptutmWbt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuGHg3%2FbtrrDwNouff%2FhIdf2XHjJqrL0ptutmWbt1%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;443&quot; height=&quot;442&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약위와 같이 파일이 생성되지 않았다면, IDE상 'Build &amp;gt; Clean 프로젝트' 이후 'Rebuild Project'를 진행해 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 모두 3개가 만들어졌는데, Arguments를 보낼 TestFragment의 방향 즉 Directions, 그리고 수신하는 방향의 secondFragmentDirections, 마지막으로 SecondFragment의 Arguments를 정의한 파일 하나이다.&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;String Passing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 사용자의 입력을 받기 위해 fragment_test를 수정해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MeUZI/btrrET9dZu4/k2RABKBUAsVQZNWLCBjLEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MeUZI/btrrET9dZu4/k2RABKBUAsVQZNWLCBjLEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MeUZI/btrrET9dZu4/k2RABKBUAsVQZNWLCBjLEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMeUZI%2FbtrrET9dZu4%2Fk2RABKBUAsVQZNWLCBjLEK%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;385&quot; height=&quot;668&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text Edit하나를 추가해 준다. xml은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643119889231&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.TestFragment&quot;&amp;gt;


    &amp;lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;안녕하세요 Fragment 설정하는 방법이에요&quot;
        android:textSize=&quot;20sp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:layout_constraintVertical_bias=&quot;0.13999999&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/textView&quot; /&amp;gt;

    &amp;lt;EditText
        android:id=&quot;@+id/editTextTextPersonName&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:ems=&quot;10&quot;
        android:hint=&quot;input value&quot;
        android:inputType=&quot;textPersonName&quot;
        app:layout_constraintBottom_toTopOf=&quot;@+id/button&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/textView&quot;
        tools:layout_editor_absoluteX=&quot;96dp&quot; /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;editText의 id는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;editTextTextPersonName&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 TextFragment.kt를 수정할 차례이다.&lt;/p&gt;
&lt;pre id=&quot;code_1643119928971&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.navigation.findNavController


/**
 * A simple [Fragment] subclass.
 * Use the [TestFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class TestFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_test, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener {
            val str = view.findViewById&amp;lt;EditText&amp;gt;(R.id.editTextTextPersonName).text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            view.findNavController().navigate(action)
        }
        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정한 곳은 모두 3개의 라인인데&lt;/p&gt;
&lt;pre id=&quot;code_1643119998628&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            val str = view.findViewById&amp;lt;EditText&amp;gt;(R.id.editTextTextPersonName).text.toString() ?: &quot;hello&quot;
            val action = TestFragmentDirections.actionTestFragmentToSecondFragment(str)
            view.findNavController().navigate(action)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;editTextTextPersonName에서 사용자 입력 string을 얻어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 값을 앞서 Compile time에 generated된 TestFragmentDirections에 action 메서드인 TestFragmentToSecondFragment를 통해서 파라메터를 넣고 action을 만들어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 action을 NavController를 통해서 실행 시켜주는 순서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 TestFragment의 Directions를 통해서 파라메터는 SecondFragment에 넘어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 받아보자. SecondFragment.kt를 열어주자.&lt;/p&gt;
&lt;pre id=&quot;code_1643120173574&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.navigation.findNavController


/**
 * A simple [Fragment] subclass.
 * Use the [SecondFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_second, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button2).setOnClickListener {
            view.findNavController().navigate(R.id.action_secondFragment_to_thirdFragment)
        }
        view.findViewById&amp;lt;TextView&amp;gt;(R.id.textView2).text = SecondFragmentArgs.fromBundle(requireArguments()).seValue

        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 달라진 코드는&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;view.findViewById&amp;lt;TextView&amp;gt;(R.id.textView2).text = SecondFragmentArgs.fromBundle(requireArguments()).seValue&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 한 라인이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SecondFragment로 넘어온 Arguments에서 번들을 얻어오고 해당 번들에 있는 id 값인 seValue를 갖어와서 textView2의 영역에 해당 값을 표시해주면 끝이난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로그램을 실행해 보면 아래와 같이 Arguments가 잘 넘어가는 것을 볼수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RJL46/btrrIeZlERr/45CdXxPtuUq24uvGSKcZa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RJL46/btrrIeZlERr/45CdXxPtuUq24uvGSKcZa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RJL46/btrrIeZlERr/45CdXxPtuUq24uvGSKcZa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRJL46%2FbtrrIeZlERr%2F45CdXxPtuUq24uvGSKcZa1%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;281&quot; height=&quot;482&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvDafB/btrrDv8Qmdr/KrbUaoSRYClqkKC9qMked0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvDafB/btrrDv8Qmdr/KrbUaoSRYClqkKC9qMked0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvDafB/btrrDv8Qmdr/KrbUaoSRYClqkKC9qMked0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvDafB%2FbtrrDv8Qmdr%2FKrbUaoSRYClqkKC9qMked0%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;261&quot; height=&quot;458&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;854&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;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643120451855&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;GitHub - theyoung/fragmentsetup&quot; data-og-description=&quot;Contribute to theyoung/fragmentsetup development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot; data-og-url=&quot;https://github.com/theyoung/fragmentsetup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpcZvT/hyNb9ibMrA/G5cwETAWuyXlzs8I4YHs4K/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/2de0436018dde923c210601237c121df6909aa4c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpcZvT/hyNb9ibMrA/G5cwETAWuyXlzs8I4YHs4K/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245');&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;GitHub - theyoung/fragmentsetup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to theyoung/fragmentsetup 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 View Binding 하는 방법을 배워 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.03 - [Android] - Android View Binding (뷰 바인딩)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Action</category>
      <category>Android</category>
      <category>arguments</category>
      <category>fragment</category>
      <category>navigation</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/246</guid>
      <comments>https://enumclass.tistory.com/246#entry246comment</comments>
      <pubDate>Tue, 25 Jan 2022 23:21:00 +0900</pubDate>
    </item>
    <item>
      <title>Android Fragment Navigation with Action</title>
      <link>https://enumclass.tistory.com/245</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/244&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment 설정하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만들어진 Fragment에 Navigation을 이용해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Activity하나에 3개의 Fragment가 이동하도록 만드는게 목적이다.&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;Navigation할 Fragment 2개 더 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서 설명한 방법에 따라서 Fragment2개를 더 만들어 보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A6Qe2/btrrAdfj4SU/pevGbvrwwqpYg5bwaSvZak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A6Qe2/btrrAdfj4SU/pevGbvrwwqpYg5bwaSvZak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A6Qe2/btrrAdfj4SU/pevGbvrwwqpYg5bwaSvZak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA6Qe2%2FbtrrAdfj4SU%2FpevGbvrwwqpYg5bwaSvZak%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;314&quot; height=&quot;385&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 2개의 Fragment를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation이 될 순서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestFragment -&amp;gt; SecondFragment -&amp;gt; ThirdFragment&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Fragments사이의 이동을 하기전에 kt파일을 onCreateView만 남겨놓고 깨끗히 지워 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643031615380&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup


/**
 * A simple [Fragment] subclass.
 * Use the [SecondFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_second, container, false)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1643031626306&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup


/**
 * A simple [Fragment] subclass.
 * Use the [ThirdFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class ThirdFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_third, container, false)
    }

}&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;Navigation 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 fragment 사이 관계를 정해주는 xml을 만들어 주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ0PLU/btrrEEW0xEG/19Br9Uow3ithtkg2cFxMq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ0PLU/btrrEEW0xEG/19Br9Uow3ithtkg2cFxMq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ0PLU/btrrEEW0xEG/19Br9Uow3ithtkg2cFxMq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ0PLU%2FbtrrEEW0xEG%2F19Br9Uow3ithtkg2cFxMq0%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;500&quot; height=&quot;219&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android Resource File을 선택해서 만들어 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA6XKS/btrrF2XDK16/KkHZPHCMq81HQjm11y63G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA6XKS/btrrF2XDK16/KkHZPHCMq81HQjm11y63G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA6XKS/btrrF2XDK16/KkHZPHCMq81HQjm11y63G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA6XKS%2FbtrrF2XDK16%2FKkHZPHCMq81HQjm11y63G0%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;821&quot; height=&quot;481&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resource Type을 Navigation으로 선택해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation을 생성하면 build.gradle에 다음과 같은 dependencies가 생긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1643032246548&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Navigation UI 모드를 들어갈 수가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1373&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkkasU/btrrumRCsdW/C3eMIINIHavkNlpyaIEoa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkkasU/btrrumRCsdW/C3eMIINIHavkNlpyaIEoa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkkasU/btrrumRCsdW/C3eMIINIHavkNlpyaIEoa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkkasU%2FbtrrumRCsdW%2FC3eMIINIHavkNlpyaIEoa1%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;1373&quot; height=&quot;628&quot; data-origin-width=&quot;1373&quot; data-origin-height=&quot;628&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;h2 data-ke-size=&quot;size26&quot;&gt;Navigation 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;New Destination을 이용해서 3개의 Fragment를 불러온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X17gU/btrrDN0ZFve/thSYbS1eXDLDWI0IkNwmF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X17gU/btrrDN0ZFve/thSYbS1eXDLDWI0IkNwmF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X17gU/btrrDN0ZFve/thSYbS1eXDLDWI0IkNwmF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX17gU%2FbtrrDN0ZFve%2FthSYbS1eXDLDWI0IkNwmF0%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;220&quot; height=&quot;60&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LrKZz/btrrEUMkjPP/StT8VHU8BYzaqY43le2vmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LrKZz/btrrEUMkjPP/StT8VHU8BYzaqY43le2vmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LrKZz/btrrEUMkjPP/StT8VHU8BYzaqY43le2vmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLrKZz%2FbtrrEUMkjPP%2FStT8VHU8BYzaqY43le2vmk%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;699&quot; height=&quot;449&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개의 fragment를 아래 이미지와 같이 화살표로 연결해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NPyNq/btrrAdzC1T4/sTskRoOLXVvmvkWrKhUwM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NPyNq/btrrAdzC1T4/sTskRoOLXVvmvkWrKhUwM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NPyNq/btrrAdzC1T4/sTskRoOLXVvmvkWrKhUwM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNPyNq%2FbtrrAdzC1T4%2FsTskRoOLXVvmvkWrKhUwM1%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;296&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 화살은 action이라고 하는 이름을 갖게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFeTHm/btrrAdTUWms/tGlqPTNCQ0REjrmrlZwwjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFeTHm/btrrAdTUWms/tGlqPTNCQ0REjrmrlZwwjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFeTHm/btrrAdTUWms/tGlqPTNCQ0REjrmrlZwwjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFeTHm%2FbtrrAdTUWms%2FtGlqPTNCQ0REjrmrlZwwjk%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;934&quot; height=&quot;187&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 action을 통해서 fragment의 이동을 정의 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 navigation_graph.xml을 MainActivity에 연결해 주자.&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;activity_main.xml에 navigation 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FragmentContainerView는 Fragment를 지정할 수 있지만, 이와 동시에 navigation을 지정할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643032617664&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:padding=&quot;24dp&quot;
    tools:context=&quot;.MainActivity&quot;&amp;gt;

    &amp;lt;androidx.fragment.app.FragmentContainerView
        android:id=&quot;@+id/fragment_Container_View&quot;
        android:name=&quot;com.example.fragmentsetup.TestFragment&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:layout_marginStart=&quot;24dp&quot;
        android:layout_marginTop=&quot;24dp&quot;
        android:layout_marginEnd=&quot;24dp&quot;
        android:layout_marginBottom=&quot;24dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        tools:layout=&quot;@layout/fragment_test&quot;&amp;gt;

    &amp;lt;/androidx.fragment.app.FragmentContainerView&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 fragment_test만 화면에 보이게 하는 xml이었다. 이곳에 navigation을 넣어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643032716404&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:padding=&quot;24dp&quot;
    tools:context=&quot;.MainActivity&quot;&amp;gt;

    &amp;lt;androidx.fragment.app.FragmentContainerView
        android:id=&quot;@+id/fragment_Container_View&quot;
        android:name=&quot;androidx.navigation.fragment.NavHostFragment&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:layout_marginStart=&quot;24dp&quot;
        android:layout_marginTop=&quot;24dp&quot;
        android:layout_marginEnd=&quot;24dp&quot;
        android:layout_marginBottom=&quot;24dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:navGraph=&quot;@navigation/navigation_graph&quot;
        app:defaultNavHost=&quot;true&quot;
        &amp;gt;

    &amp;lt;/androidx.fragment.app.FragmentContainerView&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 점은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643032732970&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android:name=&quot;androidx.navigation.fragment.NavHostFragment&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 name의 class path는 NavHostFragment로 대표된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 navGraph와 defaultNavHost를 true로 설정해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643032782011&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app:navGraph=&quot;@navigation/navigation_graph&quot;
app:defaultNavHost=&quot;true&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위까지 반영한 결과는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9a50G/btrrDdMBbAY/DdqD5rQyL7fKEVVPqmpJ7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9a50G/btrrDdMBbAY/DdqD5rQyL7fKEVVPqmpJ7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9a50G/btrrDdMBbAY/DdqD5rQyL7fKEVVPqmpJ7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9a50G%2FbtrrDdMBbAY%2FDdqD5rQyL7fKEVVPqmpJ7k%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;226&quot; height=&quot;397&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 fragment는 정상 출력이 되는데, 다음 fragment로 이동할 방법이 없다. 이를 해결하기위해서 버튼을 추가해 주자.&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;버튼 추가 및 Action 연결&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcfYjV/btrrxXqhKxj/9hVxTzLJ5fGhTd7LknGBDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcfYjV/btrrxXqhKxj/9hVxTzLJ5fGhTd7LknGBDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcfYjV/btrrxXqhKxj/9hVxTzLJ5fGhTd7LknGBDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcfYjV%2FbtrrxXqhKxj%2F9hVxTzLJ5fGhTd7LknGBDk%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;544&quot; height=&quot;446&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment_test.xml에 위와 같이 Next 버튼을 위치 시키자.&lt;/p&gt;
&lt;pre id=&quot;code_1643033023931&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.TestFragment&quot;&amp;gt;


    &amp;lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;안녕하세요 Fragment 설정하는 방법이에요&quot;
        android:textSize=&quot;20sp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:layout_constraintVertical_bias=&quot;0.13999999&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@+id/textView&quot; /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 방법으로 fragment_second와 fragment_third에도 넣어주자.&lt;/p&gt;
&lt;pre id=&quot;code_1643033230655&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.SecondFragment&quot;&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;TextView
        android:id=&quot;@+id/textView2&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:text=&quot;@string/hello_blank_fragment&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button2&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Next&quot;
        app:layout_constraintBottom_toBottomOf=&quot;@+id/textView2&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView2&quot;
        app:layout_constraintTop_toTopOf=&quot;@+id/textView2&quot; /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1643033252192&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.ThirdFragment&quot;&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;TextView
        android:id=&quot;@+id/textView3&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:text=&quot;@string/hello_blank_fragment&quot; /&amp;gt;

    &amp;lt;Button
        android:id=&quot;@+id/button3&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;First&quot;
        app:layout_constraintBottom_toBottomOf=&quot;@+id/textView3&quot;
        app:layout_constraintEnd_toEndOf=&quot;@+id/textView3&quot;
        app:layout_constraintStart_toStartOf=&quot;@+id/textView3&quot;
        app:layout_constraintTop_toTopOf=&quot;@+id/textView3&quot; /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 layout에 button을 다 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;button을 누르면 fragment_second로 이동을하고 button2를 누르면 fragment_third로 이동하고 button3을 누르면 move back을 하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestFragment.kt로 이동을 해서 다음과 같이 수정해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643033534036&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.findNavController


/**
 * A simple [Fragment] subclass.
 * Use the [TestFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class TestFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_test, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener {
                view.findNavController().navigate(R.id.action_testFragment_to_secondFragment)
        }
        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643033556859&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val view = inflater.inflate(R.layout.fragment_test, container, false)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment는 activity와는 다르다 그래서 내장함수인 findViewById등의 기능을 직접적으로 사용할 수 없다. 이를 해결하기 위해서 해당 fragment를 사용하는 곳의 root view를 얻어와야 하는데, 위의 방법으로 view를 얻어올 수있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643033625690&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;view.findViewById&amp;lt;Button&amp;gt;(R.id.button).setOnClickListener {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얻어온 rootview에서 button id로 Button View를 획득하고 해당 버튼이 클릭되면 불리워질 listener를 등록해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643033687935&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;view.findNavController().navigate(R.id.action_testFragment_to_secondFragment)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation은 스스로 욺직일 수 없다. 해당 욺직임은 Navigation Controller를 통해서 가능한데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 Nav Controller를 root view에서 얻어온 후 action Id를 바탕으로 navigation하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드로 SecondFragment도 만들어 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1643033961346&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.findNavController


/**
 * A simple [Fragment] subclass.
 * Use the [SecondFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_second, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button2).setOnClickListener {
            view.findNavController().navigate(R.id.action_secondFragment_to_thirdFragment)
        }
        return view
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 잘 이동하는 것을 확인 할 수있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O0ZY8/btrrDnBunFm/SYOVrelqc9eKSKOKKi9FUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O0ZY8/btrrDnBunFm/SYOVrelqc9eKSKOKKi9FUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O0ZY8/btrrDnBunFm/SYOVrelqc9eKSKOKKi9FUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO0ZY8%2FbtrrDnBunFm%2FSYOVrelqc9eKSKOKKi9FUK%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;260&quot; height=&quot;456&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;1279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebVnXI/btrrDPEwVG4/Ou5zmiT5R4jjSbccFn2Gm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebVnXI/btrrDPEwVG4/Ou5zmiT5R4jjSbccFn2Gm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebVnXI/btrrDPEwVG4/Ou5zmiT5R4jjSbccFn2Gm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebVnXI%2FbtrrDPEwVG4%2FOu5zmiT5R4jjSbccFn2Gm1%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;268&quot; height=&quot;461&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;1279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;1282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSeYaz/btrrwgcydlN/iTmlxkhOjiC0nMcJeTYMdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSeYaz/btrrwgcydlN/iTmlxkhOjiC0nMcJeTYMdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSeYaz/btrrwgcydlN/iTmlxkhOjiC0nMcJeTYMdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSeYaz%2FbtrrwgcydlN%2FiTmlxkhOjiC0nMcJeTYMdk%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;270&quot; height=&quot;469&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;1282&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;h2 data-ke-size=&quot;size26&quot;&gt;뒤로가기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위의 ThirdFragment의 버튼은 뒤로 갈 곳이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 First버튼을 누르면, 가장처음 fragment로 이동하게 하고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 ThirdFragment에 다음과 같이 코드를 수정하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1643034464770&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.findNavController


/**
 * A simple [Fragment] subclass.
 * Use the [ThirdFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class ThirdFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_third, container, false)
        view.findViewById&amp;lt;Button&amp;gt;(R.id.button3).setOnClickListener {
            view.findNavController().popBackStack()
        }
        return view

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 파일과 다른점이 있다면,&lt;/p&gt;
&lt;pre id=&quot;code_1643034481581&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;view.findNavController().popBackStack()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Activity와 동일하게 Fragment도 stack에 history를 쌓는 방식으로 화면을 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 뒤로가기 처리를 할 경우 ThirdFragment의 직전에 있던 SecondFragment가 나타나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 해결하기위해서 navigation_graph.xml을 수정하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643034568070&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;navigation xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:id=&quot;@+id/navigation_graph&quot;
    app:startDestination=&quot;@id/testFragment&quot;&amp;gt;

    &amp;lt;fragment
        android:id=&quot;@+id/testFragment&quot;
        android:name=&quot;com.example.fragmentsetup.TestFragment&quot;
        android:label=&quot;fragment_test&quot;
        tools:layout=&quot;@layout/fragment_test&quot; &amp;gt;
        &amp;lt;action
            android:id=&quot;@+id/action_testFragment_to_secondFragment&quot;
            app:destination=&quot;@id/secondFragment&quot;

            /&amp;gt;
    &amp;lt;/fragment&amp;gt;
    &amp;lt;fragment
        android:id=&quot;@+id/secondFragment&quot;
        android:name=&quot;com.example.fragmentsetup.SecondFragment&quot;
        android:label=&quot;fragment_second&quot;
        tools:layout=&quot;@layout/fragment_second&quot;
        &amp;gt;
        &amp;lt;action
            app:popUpTo=&quot;@id/testFragment&quot;
            android:id=&quot;@+id/action_secondFragment_to_thirdFragment&quot;
            app:destination=&quot;@id/thirdFragment&quot; /&amp;gt;
    &amp;lt;/fragment&amp;gt;
    &amp;lt;fragment
        android:id=&quot;@+id/thirdFragment&quot;
        android:name=&quot;com.example.fragmentsetup.ThirdFragment&quot;
        android:label=&quot;fragment_third&quot;
        tools:layout=&quot;@layout/fragment_third&quot; /&amp;gt;
&amp;lt;/navigation&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달라진 코드는&lt;/p&gt;
&lt;pre id=&quot;code_1643034585339&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;action
            app:popUpTo=&quot;@id/testFragment&quot;
            android:id=&quot;@+id/action_secondFragment_to_thirdFragment&quot;
            app:destination=&quot;@id/thirdFragment&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;action_secondFragment_to_thirdFragment&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 실행 되면&lt;/p&gt;
&lt;pre id=&quot;code_1643034620122&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app:popUpTo=&quot;@id/testFragment&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;testFragment가 나타낼때까지 stack을 삭제하라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 수정하고 나면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;1256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brssQP/btrrAdGrMyL/ku2b4NXEICM1WE6MUUG1n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brssQP/btrrAdGrMyL/ku2b4NXEICM1WE6MUUG1n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brssQP/btrrAdGrMyL/ku2b4NXEICM1WE6MUUG1n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrssQP%2FbtrrAdGrMyL%2Fku2b4NXEICM1WE6MUUG1n1%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;280&quot; height=&quot;484&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;1256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Third화면에서 First 버튼을 눌렀을때&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;1291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1mbu/btrrDoAqT7s/zNP5eop38ardeZTsD7PvGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1mbu/btrrDoAqT7s/zNP5eop38ardeZTsD7PvGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1mbu/btrrDoAqT7s/zNP5eop38ardeZTsD7PvGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1mbu%2FbtrrDoAqT7s%2FzNP5eop38ardeZTsD7PvGk%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;277&quot; height=&quot;471&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;1291&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/e69cba09cb7d10ca54192f62f9e28c3e228087dc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/e69cba09cb7d10ca54192f62f9e28c3e228087dc&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;이제 Action에 파라메터를 넣어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/246&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.25 - [Android] - Android Fragment with Arguments&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Action</category>
      <category>Android</category>
      <category>fragment</category>
      <category>navigation</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/245</guid>
      <comments>https://enumclass.tistory.com/245#entry245comment</comments>
      <pubDate>Mon, 24 Jan 2022 23:38:28 +0900</pubDate>
    </item>
    <item>
      <title>Android Fragment 설정하기</title>
      <link>https://enumclass.tistory.com/244</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Layout에 Fragment 설정하기&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6wwih/btrrr6nvFR1/h9XgSYPqV2nXuKFqyyE15K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6wwih/btrrr6nvFR1/h9XgSYPqV2nXuKFqyyE15K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6wwih/btrrr6nvFR1/h9XgSYPqV2nXuKFqyyE15K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6wwih%2Fbtrrr6nvFR1%2Fh9XgSYPqV2nXuKFqyyE15K%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;900&quot; height=&quot;651&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Empty Activity를 기반으로 신규 프로젝트를 만들어 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBIt8q/btrrAc1Kafk/50YKafTbZZf1zFV8kb5Fuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBIt8q/btrrAc1Kafk/50YKafTbZZf1zFV8kb5Fuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBIt8q/btrrAc1Kafk/50YKafTbZZf1zFV8kb5Fuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBIt8q%2FbtrrAc1Kafk%2F50YKafTbZZf1zFV8kb5Fuk%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;897&quot; height=&quot;647&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 21기준으로 Minimum SDK를 설정해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/jetpack/compose/setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/jetpack/compose/setup&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack이 API21 부터 호환하기 때문에 min SDK는 가급적 21로 맞추어 줄 예정이다.&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;Gradle 설정 확인하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xKXo4/btrrxXKyins/OhoxLLKgBY862KTyt115Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xKXo4/btrrxXKyins/OhoxLLKgBY862KTyt115Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xKXo4/btrrxXKyins/OhoxLLKgBY862KTyt115Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxKXo4%2FbtrrxXKyins%2FOhoxLLKgBY862KTyt115Xk%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;373&quot; height=&quot;444&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만들어진 Gradle의 Dependency를 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;build.gradle&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643028022507&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId &quot;com.example.fragmentsetup&quot;
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName &quot;1.0&quot;

        testInstrumentationRunner &quot;androidx.test.runner.AndroidJUnitRunner&quot;
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;22년 1월 기준으로 compileSdk와 targetSDK가 31이라는 것을 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 SDK의 버전은 jetpack과 연관성이 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 아래 dependencies의 compileSdk와&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1643028169534&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'&lt;/code&gt;&lt;/pre&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;&lt;a href=&quot;https://developer.android.com/jetpack/androidx/versions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/jetpack/androidx/versions&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 사이트를 방문해서 최신 버전으로 관련 라이브러리 버전을 일괄 업데이트 해주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1409&quot; data-origin-height=&quot;799&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvsQZC/btrrCYooK58/Ey0QVTyRzKBZQhvidhU4Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvsQZC/btrrCYooK58/Ey0QVTyRzKBZQhvidhU4Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvsQZC/btrrCYooK58/Ey0QVTyRzKBZQhvidhU4Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvsQZC%2FbtrrCYooK58%2FEy0QVTyRzKBZQhvidhU4Y0%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;1409&quot; height=&quot;799&quot; data-origin-width=&quot;1409&quot; data-origin-height=&quot;799&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1299&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deOHWD/btrrAc8vhv1/3mkZxBwCwfPsG3VMK9tgx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deOHWD/btrrAc8vhv1/3mkZxBwCwfPsG3VMK9tgx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deOHWD/btrrAc8vhv1/3mkZxBwCwfPsG3VMK9tgx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeOHWD%2FbtrrAc8vhv1%2F3mkZxBwCwfPsG3VMK9tgx0%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;1299&quot; height=&quot;155&quot; data-origin-width=&quot;1299&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;core와 appcompat 라이브러리 최신이 각각 1.7.0과 1.4.0 인것을 확인할 수 있다.&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;fragment layout 추가하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vPGrq/btrrDerafxA/t94YJatT4RiVV6caJbo3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vPGrq/btrrDerafxA/t94YJatT4RiVV6caJbo3KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vPGrq/btrrDerafxA/t94YJatT4RiVV6caJbo3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvPGrq%2FbtrrDerafxA%2Ft94YJatT4RiVV6caJbo3KK%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;718&quot; height=&quot;593&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 위치에서 Fragment (Blank)를 선택하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YbiJD/btrrxXKyGKa/s4dMWSQbkdbsYYJmzrET81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YbiJD/btrrxXKyGKa/s4dMWSQbkdbsYYJmzrET81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YbiJD/btrrxXKyGKa/s4dMWSQbkdbsYYJmzrET81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYbiJD%2FbtrrxXKyGKa%2Fs4dMWSQbkdbsYYJmzrET81%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;894&quot; height=&quot;647&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TestFragment를 하나 만들어 주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 파일이 생성되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GmBhb/btrrDeralb2/Tw3zm8NPy3vdcYmAGh5k70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GmBhb/btrrDeralb2/Tw3zm8NPy3vdcYmAGh5k70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GmBhb/btrrDeralb2/Tw3zm8NPy3vdcYmAGh5k70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGmBhb%2FbtrrDeralb2%2FTw3zm8NPy3vdcYmAGh5k70%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;314&quot; height=&quot;234&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 TestFragment.kt에서 불필요한 내용을 지워주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onCreateView만 남겨놓고 모두 불필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1643028774443&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.fragmentsetup

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup


/**
 * A simple [Fragment] subclass.
 * Use the [TestFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class TestFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_test, container, false)
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 fragment_test.xml을 조금 수정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Framelayout을 ConstraintLayout으로 변경하고 간단한 Text를 넣었다.&lt;/p&gt;
&lt;pre id=&quot;code_1643028966980&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.TestFragment&quot;&amp;gt;


    &amp;lt;TextView
        android:id=&quot;@+id/textView&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;안녕하세요 Fragment 설정하는 방법이에요&quot;
        android:textSize=&quot;20sp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        app:layout_constraintVertical_bias=&quot;0.13999999&quot; /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eg8ZEy/btrrArxAnPM/wJ0StpgW4MR3oc5nbXjCW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eg8ZEy/btrrArxAnPM/wJ0StpgW4MR3oc5nbXjCW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eg8ZEy/btrrArxAnPM/wJ0StpgW4MR3oc5nbXjCW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feg8ZEy%2FbtrrArxAnPM%2FwJ0StpgW4MR3oc5nbXjCW1%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;243&quot; height=&quot;430&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;430&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;h2 data-ke-size=&quot;size26&quot;&gt;Activity에 FragmentContainerView 위치 시키기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fragment는 FragmentContainerView를 통해서 layout에 위치 시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;activity_main.xml을 아래와 같이 수정해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643029588802&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:padding=&quot;24dp&quot;
    tools:context=&quot;.MainActivity&quot;&amp;gt;

    &amp;lt;androidx.fragment.app.FragmentContainerView
        android:id=&quot;@+id/fragment_Container_View&quot;
        android:name=&quot;com.example.fragmentsetup.TestFragment&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:layout_marginStart=&quot;24dp&quot;
        android:layout_marginTop=&quot;24dp&quot;
        android:layout_marginEnd=&quot;24dp&quot;
        android:layout_marginBottom=&quot;24dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;
        tools:layout=&quot;@layout/fragment_test&quot;&amp;gt;

    &amp;lt;/androidx.fragment.app.FragmentContainerView&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QBc2N/btrrDOMkBBU/uZuX0umExvM89AnDw4xU5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QBc2N/btrrDOMkBBU/uZuX0umExvM89AnDw4xU5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QBc2N/btrrDOMkBBU/uZuX0umExvM89AnDw4xU5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQBc2N%2FbtrrDOMkBBU%2FuZuX0umExvM89AnDw4xU5K%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;534&quot; height=&quot;437&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FragmentContainerView를 사용할때 신경써야 하는 부분은, name 이곳에 표시할 fragment의 class path name을 써야한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;android:name=&quot;com.example.fragmentsetup.TestFragment&quot;​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정까지만 하고 실행 시켜보면 아래와 같이 잘 작동하는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh7aqF/btrrD8RzIeR/krXIMzK1Sv2HXR3AoLKA70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh7aqF/btrrD8RzIeR/krXIMzK1Sv2HXR3AoLKA70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh7aqF/btrrD8RzIeR/krXIMzK1Sv2HXR3AoLKA70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh7aqF%2FbtrrD8RzIeR%2FkrXIMzK1Sv2HXR3AoLKA70%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;349&quot; height=&quot;621&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;898&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;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/40defa85ab7becc3e026eddc11bc093f2b12745c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/fragmentsetup/tree/40defa85ab7becc3e026eddc11bc093f2b12745c&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643120502151&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;GitHub - theyoung/fragmentsetup&quot; data-og-description=&quot;Contribute to theyoung/fragmentsetup development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/40defa85ab7becc3e026eddc11bc093f2b12745c&quot; data-og-url=&quot;https://github.com/theyoung/fragmentsetup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qrzBx/hyNdpjp4L1/njhpppkKw9hc4c9LlCUtk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/fragmentsetup/tree/40defa85ab7becc3e026eddc11bc093f2b12745c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/theyoung/fragmentsetup/tree/40defa85ab7becc3e026eddc11bc093f2b12745c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qrzBx/hyNdpjp4L1/njhpppkKw9hc4c9LlCUtk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=1020_170_1089_245');&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;GitHub - theyoung/fragmentsetup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to theyoung/fragmentsetup 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;딱 여기까지만 설정된 프로젝트&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;이제 Fragment에 Navigation을 연결해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/245&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.24 - [Android] - Android Fragment Navigation with Action&lt;/a&gt;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Android</category>
      <category>fragment</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/244</guid>
      <comments>https://enumclass.tistory.com/244#entry244comment</comments>
      <pubDate>Mon, 24 Jan 2022 22:33:55 +0900</pubDate>
    </item>
    <item>
      <title>windows aws cli update</title>
      <link>https://enumclass.tistory.com/243</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;aws cli를 version 1 쓰고 있는 사용자는 version 2로 업데이트 해줘야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1425&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAIvbK/btrqLU8jgMH/cB3oNodySWirsyjRl9r20k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAIvbK/btrqLU8jgMH/cB3oNodySWirsyjRl9r20k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAIvbK/btrqLU8jgMH/cB3oNodySWirsyjRl9r20k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAIvbK%2FbtrqLU8jgMH%2FcB3oNodySWirsyjRl9r20k%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; alt=&quot;aws cli&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;184&quot; data-origin-width=&quot;1425&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 update기능은 제공하지 않는 다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uninstall 후 version 2를 install 해줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1642148843034&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C:\&amp;gt; appwiz.cpl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 실행해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VIoKs/btrqLw0Wcwa/SXP7oFqTKLn3UxpeDL9KIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VIoKs/btrqLw0Wcwa/SXP7oFqTKLn3UxpeDL9KIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VIoKs/btrqLw0Wcwa/SXP7oFqTKLn3UxpeDL9KIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVIoKs%2FbtrqLw0Wcwa%2FSXP7oFqTKLn3UxpeDL9KIk%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; alt=&quot;aws cli&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;628&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Command Line Interface를 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://awscli.amazonaws.com/AWSCLIV2.msi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://awscli.amazonaws.com/AWSCLIV2.msi&lt;/a&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;위의 url이 뭔가 좀 그러면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 windows를 선택해서 다운로드 받으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운받은 파일을 install하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccp8FM/btrqLw0XEbb/JolwewcfxCuHVOD8G2gBZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccp8FM/btrqLw0XEbb/JolwewcfxCuHVOD8G2gBZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccp8FM/btrqLw0XEbb/JolwewcfxCuHVOD8G2gBZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccp8FM%2FbtrqLw0XEbb%2FJolwewcfxCuHVOD8G2gBZK%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; alt=&quot;aws cli&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;350&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스톨 완료 후 터미널에서 아래와 같이 버전을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQCbV5/btrqGUuZYEx/O9y3BkqkjDOG6TRuJtW1Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQCbV5/btrqGUuZYEx/O9y3BkqkjDOG6TRuJtW1Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQCbV5/btrqGUuZYEx/O9y3BkqkjDOG6TRuJtW1Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQCbV5%2FbtrqGUuZYEx%2FO9y3BkqkjDOG6TRuJtW1Tk%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; alt=&quot;aws cli&quot; loading=&quot;lazy&quot; width=&quot;593&quot; height=&quot;305&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AWS</category>
      <category>aws cli</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/243</guid>
      <comments>https://enumclass.tistory.com/243#entry243comment</comments>
      <pubDate>Fri, 14 Jan 2022 17:34:12 +0900</pubDate>
    </item>
    <item>
      <title>Intellij cygwin 연동하기</title>
      <link>https://enumclass.tistory.com/242</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Intellij 계열의 IDE를 이용할 때 cygwin을 default Teminal로 사용하고 싶을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yHb09/btrqMRCYJ6z/UM3VKpRa0VuE4aIeTXxgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yHb09/btrqMRCYJ6z/UM3VKpRa0VuE4aIeTXxgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yHb09/btrqMRCYJ6z/UM3VKpRa0VuE4aIeTXxgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyHb09%2FbtrqMRCYJ6z%2FUM3VKpRa0VuE4aIeTXxgek%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;845&quot; height=&quot;325&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDE에서 Terminal 최우측 화살표를 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMmZVj/btrqHEyC80L/68zC16PJ6t1FQQ8BAjt6ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMmZVj/btrqHEyC80L/68zC16PJ6t1FQQ8BAjt6ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMmZVj/btrqHEyC80L/68zC16PJ6t1FQQ8BAjt6ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMmZVj%2FbtrqHEyC80L%2F68zC16PJ6t1FQQ8BAjt6ik%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;779&quot; height=&quot;266&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Settings를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T1V0J/btruFfVrCx1/g2QtWtlO2iekuNpn5RDFqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T1V0J/btruFfVrCx1/g2QtWtlO2iekuNpn5RDFqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T1V0J/btruFfVrCx1/g2QtWtlO2iekuNpn5RDFqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT1V0J%2FbtruFfVrCx1%2Fg2QtWtlO2iekuNpn5RDFqk%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; alt=&quot;cygwin&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;425&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;704&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 setting화면이 나오면&lt;/p&gt;
&lt;pre id=&quot;code_1646051580584&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;c:\cygwin64\bin\sh&quot; -lic &quot;cd ${OLDPWD-.}; bash&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cygwin의 bash.exe path를 설정해 주고 Apply를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFk87c/btrqLCmnJuO/bhOZqu7g0d1ZP1j4NHCZe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFk87c/btrqLCmnJuO/bhOZqu7g0d1ZP1j4NHCZe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFk87c/btrqLCmnJuO/bhOZqu7g0d1ZP1j4NHCZe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFk87c%2FbtrqLCmnJuO%2FbhOZqu7g0d1ZP1j4NHCZe0%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;835&quot; height=&quot;256&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 tab에서 '+'를 클릭하면 cygwin teminal이 작동 되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>intellij</category>
      <category>cygwin</category>
      <category>intellij</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/242</guid>
      <comments>https://enumclass.tistory.com/242#entry242comment</comments>
      <pubDate>Fri, 14 Jan 2022 17:09:48 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes Docker alpine cpu memory process monitoring</title>
      <link>https://enumclass.tistory.com/241</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;알고자 하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;alpine os를 기반으로 하는 docker image가 kubernetes에서 실행되면서 이미지 내 cpu와 memory를 각 프로세스가 얼마나 점유하는지를 확인 하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;주요 환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubeadm 및 kubectl이 설치가 완료 되어있고 node는 한개 이상이 있다고 전제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k9s의 기초 이야기는 여기서 하지 않을 예정이다.&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;Spring Boot Starter 를 만든다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alpine os에 어떤 프로세스가 cpu와 memory를 얼마나 먹는지 알아보기 위해서, java를 사용하는 spring boot sample app을 만들어 보고자 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1641998579935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl https://start.spring.io/starter.tgz \
-d dependencies=web,actuator \
-d language=java \
-d type=gradle-project \
-d applicationName=HelloApplication \
-d groupId=example.hello \
-d name=hello \
-d baseDir=hello \
| tar -xzvf -&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기는 spring initializer로써 좋은 Spring Boot 시작점이 될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1641998655423&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hello/
hello/.gitignore
hello/settings.gradle
hello/src/
hello/src/main/
hello/src/main/java/
hello/src/main/java/example/
hello/src/main/java/example/hello/
hello/src/main/java/example/hello/demo/
hello/src/main/java/example/hello/demo/HelloApplication.java
hello/src/main/resources/
hello/src/main/resources/templates/
hello/src/main/resources/application.properties
hello/src/main/resources/static/
hello/src/test/
hello/src/test/java/
hello/src/test/java/example/
hello/src/test/java/example/hello/
hello/src/test/java/example/hello/demo/
hello/src/test/java/example/hello/demo/HelloApplicationTests.java
hello/gradlew
hello/HELP.md
hello/gradle/
hello/gradle/wrapper/
hello/gradle/wrapper/gradle-wrapper.properties
hello/gradle/wrapper/gradle-wrapper.jar
hello/build.gradle
hello/gradlew.bat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 hello 폴더에 소스가 다운로드 받아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 프로젝트를 Dockerfile로 빌드하고 이미지화 할 수 있도록 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1641998745913&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM gradle:6.9.0-jdk11-openj9 as builder

COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build

FROM openjdk:14-alpine3.10

COPY --from=builder /home/gradle/src/build/libs/demo-0.0.1-SNAPSHOT.jar demo.jar

EXPOSE 8080

CMD [&quot;java&quot;, &quot;-jar&quot;, &quot;demo.jar&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서데로 jdk11 이미지로 source를 artifact로 만들어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어진 artifact를 실행할 alpine 이미지를 이용해서 실행 가능하도록&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;artifact이름을 &quot;demo.jar&quot;로 변경해 주고 alpine 이미지의 root에 해당 jar를 copy해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 8080포트를 열어주고 java -jar demo.jar 가 실행 될 수 있도록 entrypoint를 만들어 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1641999689659&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build -t hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile이 있는 폴더에서 상기 명령어를 입력하면 hello 이미지가 docker에 등록 된다.&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;만들어진 이미지를 아래와 같이 hello.yaml을 만들어서 deploy 시키면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1641999602402&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  ports:
  - name: web
    nodePort: 32500
    port: 8080
    protocol: TCP
  selector:
    app: hello
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  labels:
    app: hello
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: hello:latest
        ports:
          - containerPort: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 파일을 이제 k9s에 아래의 명령어로 등록해 주면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1641999869152&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl apply -f hello.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 명령어가 정상적으로 이루어졌다면 다음과 같은 글이 나오게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1641999897039&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;service/hello created
deployment.apps/hello created&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1641999918011&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get deployments,pods,services&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get 명령어를 통해서 pods와 services등록이 완료 된것을 확인 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1641999957483&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello   0/1     1            0           111s

NAME                         READY   STATUS             RESTARTS   AGE
pod/hello-577c59484f-7z8tw   0/1     ImagePullBackOff   0          111s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/hello        NodePort    10.106.236.40   &amp;lt;none&amp;gt;        8080:32500/TCP   111s
service/kubernetes   ClusterIP   10.96.0.1       &amp;lt;none&amp;gt;        443/TCP          23m&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 hello가 잘 작동하는지 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번재 pod를 보면 Status가 ImagePullBackOff상태이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서 만든 hello이미지를 public docker repository 또는 local docker repository에서 갖어 오지 못하는 이유 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;minikube를 사용할 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/a/42564211&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/a/42564211&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;local Docker repository 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;helm을 install 하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://helm.sh/docs/intro/install/#from-apt-debianubuntu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://helm.sh/docs/intro/install/#from-apt-debianubuntu&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1642000689614&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo &quot;deb https://baltocdn.com/helm/stable/debian/ all main&quot; | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;install이 완료 되었다면 로컬 repository를 다음과 같이 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1642000776110&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;helm repo add twuni https://helm.twun.io&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642000789247&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;helm install registry twuni/docker-registry \
  --version 1.10.0 \
  --namespace kube-system \
  --set service.type=NodePort \
  --set service.nodePort=31500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 등록이 되었다면 다음과 같이 확인이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1642000836517&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;get service --namespace kube-system
NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
dash-kubernetes-dashboard   NodePort    10.100.109.37   &amp;lt;none&amp;gt;        80:30000/TCP             38m
kube-dns                    ClusterIP   10.96.0.10      &amp;lt;none&amp;gt;        53/UDP,53/TCP,9153/TCP   38m
registry-docker-registry    NodePort    10.107.199.93   &amp;lt;none&amp;gt;        5000:31500/TCP           28m&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 registry-docker-registry가 등록 된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1642001024271&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:31500/v2/_catalog
{&quot;repositories&quot;:[&quot;hello&quot;]}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cur을 31500포트를 이용해 실행하면 위와 같이 레파지토리가 등록 되면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 repository에 등록을 위해서는 docker를 build할때 이름을 push대상과 일치 시켜줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1642001378755&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker build -t localhost:31500/hello .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 완료 되면 아래와 같이 image가 local에 등록된다.&lt;/p&gt;
&lt;pre id=&quot;code_1642001405206&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker images
REPOSITORY                                            TAG                  IMAGE ID            CREATED             SIZE
localhost:31500/hello                                 latest               289934149f67        9 seconds ago       360MB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지를 local repository에 등록해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1642001444569&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker push localhost:31500/hello
The push refers to repository [localhost:31500/hello]
1571079e3f46: Layer already exists 
f199ad476e12: Layer already exists 
531743b7098c: Layer already exists 
latest: digest: sha256:23694be6e320daffceccb5f78e4ba4801351bdb83db9c47838a8e211c13ed26c size: 953
controlplane $&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 거의다 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 yaml을 apply 시켜보자.&lt;/p&gt;
&lt;pre id=&quot;code_1642001547189&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$kubectl apply -f hello-resolved.yaml 
service/hello created
deployment.apps/hello created&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642001564839&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get deployments,pods,services
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hello   1/1     1            1           11s

NAME                         READY   STATUS    RESTARTS   AGE
pod/hello-8445fd55cb-g8bqk   1/1     Running   0          11s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/hello        NodePort    10.111.55.187   &amp;lt;none&amp;gt;        8080:32500/TCP   11s
service/kubernetes   ClusterIP   10.96.0.1       &amp;lt;none&amp;gt;        443/TCP          50m&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Status가 Running상태가 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1642001659915&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:32500/
{&quot;timestamp&quot;:&quot;2022-01-12T15:33:38.128+00:00&quot;,&quot;status&quot;:404,&quot;error&quot;:&quot;Not Found&quot;,&quot;path&quot;:&quot;/&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl을 해보면 루트 path가 없다는 내용이 나오는데 이 자체만으로 was가 작동한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이젠 해당 인스턴스가 실행한 후로 cpu와 memory가 어떻게 사용되는지 알아보자.&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;Script 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alpine os에서 java가 실행되기전에 os의 top을 주기적으로 실행시키면서 파일에 로그를 남기도록 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이를 위해서는 간단한 script가 필요한데, 다음과 같이 작성 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abc.sh&lt;/p&gt;
&lt;pre id=&quot;code_1642001822343&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/sh
top -b &amp;gt; top.log &amp;amp;
java -jar demo.jar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 내에서 top을 주기적으로 top.log에 담고, java로 was를 실행 시킨다는 의미이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile에서 entry point는 하나임으로 해당 부분을 수정해 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1642001913410&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM gradle:6.9.0-jdk11-openj9 as builder

COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build

FROM openjdk:14-alpine3.10

# Copy JAR into container image
COPY --from=builder /home/gradle/src/build/libs/demo-0.0.1-SNAPSHOT.jar demo.jar
COPY abc.sh abc.sh
EXPOSE 8080

# Run application using executable jar application
CMD [&quot;sh&quot;,&quot;abc.sh&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 docker를 build하자.&lt;/p&gt;
&lt;pre id=&quot;code_1642002193881&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker build -t localhost:31500/hello .
Sending build context to Docker daemon  93.18kB
Step 1/9 : FROM gradle:6.9.0-jdk11-openj9 as builder
 ---&amp;gt; 8e83f98d5588
Step 2/9 : COPY --chown=gradle:gradle . /home/gradle/src
 ---&amp;gt; 9e370ca3e7e9
Step 3/9 : WORKDIR /home/gradle/src
 ---&amp;gt; Running in 5e12399a00b7
Removing intermediate container 5e12399a00b7
 ---&amp;gt; 27b9a4a4e905
Step 4/9 : RUN gradle build
 ---&amp;gt; Running in 7de5dbc5c6c8

Welcome to Gradle 6.9!

Here are the highlights of this release:
 - This is a small backport release.
 - Java 16 can be used to compile when used with Java toolchains
 - Dynamic versions can be used within plugin declarations
 - Native support for Apple Silicon processors

For more details see https://docs.gradle.org/6.9/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)
&amp;gt; Task :compileJava
&amp;gt; Task :processResources
&amp;gt; Task :classes
&amp;gt; Task :bootJarMainClassName
&amp;gt; Task :bootJar
&amp;gt; Task :jar
&amp;gt; Task :assemble
&amp;gt; Task :compileTestJava
&amp;gt; Task :processTestResources NO-SOURCE
&amp;gt; Task :testClasses
&amp;gt; Task :test
&amp;gt; Task :check
&amp;gt; Task :build

BUILD SUCCESSFUL in 35s
7 actionable tasks: 7 executed
Removing intermediate container 7de5dbc5c6c8
 ---&amp;gt; aef7d468fd2a
Step 5/9 : FROM openjdk:14-alpine3.10
 ---&amp;gt; 8273876b08aa
Step 6/9 : COPY --from=builder /home/gradle/src/build/libs/demo-0.0.1-SNAPSHOT.jar demo.jar
 ---&amp;gt; 7267df16b644
Step 7/9 : COPY abc.sh abc.sh
 ---&amp;gt; a953f0e98a6e
Step 8/9 : EXPOSE 8080
 ---&amp;gt; Running in e90c8ccf8cf0
Removing intermediate container e90c8ccf8cf0
 ---&amp;gt; 22e45dde75b9
Step 9/9 : CMD [&quot;sh&quot;,&quot;abc.sh&quot;]
 ---&amp;gt; Running in e19e6e284609
Removing intermediate container e19e6e284609
 ---&amp;gt; 364a1f655000
Successfully built 364a1f655000
Successfully tagged localhost:31500/hello:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지가 잘 빌드 되었다. 다시 레파지토리에 등록하자.&lt;/p&gt;
&lt;pre id=&quot;code_1642002308069&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker push localhost:31500/hello:latest
The push refers to repository [localhost:31500/hello]
61808d9147b4: Pushed 
123cb3333dd3: Pushed 
f199ad476e12: Layer already exists 
531743b7098c: Layer already exists 
latest: digest: sha256:91a64110c666cd336ba4fa4c38c346b4fac803696917c76c1176ce276a622628 size: 1160&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;Apply 해제 및 재 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 이미지를 다시 실행해야 한다. 이를 위해 앞선 yaml을 해제 하고 다시 등록 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1642002416382&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl delete -f hello-resolved.yaml 
service &quot;hello&quot; deleted
deployment.apps &quot;hello&quot; deleted&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642002436500&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl apply -f hello-resolved.yaml 
service/hello created
deployment.apps/hello created&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 동작 하는지 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1642002460106&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
hello-8445fd55cb-s9bps   1/1     Running   0          14s&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642002504391&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:32500/
{&quot;timestamp&quot;:&quot;2022-01-12T15:48:02.523+00:00&quot;,&quot;status&quot;:404,&quot;error&quot;:&quot;Not Found&quot;,&quot;path&quot;:&quot;/&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 마지막 단계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인스턴스가 실행되면서 어떤 프로세스가 cpu와 메모리를 얼마나 사용했는지 확인하자.&lt;/p&gt;
&lt;pre id=&quot;code_1642002582177&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl exec --stdin --tty hello-8445fd55cb-s9bps -- /bin/sh
/ # ls
abc.sh    demo.jar  etc       lib       mnt       proc      run       srv       tmp       usr
bin       dev       home      media     opt       root      sbin      sys       top.log   var&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기는 hello pod에 sh로 접근해서 top.log가 root write된것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아... log잘찍힌 것 까지 여기 붙이고 싶었는데, 테스트 서버 사용시간이 완료 되어서.. 끝나 버렸다.&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;pre id=&quot;code_1642003236685&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abc.sh    demo.jar  etc       lib       mnt       proc      run       srv       tmp       usr
bin       dev       home      media     opt       root      sbin      sys       top.log   var
/ # cat top.log
Mem: 2458124K used, 1580980K free, 1380K shrd, 115460K buff, 1866268K cached
CPU:  68% usr  15% sys   0% nic  15% idle   0% io   0% irq   0% sirq
Load average: 0.32 0.21 0.12 3/311 20
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2449m  61%   1  74% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1560   0%   0   0% top -b
Mem: 2561480K used, 1477624K free, 1400K shrd, 115480K buff, 1866392K cached
CPU:  57% usr   2% sys   0% nic  40% idle   0% io   0% irq   0% sirq
Load average: 0.54 0.25 0.14 1/316 25
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2512m  63%   1  58% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b
Mem: 2566960K used, 1472144K free, 1400K shrd, 115488K buff, 1866400K cached
CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
Load average: 0.49 0.25 0.14 1/320 29
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2518m  63%   1   4% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b
Mem: 2565296K used, 1473808K free, 1400K shrd, 115504K buff, 1866400K cached
CPU:   2% usr   0% sys   0% nic  97% idle   0% io   0% irq   0% sirq
Load average: 0.45 0.24 0.14 1/320 29
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2518m  63%   1   0% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b
Mem: 2565248K used, 1473856K free, 1400K shrd, 115512K buff, 1866400K cached
CPU:   1% usr   0% sys   0% nic  97% idle   0% io   0% irq   0% sirq
Load average: 0.42 0.24 0.14 1/320 29
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2518m  63%   1   0% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b
Mem: 2565184K used, 1473920K free, 1400K shrd, 115520K buff, 1866400K cached
CPU:   1% usr   0% sys   0% nic  97% idle   0% io   0% irq   0% sirq
Load average: 0.38 0.24 0.13 1/320 29
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2518m  63%   1   0% java -jar demo.jar
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b
Mem: 2568148K used, 1470956K free, 1404K shrd, 115524K buff, 1866548K cached
CPU:   2% usr   0% sys   0% nic  97% idle   0% io   0% irq   0% sirq
Load average: 0.35 0.23 0.13 2/323 36
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    8     1 root     S    2518m  63%   1   0% java -jar demo.jar
   30     0 root     S     1628   0%   0   0% /bin/sh
    1     0 root     S     1620   0%   1   0% sh abc.sh
    7     1 root     R     1564   0%   0   0% top -b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기와 같이 alpine os가 실행된 시점이후로 cpu의 사용량을 확인 가능해 졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 metrics로는 인스턴스내의 프로세스별 모니터링이 안되다 보니까 이상한 행위를 하긴 했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에는 도움이 되길 바란다~&lt;/p&gt;</description>
      <category>AWS</category>
      <category>alpine</category>
      <category>kubenetes</category>
      <category>monitoring</category>
      <category>Process</category>
      <category>쿠버네티스</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/241</guid>
      <comments>https://enumclass.tistory.com/241#entry241comment</comments>
      <pubDate>Thu, 13 Jan 2022 01:01:34 +0900</pubDate>
    </item>
    <item>
      <title>Longest Increasing Subsequence</title>
      <link>https://enumclass.tistory.com/240</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-increasing-subsequence/&quot;&gt;Longest Increasing Subsequence&lt;/a&gt;&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 Array에서 가장 긴 연속된 순서의 길이를 찾아라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array내의 Position을 수정할 순 없지만, 중간에 있는 값을 삭제 할 수는 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Brute Force&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot; height=&quot;61&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;244&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6xCSe/btrgMz6fFSV/hTkI5UNiUScvfquk6uB5bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6xCSe/btrgMz6fFSV/hTkI5UNiUScvfquk6uB5bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6xCSe/btrgMz6fFSV/hTkI5UNiUScvfquk6uB5bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6xCSe%2FbtrgMz6fFSV%2FhTkI5UNiUScvfquk6uB5bK%2Fimg.png&quot; width=&quot;341&quot; height=&quot;61&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;244&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;상위와 같은 배열이 주어졌을 경우 다음과 같이 Array를 찾을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PkgfK/btrgBmuARca/M68jkyqBSAOhCQAUvMiwDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PkgfK/btrgBmuARca/M68jkyqBSAOhCQAUvMiwDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PkgfK/btrgBmuARca/M68jkyqBSAOhCQAUvMiwDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPkgfK%2FbtrgBmuARca%2FM68jkyqBSAOhCQAUvMiwDK%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;9보다 작은 값이 이전에 있었는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없었다면 dp 1을 유지한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfT4JU/btrgBIKUEZU/EQ4pa4WQKPpbyAUacgmus1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfT4JU/btrgBIKUEZU/EQ4pa4WQKPpbyAUacgmus1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfT4JU/btrgBIKUEZU/EQ4pa4WQKPpbyAUacgmus1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfT4JU%2FbtrgBIKUEZU%2FEQ4pa4WQKPpbyAUacgmus1%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;2보다 더 작은 값이 있는가? 없음으로 유지한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C46A4/btrgDT5PZzw/52VvcaEoyORN7Q5ukLICVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C46A4/btrgDT5PZzw/52VvcaEoyORN7Q5ukLICVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C46A4/btrgDT5PZzw/52VvcaEoyORN7Q5ukLICVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC46A4%2FbtrgDT5PZzw%2F52VvcaEoyORN7Q5ukLICVk%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;5보다 작은 값이 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2가 더 작았다. 그럼 2의 dp + 1을 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGINDg/btrgJXTUDcj/rlNeX2hEofXHrj3KAkn5Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGINDg/btrgJXTUDcj/rlNeX2hEofXHrj3KAkn5Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGINDg/btrgJXTUDcj/rlNeX2hEofXHrj3KAkn5Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGINDg%2FbtrgJXTUDcj%2FrlNeX2hEofXHrj3KAkn5Sk%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdwPV8/btrgDdXvQqP/pLlT5flltJgAY5utrB2Ow1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdwPV8/btrgDdXvQqP/pLlT5flltJgAY5utrB2Ow1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdwPV8/btrgDdXvQqP/pLlT5flltJgAY5utrB2Ow1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdwPV8%2FbtrgDdXvQqP%2FpLlT5flltJgAY5utrB2Ow1%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;3도 같은 방법으로 dp의 값을 올려준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx762Y/btrgMtkHnjl/6mK2fLcXCphaFzcMQJOlQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx762Y/btrgMtkHnjl/6mK2fLcXCphaFzcMQJOlQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx762Y/btrgMtkHnjl/6mK2fLcXCphaFzcMQJOlQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx762Y%2FbtrgMtkHnjl%2F6mK2fLcXCphaFzcMQJOlQ0%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;7의 경우 2,5,3 중 가장 높은 dp의 값에 + 1을 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIF8Md/btrgIObyXDD/MoEfPYrTn1DXvHRq4YtKC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIF8Md/btrgIObyXDD/MoEfPYrTn1DXvHRq4YtKC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIF8Md/btrgIObyXDD/MoEfPYrTn1DXvHRq4YtKC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIF8Md%2FbtrgIObyXDD%2FMoEfPYrTn1DXvHRq4YtKC0%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;101은 4가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cF7Oqx/btrgBDWVHUN/ZmSe01kxxT9jkuUzfeB1wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cF7Oqx/btrgBDWVHUN/ZmSe01kxxT9jkuUzfeB1wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cF7Oqx/btrgBDWVHUN/ZmSe01kxxT9jkuUzfeB1wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcF7Oqx%2FbtrgBDWVHUN%2FZmSe01kxxT9jkuUzfeB1wK%2Fimg.png&quot; width=&quot;401&quot; height=&quot;168&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;672&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;18도 4가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방식은 Time Complexity가 $ O(n^2) $ 이 된다.&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;1D DP 유지하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 이해하는데 시간이 좀 결려서 글을 남기게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqvqwf/btrgCD9ZpxB/hsYUB9kxQThxoogfvVkKU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqvqwf/btrgCD9ZpxB/hsYUB9kxQThxoogfvVkKU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqvqwf/btrgCD9ZpxB/hsYUB9kxQThxoogfvVkKU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqvqwf%2FbtrgCD9ZpxB%2FhsYUB9kxQThxoogfvVkKU0%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;위에서는 sub-sequence의 count를 dp에 유지했다면, 여기에서는 가장긴 숫자의 값을 유지하는 방식으로 접근해 보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pRcgy/btrgJXTURKB/dAvDvTGDKL4HoLh3kUrAHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pRcgy/btrgJXTURKB/dAvDvTGDKL4HoLh3kUrAHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pRcgy/btrgJXTURKB/dAvDvTGDKL4HoLh3kUrAHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpRcgy%2FbtrgJXTURKB%2FdAvDvTGDKL4HoLh3kUrAHK%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;초기 값은 무조건 1임으로 0번째 값을 넣어준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ludWW/btrgFTEt4iI/6QXrd03OQkGhbL49klYfj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ludWW/btrgFTEt4iI/6QXrd03OQkGhbL49klYfj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ludWW/btrgFTEt4iI/6QXrd03OQkGhbL49klYfj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FludWW%2FbtrgFTEt4iI%2F6QXrd03OQkGhbL49klYfj0%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;9가 10보다 큰가를 보면 그렇지 않다. 그럼 기존 10을 대신해서 9를 넣어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 괜찮은 이유는 이후에 나오는 11,12 등의 큰 값은 결국 9보다도 큰 값이 되기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZsZZx/btrgBkQ62AZ/wehb8Ng190fcmfJBxtVl9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZsZZx/btrgBkQ62AZ/wehb8Ng190fcmfJBxtVl9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZsZZx/btrgBkQ62AZ/wehb8Ng190fcmfJBxtVl9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZsZZx%2FbtrgBkQ62AZ%2Fwehb8Ng190fcmfJBxtVl9K%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;2가 9보다 큰가? 그렇지 않다. 2로 대체해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be921I/btrgMAxkhXy/4Hx6xHdvdIVwMGxKz7OuK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be921I/btrgMAxkhXy/4Hx6xHdvdIVwMGxKz7OuK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be921I/btrgMAxkhXy/4Hx6xHdvdIVwMGxKz7OuK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe921I%2FbtrgMAxkhXy%2F4Hx6xHdvdIVwMGxKz7OuK1%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;5가 2보다 큰가? 그렇다 sub-sequence에 append해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXrYip/btrgC6K4lwN/AaLxjkhlRSKI95CE7nnwXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXrYip/btrgC6K4lwN/AaLxjkhlRSKI95CE7nnwXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXrYip/btrgC6K4lwN/AaLxjkhlRSKI95CE7nnwXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXrYip%2FbtrgC6K4lwN%2FAaLxjkhlRSKI95CE7nnwXK%2Fimg.png&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&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;3은 5보다 큰가? 그렇지 않다. 그럼 3은 어디에 위치 해야하는가? 기존에 있던 2의 뒤에 위치하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3이 5를 대체하게 되는데, 이것이 괜찮은 이유는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음값이 5보다 작은 값이 오게 되면 길이기준으로 2,3으로 연속한 수의 길이가 가장 길기 때문에 문제없다.&lt;/li&gt;
&lt;li&gt;다음값이 5보다 큰 6이 온다고 해도 길이로는 2,5,6과 ,2,3,6 둘다 같은 3의 길이기 때문에 문제 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 3이 5를 대체하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beQOWW/btrgDVo2C4y/CMLP1fkWJay0xKK0vG8W71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beQOWW/btrgDVo2C4y/CMLP1fkWJay0xKK0vG8W71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beQOWW/btrgDVo2C4y/CMLP1fkWJay0xKK0vG8W71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeQOWW%2FbtrgDVo2C4y%2FCMLP1fkWJay0xKK0vG8W71%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ2ZuJ/btrgMtkHFXP/5YGvQiIRn46nrPw7xMQ5S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ2ZuJ/btrgMtkHFXP/5YGvQiIRn46nrPw7xMQ5S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ2ZuJ/btrgMtkHFXP/5YGvQiIRn46nrPw7xMQ5S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ2ZuJ%2FbtrgMtkHFXP%2F5YGvQiIRn46nrPw7xMQ5S0%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;7은 3보다 크다. append한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xmgsb/btrgFTYOsW9/kZIqlnjvX2hKEANRD88vVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xmgsb/btrgFTYOsW9/kZIqlnjvX2hKEANRD88vVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xmgsb/btrgFTYOsW9/kZIqlnjvX2hKEANRD88vVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxmgsb%2FbtrgFTYOsW9%2FkZIqlnjvX2hKEANRD88vVK%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;101은 7보다 크다. append 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAnkGc/btrgDUp7VCG/KRokWuxR9YIKhQFFvu9acK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAnkGc/btrgDUp7VCG/KRokWuxR9YIKhQFFvu9acK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAnkGc/btrgDUp7VCG/KRokWuxR9YIKhQFFvu9acK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAnkGc%2FbtrgDUp7VCG%2FKRokWuxR9YIKhQFFvu9acK%2Fimg.png&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&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;18은 101보다 작다. 그럼으로 101이 후에 append 될수 없다. 대신 18이 101을 대체 하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJgo8I/btrgB6dJxQ9/grJHtE7PECQPStAxRKQ3nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJgo8I/btrgB6dJxQ9/grJHtE7PECQPStAxRKQ3nK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJgo8I/btrgB6dJxQ9/grJHtE7PECQPStAxRKQ3nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJgo8I%2FbtrgB6dJxQ9%2FgrJHtE7PECQPStAxRKQ3nK%2Fimg.png&quot; width=&quot;427&quot; height=&quot;141&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;564&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;다시 한번 이것이 괜찮은 이유는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;101보다 큰값이 18뒤에 온다고 해도, 결국 18보다 큰 값임으로 length의 길이는 변함이 없다.&lt;/li&gt;
&lt;li&gt;18보다 크고 101보다 작은 값이 오게 된다면, 101이 최종일때 보다 18이 최종일때 length가 더 길게 된다&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방식도 time complexity가 $ O(N^2) $ 이된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Binary Search 사용하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/poiQ5/btrgB38TXrm/fb1kPHnSzdmzzy7MnQMaA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/poiQ5/btrgB38TXrm/fb1kPHnSzdmzzy7MnQMaA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/poiQ5/btrgB38TXrm/fb1kPHnSzdmzzy7MnQMaA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpoiQ5%2FbtrgB38TXrm%2Ffb1kPHnSzdmzzy7MnQMaA1%2Fimg.png&quot; width=&quot;427&quot; height=&quot;181&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;724&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;다시 한번 위의 경우를 생각해 보자. 18이라는 값이 7뒤의 101의 자리를 교체 한다는 것은 어떻게 알았을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법과 같다면, &quot;2-&amp;gt;3-&amp;gt;7-&amp;gt;101&quot; 7이후가 18이 들어갈 수 있는 직전 위치라는 것을 순차적으로 검색 했기 때문에 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 착안 할 수 있는 것이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Sub Sequence의 좌측은 정렬되어 있다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬되어 있다면 binary search를 이용해서 $ O(logN) $ 으로 최적화 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/178&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.06.17 - [Problem Solving] - Binary Search Lower Bound와 Upper Bound&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;binary search를 이앵효 lower bound를 찾는 것이다. 자기 자신보다 작은 가장 직전의 point를 찾는 것이 그 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 적용하면 최종적으로 $ O(N Log N) $으로 최적화 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1633255254769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def lengthOfLIS(self, nums: List[int]) -&amp;gt; int:
        sub = [nums[0]]
        
        for num in nums[1:]:
            if num &amp;gt; sub[-1]:
                sub.append(num)
            else:
                point = bisect_left(sub,num)
                sub[point] = num
        
        return len(sub)&lt;/code&gt;&lt;/pre&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;</description>
      <category>Problem Solving</category>
      <category>binary search</category>
      <category>DP</category>
      <category>Longest Increasing Subsequence</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/240</guid>
      <comments>https://enumclass.tistory.com/240#entry240comment</comments>
      <pubDate>Sun, 3 Oct 2021 19:01:48 +0900</pubDate>
    </item>
    <item>
      <title>Decode Ways</title>
      <link>https://enumclass.tistory.com/239</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/decode-ways/&quot;&gt;Decode Ways&lt;/a&gt;&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대문자 A부터 Z까지를 1부터 26까지의 숫자와 대응시켰을 때&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;h2 data-ke-size=&quot;size26&quot;&gt;접근 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Brute force 접근의 경우는 모든 경우를 분기 시키는 것이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 '112'가 주어졌다고 하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;221&quot; height=&quot;401&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;1604&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Oi3p6/btrfzjZnqy4/IOy28VPK2ChxYaik3vvN7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Oi3p6/btrfzjZnqy4/IOy28VPK2ChxYaik3vvN7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Oi3p6/btrfzjZnqy4/IOy28VPK2ChxYaik3vvN7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOi3p6%2FbtrfzjZnqy4%2FIOy28VPK2ChxYaik3vvN7K%2Fimg.png&quot; width=&quot;221&quot; height=&quot;401&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;1604&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;'aab','ai','kb' 이렇게 3개의 선택을 할 수 있다. 이 선택의 순서를 dfs방식으로 하게 되면, 최악의 경우 '11111111' &amp;lt;- 이런경우 매 순간 1이냐 11이냐 2개의 선택이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인 시각복잡도는 $ O(2^n) $ 이 되게 된다. 그러나 이것을 memorization하면 한번에 한 Path만 지나게 됨으로 $ O(N length) $라고 한다. 해답으로 나온거니까 맞을 것이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;leetcode 해답을 보니 위의 방법이 쉽게 문제 접근하기 좋은거 같다.&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;h2 data-ke-size=&quot;size26&quot;&gt;패턴 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제의 pattern을 찾기 위해서 몇가지 내용을 확인 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'&lt;span style=&quot;background-color: #f8f9fa; color: #000000;&quot;&gt;11111026'&amp;nbsp;&lt;/span&gt;이 주어졌을 때를 따라가 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;111&quot; height=&quot;81&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;324&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB95Yq/btrfG2CkkY5/4GNpaNCM1mXxtT4bzMTTz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB95Yq/btrfG2CkkY5/4GNpaNCM1mXxtT4bzMTTz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB95Yq/btrfG2CkkY5/4GNpaNCM1mXxtT4bzMTTz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB95Yq%2FbtrfG2CkkY5%2F4GNpaNCM1mXxtT4bzMTTz1%2Fimg.png&quot; width=&quot;111&quot; height=&quot;81&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;324&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;0이 아닌 한자리 수일 경우 무조건 1회가 답이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;151&quot; height=&quot;131&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;524&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgTp3s/btrfEmt4qTk/QucydKyQfLGazNlRv3BiZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgTp3s/btrfEmt4qTk/QucydKyQfLGazNlRv3BiZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgTp3s/btrfEmt4qTk/QucydKyQfLGazNlRv3BiZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgTp3s%2FbtrfEmt4qTk%2FQucydKyQfLGazNlRv3BiZk%2Fimg.png&quot; width=&quot;151&quot; height=&quot;131&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;524&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;11은 AA와 K 두가지 경우가 생기게 된다. 답은 2이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;191&quot; height=&quot;171&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NkUyz/btrfuOyRBEv/eCsP467i6IkkfsYkXW6HYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NkUyz/btrfuOyRBEv/eCsP467i6IkkfsYkXW6HYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NkUyz/btrfuOyRBEv/eCsP467i6IkkfsYkXW6HYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNkUyz%2FbtrfuOyRBEv%2FeCsP467i6IkkfsYkXW6HYk%2Fimg.png&quot; width=&quot;191&quot; height=&quot;171&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;684&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;111은 AAA, KA, AK이렇게 3가지 경우의 수가 나오게 되는데, 무언가 위의 1과 11사이에 연관성을 발견 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;111 중 &lt;span style=&quot;background-color: #ee2323;&quot;&gt;11&lt;/span&gt;1 이 확정된 경우 추가된 1 A를 붙혀서 완성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 앞의 1과 합쳐서 1만 확정시키고 2가지를 자리를 합치면 i-2 자리수의 A와 확정 K를 합쳐서 1 가지수가 된다.&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;벌서 무언가 패턴이 보이는데 조금더 확인해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;231&quot; height=&quot;261&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;1044&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0N9Gk/btrfw5mOufe/bw6LPMbq2cn4khT7aoyOF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0N9Gk/btrfw5mOufe/bw6LPMbq2cn4khT7aoyOF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0N9Gk/btrfw5mOufe/bw6LPMbq2cn4khT7aoyOF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0N9Gk%2Fbtrfw5mOufe%2Fbw6LPMbq2cn4khT7aoyOF0%2Fimg.png&quot; width=&quot;231&quot; height=&quot;261&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;1044&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;이경우도 i-1의 결과값에 A를 확정지은 갯수와 i-2에 결과값에 2자리 sum인 K를 확정 지은 수로 나타낼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;271&quot; height=&quot;381&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;1524&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UqpuK/btrftnVNSfs/VAHNO6HvXMCO7Ew6vRiUbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UqpuK/btrftnVNSfs/VAHNO6HvXMCO7Ew6vRiUbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UqpuK/btrftnVNSfs/VAHNO6HvXMCO7Ew6vRiUbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUqpuK%2FbtrftnVNSfs%2FVAHNO6HvXMCO7Ew6vRiUbK%2Fimg.png&quot; width=&quot;271&quot; height=&quot;381&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;1524&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;이경우도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해서 다음과 같은 공식 유도가 가능하다는 것을 유추 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[n] = dp[n-1] + dp[n-2]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 만약에 0인 경우는 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;371&quot; height=&quot;381&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1524&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQhLmC/btrfG2I5Gdg/rD3l2nKtiknW3hb6k7k7Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQhLmC/btrfG2I5Gdg/rD3l2nKtiknW3hb6k7k7Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQhLmC/btrfG2I5Gdg/rD3l2nKtiknW3hb6k7k7Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQhLmC%2FbtrfG2I5Gdg%2FrD3l2nKtiknW3hb6k7k7Pk%2Fimg.png&quot; width=&quot;371&quot; height=&quot;381&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1524&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;0인 경우는 dp[n-1]을 선택할 수가 없다. 그 이유는 0이 혼자서 선택될 수 없기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[n] = dp[n-2] (현재 값이 0인경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 앞자리가 0인 경우는 어떨까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;371&quot; height=&quot;265&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1060&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G2Jnu/btrfw5tA0Ry/5TpJkcPRzbQjtnoOpRz3F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G2Jnu/btrfw5tA0Ry/5TpJkcPRzbQjtnoOpRz3F0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G2Jnu/btrfw5tA0Ry/5TpJkcPRzbQjtnoOpRz3F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG2Jnu%2Fbtrfw5tA0Ry%2F5TpJkcPRzbQjtnoOpRz3F0%2Fimg.png&quot; width=&quot;371&quot; height=&quot;265&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1060&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;2의 앞이 0인경우는 2자리를 합쳐서 하나로 만들 수 없음으로 dp[n-2]를 계산 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 다른 말로 바꿔 말하자면 2자리를 합쳐서 0보다 크고 26보다 작은 값이 아니면 2자리로써 사용이 불가함으로 dp[n-2]는 사용할 수 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[n] = dp[n-1] (2자리의 값이 0 &amp;lt; 2자리 &amp;lt; 27 이 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;아닌 경우&lt;/span&gt;&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&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;341&quot; height=&quot;501&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;2004&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyJpOC/btrfzkcSLZd/OCW3OU9kn1MrYSmeKr6PIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyJpOC/btrfzkcSLZd/OCW3OU9kn1MrYSmeKr6PIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyJpOC/btrfzkcSLZd/OCW3OU9kn1MrYSmeKr6PIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyJpOC%2FbtrfzkcSLZd%2FOCW3OU9kn1MrYSmeKr6PIk%2Fimg.png&quot; width=&quot;341&quot; height=&quot;501&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;2004&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;마지막으로 6을 넣어서 dp[n-1]과 dp[n-2]를 선택 가능한 경우에는 두개의 가지수를 sum하면 답이 된다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재의 값이 한자리로써 사용할 수 있는 경우 dp[n-1]의 가짓수를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;현재의 값과 바로 앞자리의 값이 2자리로써 사용할 수 있는 경우 dp[n-2]의 가짓수를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&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;pre id=&quot;code_1632321328098&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var numDecodings = function(s) {
    let dp = new Array(s.length).fill(0);
    dp[0] = s.charAt(0) === '0'? 0 : 1;
    
    for(let i = 1; i &amp;lt; s.length; i++){
        let oneStep = dp[i-1]?? 0;
        let twoStep = dp[i-2]?? 1;
        
        if(s.charAt(i) === '0'){
            oneStep = 0;
        }
        
        const twoCharsInt = parseInt(s.charAt(i-1) + s.charAt(i));
        
        if(s.charAt(i-1) === '0' || 26 &amp;lt; twoCharsInt){
            twoStep = 0;
        }
        dp[i] = oneStep + twoStep;
    }
    // console.log(dp);
    return dp[dp.length-1];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 dp로 space complexity를 $ O(N) $을 사용했지만, 실질적으로 사용하는 메모리는 2개임으로 이것을 아래와 같이 최적화 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1632321805512&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var numDecodings = function(s) {
    let dp = new Array(s.length).fill(0);
    let oneStep = s.charAt(0) === '0'? 0 : 1;
    let twoStep = 1; 
    let result = oneStep;
    
    for(let i = 1; i &amp;lt; s.length; i++){     
        result = 0;
        
        if(! (s.charAt(i) === '0')){
            result += oneStep;
        }
        
        const twoCharsInt = parseInt(s.charAt(i-1) + s.charAt(i));
        
        if(!(s.charAt(i-1) === '0' || 26 &amp;lt; twoCharsInt)){
            result += twoStep;
        }
        twoStep = oneStep;
        oneStep = result;
    }
    return result;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;time complexity는 $ O(N) $ 이 된다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>Decode Ways</category>
      <category>DP</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/239</guid>
      <comments>https://enumclass.tistory.com/239#entry239comment</comments>
      <pubDate>Wed, 22 Sep 2021 23:44:15 +0900</pubDate>
    </item>
    <item>
      <title>Arithmetic Slices</title>
      <link>https://enumclass.tistory.com/238</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/arithmetic-slices/&quot;&gt;Arithmetic Slices&lt;/a&gt;&lt;/h4&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;number array가 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 number의 연속적인 차가 동일할 경우 부분 집합의 갯수를 return해라&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;문제 내용을 보면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f7f9fa; color: #546e7a;&quot;&gt;[1,3,5,7,9]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f7f9fa; color: #546e7a;&quot;&gt;[7,7,7,7]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f7f9fa; color: #546e7a;&quot;&gt;[3,-1,-5,-9]&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;예를 들어서 [7,7,7,7]은 [0,0,0]의 차를 동일하게 갖고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 array는 left : [7,7,7] right : [7,7,7] 전체 [7,7,7,7] 이렇게 3개의 부분 집합을 만들 수 있다.&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;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;[a,a]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 생각해 보자. 위에서 [7,7,7,7]은 [0,0,0]으로 구성되어 있음으로 이 동일 갑을 'a'라고 치환한 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[a,a] : 1회&lt;/li&gt;
&lt;li&gt;[a,a,a] : [a,a],[a,a],[a,a,a] 3회&amp;nbsp;&lt;/li&gt;
&lt;li&gt;[a,a,a,a] : [a,a][a,a][a,a] , [a,a,a], [a,a,a] , [a,a,a,a] 7회&lt;/li&gt;
&lt;li&gt;[a,a,a,a,a] : [a,a][a,a][a,a][a,a] , [a,a,a], [a,a,a],[a,a,a]&amp;nbsp; , [a,a,a,a],[a,a,a,a], [a,a,a,a,a] 10회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴을 보면 a가 5개가 있을 때, [a,a]가 4번, [a,a,a]가 3번 [a,a,a,a]가 2번 [a,a,a,a,a]가 1번 나오는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ total = \frac{n(n-1)}{2} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 형태로 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 dp의 형태로 바꾸면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[a,a] 동일 숫자가 나왔을 때 1회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[a,a,a] 동일 숫자가 나왔을 때 앞서 동일 숫자가 나온 dp의 값 + 1을 지금까지 나온 값이 더하는 형태로 표현 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daPAzV/btrfs6y3iz9/qC0fxFcDlk4hkipeEtRU11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daPAzV/btrfs6y3iz9/qC0fxFcDlk4hkipeEtRU11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daPAzV/btrfs6y3iz9/qC0fxFcDlk4hkipeEtRU11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaPAzV%2Fbtrfs6y3iz9%2FqC0fxFcDlk4hkipeEtRU11%2Fimg.png&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&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;1,2,3 사이의 차가 같고 최초 임으로 0 + 1 = 1을 dp에 넣는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccy2dZ/btrfs6Tl48L/JxQq9jqkG4WtzYk6RSH9UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccy2dZ/btrfs6Tl48L/JxQq9jqkG4WtzYk6RSH9UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccy2dZ/btrfs6Tl48L/JxQq9jqkG4WtzYk6RSH9UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccy2dZ%2Fbtrfs6Tl48L%2FJxQq9jqkG4WtzYk6RSH9UK%2Fimg.png&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&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;다음은 dp[n-1]이 1임으로 1을 더해서 2 =&amp;gt; 2+1은 3이 답이된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R69bF/btrfs51fdQY/jVLRnqAhVPXadstpY5zSok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R69bF/btrfs51fdQY/jVLRnqAhVPXadstpY5zSok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R69bF/btrfs51fdQY/jVLRnqAhVPXadstpY5zSok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR69bF%2Fbtrfs51fdQY%2FjVLRnqAhVPXadstpY5zSok%2Fimg.png&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&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;다음은 2에 1을 더해서 3을 더함으로 1+2+3 = 6이 답이된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A610h/btrfs5NHRea/mCaHdtQVS7rpB6cc3ISP9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A610h/btrfs5NHRea/mCaHdtQVS7rpB6cc3ISP9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A610h/btrfs5NHRea/mCaHdtQVS7rpB6cc3ISP9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA610h%2Fbtrfs5NHRea%2FmCaHdtQVS7rpB6cc3ISP9k%2Fimg.png&quot; width=&quot;341&quot; height=&quot;171&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;684&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;마지막으로 4를 더함으로써 최종 결과인 10이 답으로 나온다.&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;pre id=&quot;code_1632315774935&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def numberOfArithmeticSlices(self, nums: List[int]) -&amp;gt; int:
        if len(nums) &amp;lt; 2: return 0
        
        dp = [0] * len(nums)
        result = 0
        
        for idx in range(2, len(nums)):
            if nums[idx-2] - nums[idx-1] == nums[idx-1] - nums[idx]:
                dp[idx] = dp[idx-1] + 1
                result += dp[idx]
        
        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;시간복잡도는 one pass임으로 $ O(N) $ 이 된다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>Arithmetic Slices</category>
      <category>DP</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/238</guid>
      <comments>https://enumclass.tistory.com/238#entry238comment</comments>
      <pubDate>Wed, 22 Sep 2021 22:03:28 +0900</pubDate>
    </item>
    <item>
      <title>Shortest Path Visiting All Nodes</title>
      <link>https://enumclass.tistory.com/237</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/shortest-path-visiting-all-nodes/&quot;&gt;Shortest Path Visiting All Nodes&lt;/a&gt;&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 Node를 모두 Visite하는데 얼마의 step이 필요한가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Node는 중복으로 방문이 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;Youtube나 google을 조회해 봐도&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Floyd-Warshall&lt;span&gt; 알고리즘이나 &lt;/span&gt;Dijkstra 를 이용한 방법이 검색되었다.&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;그래서 Discussion을 찾아보는데 Smart BFS를 사용한 방법으로 본 문제를 풀었다는 이야기를 많이 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여기서 말하는 Smart BFS가 무었인지 알아보고 이를 통해서 문제를 풀어보고자 한다.&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;BFS 접근 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS는 queue를 이용해서 접근 범위를 넓혀가는 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FORn6/btre9GgxhWq/rR1ii7rdpVr6nkxEjUUjS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FORn6/btre9GgxhWq/rR1ii7rdpVr6nkxEjUUjS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FORn6/btre9GgxhWq/rR1ii7rdpVr6nkxEjUUjS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFORn6%2Fbtre9GgxhWq%2FrR1ii7rdpVr6nkxEjUUjS0%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;이런 문제가 주어졌고 &lt;b&gt;1번 노드를 중심&lt;/b&gt;으로 해서 BFS를 접근하면 아래의 순서로 BFS를 처리하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HZra5/btre7JxOFCG/CoK0RwzzvmKEGwsA8Y4Ac1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HZra5/btre7JxOFCG/CoK0RwzzvmKEGwsA8Y4Ac1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HZra5/btre7JxOFCG/CoK0RwzzvmKEGwsA8Y4Ac1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHZra5%2Fbtre7JxOFCG%2FCoK0RwzzvmKEGwsA8Y4Ac1%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;1번 Node를 Queue에 담는다&lt;/li&gt;
&lt;li&gt;while 문에서 Queue의 length를 확인한다&lt;/li&gt;
&lt;li&gt;Queue의 길이만큼 Node를 뺀다&lt;/li&gt;
&lt;li&gt;여기서는 1 하나임으로 1 Node만 뺀다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tgo6b/btrfbCdmnWT/g1k4aQOdNCXkmK46mdgpIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tgo6b/btrfbCdmnWT/g1k4aQOdNCXkmK46mdgpIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tgo6b/btrfbCdmnWT/g1k4aQOdNCXkmK46mdgpIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTgo6b%2FbtrfbCdmnWT%2Fg1k4aQOdNCXkmK46mdgpIk%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;1 Node에서 근접한 Node를 Queue에 담는다 -&amp;gt;&lt;b&gt;2,3,4,5&lt;/b&gt; 번 Nodes를 큐에 담는다&lt;/li&gt;
&lt;li&gt;while 문에서 Queue의 length를 확인한다&lt;/li&gt;
&lt;li&gt;Queue의 길이만큼 Node를 뺀다&lt;/li&gt;
&lt;li&gt;2번 Node -&amp;gt; child 없다&lt;/li&gt;
&lt;li&gt;3번 Node -&amp;gt; 6번 Node를 담는다&lt;/li&gt;
&lt;li&gt;4번 Node -&amp;gt; 7번 Node를 담는다&lt;/li&gt;
&lt;li&gt;5번 Node -&amp;gt; child 없다&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIkpp1%2FbtrfbP4BiDP%2Fq1vjWKHuUIwVnwk3dxo7jk%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;while 문에서 Queue의 length를 확인한다&lt;/li&gt;
&lt;li&gt;Queue의 길이만큼 Node를 뺀다&lt;/li&gt;
&lt;li&gt;여기서는 6번 7번 Node를 뺀다&lt;/li&gt;
&lt;li&gt;6번 Node -&amp;gt; child 없다&lt;/li&gt;
&lt;li&gt;7번 Node -&amp;gt; child 없다&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;while 문에서 Queue의 length를 확인한다&lt;/li&gt;
&lt;li&gt;Queue가 비었음으로 종료한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우는 모두 4번의 while을 통해서 모든 Node를 순회 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 알 수 있는 것은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node의 깊이는 4 depth이다&lt;/li&gt;
&lt;li&gt;Node는 7개로 이루어져 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Path를 확인할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 path가 얼마나 길지 또는 짧을지 알 수 없다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Smart BFS 접근&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1이 시작점이라고 가정하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsh3MS/btrfdWWfcux/0Hu3yquGymKLx7Pe6adcGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsh3MS/btrfdWWfcux/0Hu3yquGymKLx7Pe6adcGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsh3MS/btrfdWWfcux/0Hu3yquGymKLx7Pe6adcGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsh3MS%2FbtrfdWWfcux%2F0Hu3yquGymKLx7Pe6adcGk%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;1에서 선택할 수 있는 Node는 2,3,4,5가 된다. 이중 어디를 선택하든 각각의 Path는 독립적이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GFbyC/btrfbmPozXn/JnGn4EFTncjis71ZScLTf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GFbyC/btrfbmPozXn/JnGn4EFTncjis71ZScLTf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GFbyC/btrfbmPozXn/JnGn4EFTncjis71ZScLTf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGFbyC%2FbtrfbmPozXn%2FJnGn4EFTncjis71ZScLTf0%2Fimg.png&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&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;각각이 독립적인 Path임으로 위의 Path는 모두 각가의 상태를 저장할 수있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 visited로 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 쉬운 Mask를 사용해서 이를 처리하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/236&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.09.12 - [Problem Solving] - Masking을 활용한 DP 문제 풀기 (Partition to K Equal Sum Subsets)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mask 활용 법은 위를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 4가지 Case는 모두 한가지의 상태로 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(0번째 index를 1번 Node라고 하겠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'111111&lt;b&gt;1&lt;/b&gt;' -&amp;gt; '111111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 Node(0번 Index)를 무조건 visited한 상태가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 위의 4가지는 각각의 상태로 전이된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 좌측 부터 Node와 Status를 표시하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 '11&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;', 2 '11111&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;', 3 '1111&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;1&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;', 4 '111&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;11&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&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;이 상태에서 각각의 노드는 다시 선택을 하게 된다. (위에 그림을 잘못 그렸는데 무방향성 그래프이다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDEJjh%2FbtrfcXODk3W%2FtIpQAkph0DduK7CbbnJc2k%2Fimg.png&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&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;맨 좌측은 5번에서 1번 노드를 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 노드는 2번을 3번 노드는 1번아니면 6번을, 4번 노드는 7번 아니면 1번을 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 일반적인 BFS와 다른점이 나타나는데, 한번 visited한 Node를 다시 또 접근한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Node를 순회해야 하기때문에, 어쩔수 없이 돌아와야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때문에 순환이 발생하게 된다. -&amp;gt; DFS를 쉽게 사용할 수 없는 이유가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 어떻게 visited를 check 해야하는지 좀더 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4UkdA/btrfcvrjrus/iDxkkWEOe3T4GULaxJPne1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4UkdA/btrfcvrjrus/iDxkkWEOe3T4GULaxJPne1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4UkdA/btrfcvrjrus/iDxkkWEOe3T4GULaxJPne1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4UkdA%2Fbtrfcvrjrus%2FiDxkkWEOe3T4GULaxJPne1%2Fimg.png&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&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;맨 좌측 두개만을 대상으로 각각 [1번 노드, 5번노드, 1번노드], [1번노드, 2번노드, 1번노드]를 선택했다고 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이경우 mask는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1&lt;/span&gt;&lt;/b&gt;&amp;nbsp;'11&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;', &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1&lt;/span&gt;&lt;/b&gt; '11111&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;' 가 된다. 비록 다시 1로 돌아 왔지만 mask는 변화가 없다. 변한것은 현재 노드번호와 mask의 관계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 1번 노드를 선택했을 때, '11&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;' / '11111&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;' 이렇게 서로 다른 상태를 갖는 상태가 된다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T5tRy/btrfdYmfwvq/gHxRjgrvAI1tDVfOoJsXY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T5tRy/btrfdYmfwvq/gHxRjgrvAI1tDVfOoJsXY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T5tRy/btrfdYmfwvq/gHxRjgrvAI1tDVfOoJsXY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT5tRy%2FbtrfdYmfwvq%2FgHxRjgrvAI1tDVfOoJsXY0%2Fimg.png&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&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;그럼 이제부터는&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; Node와 Mask를 같이 쌍으로 visit를&lt;/span&gt;&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;1 '111111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 '11&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 '11&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&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;오른쪽은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 '111111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2&lt;span&gt;&amp;nbsp;&lt;/span&gt;'11111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 '11111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 visited로 관리하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 1번 node라고 해도 완전히 다른 상태가 된다는 것을 알 수 있다.&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;왼쪽 1번 노드 상태에서 다시 5번 노드를 접근가능할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다 왜냐하면 이미 &quot;5&amp;nbsp;'11&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&quot; 5번 노드에서 1번과 5번 노드 mask 가 visited되었음을 나타내고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 왼쪽은 2번, 3번, 4번의 선택사항이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 의문이 있을 수 있다. 오른쪽에서 이미 3번을 visited했는데?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞다 하지만 mask가 서로 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽의 2번 노드 마스크 : &lt;span style=&quot;color: #ee2323;&quot;&gt;2 '1101100'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;오른쪽의 2번 노드 마스크 :&amp;nbsp; &lt;span style=&quot;color: #ee2323;&quot;&gt;2&amp;nbsp;'11111&lt;b&gt;0&lt;/b&gt;&lt;b&gt;0&lt;/b&gt;'&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이와 같은 방식을 통해서 좌측과 우측이 서로 충돌하지 않고 각가의 path를 갈 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehWNNU/btrfcwjtnri/gGfOBKDYDGuHUiIUjzCl8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehWNNU/btrfcwjtnri/gGfOBKDYDGuHUiIUjzCl8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehWNNU/btrfcwjtnri/gGfOBKDYDGuHUiIUjzCl8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehWNNU%2Fbtrfcwjtnri%2FgGfOBKDYDGuHUiIUjzCl8k%2Fimg.png&quot; width=&quot;751&quot; height=&quot;532&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;2128&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;왼쪽과 오른쪽 모두 2번노드와 5번 노드를 visit 하고 1번 노드로 와있다면?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽의 1번 노드 마스크 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1 '1101100'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;오른쪽의 1번 노드 마스크 : &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;1 '11&lt;b&gt;0&lt;/b&gt;11&lt;b&gt;0&lt;/b&gt;&lt;b&gt;0&lt;/b&gt;'&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 2노드는 같게 된다. 둘중에 하나만 남기고 계산을 이어가면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;532&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vuORt/btrfdYfuNDi/PuZ11KZ7XBpKxSYKQlw8mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vuORt/btrfdYfuNDi/PuZ11KZ7XBpKxSYKQlw8mK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vuORt/btrfdYfuNDi/PuZ11KZ7XBpKxSYKQlw8mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvuORt%2FbtrfdYfuNDi%2FPuZ11KZ7XBpKxSYKQlw8mK%2Fimg.png&quot; width=&quot;351&quot; height=&quot;532&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2128&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;여기서는 다시 3번 혹은 4번 노드를 선택하는 것으로 분기해서 2가지 Path가 만들어 질수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;801&quot; height=&quot;532&quot; data-origin-width=&quot;3204&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhRd9y/btrfbrXeWIq/C67V5vqYmgOZEpjTJJQCm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhRd9y/btrfbrXeWIq/C67V5vqYmgOZEpjTJJQCm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhRd9y/btrfbrXeWIq/C67V5vqYmgOZEpjTJJQCm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhRd9y%2FbtrfbrXeWIq%2FC67V5vqYmgOZEpjTJJQCm0%2Fimg.png&quot; width=&quot;801&quot; height=&quot;532&quot; data-origin-width=&quot;3204&quot; data-origin-height=&quot;2128&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;이런방식으로 각각의 Node가 전이되는 Count(step)을 계산하다가, 하나의 Path라도 mask가 0이 되면 계산을 종료하고 해당 count값을 return하면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 mask 0을 따지는 이유는 mask가 0이 될때 모든 node가 순환했다는 것을 말할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설명은 1번 node가 시작점이라고 정해줬는데, 실제 문제에서는 이 시작점이 정해져 있지 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Node를 시작점으로 위의 path순환을 모두 진행해야 한다. (완전검색)&lt;/li&gt;
&lt;/ul&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;&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;pre id=&quot;code_1631717033751&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var shortestPathLength = function(graph) {

    let queue = new Array();
    let dp = new Map();

    const targetMask = (1 &amp;lt;&amp;lt; graph.length) - 1;

    for (let i = 0; i &amp;lt; graph.length; i++) {
        let mask = targetMask ^ (1 &amp;lt;&amp;lt; i);
        queue.push([i, mask]);
        dp.set(`${i}_${mask}`, true);
    }

    let count = 0;

    while (0 &amp;lt; queue.length) {
        let len = queue.length;
        for (let i = 0; i &amp;lt; len; i++) {
            const [position, mask] = queue.shift();
            if(mask === 0) return count;

            for(let next of graph[position]){
                let nextMask = mask &amp;amp; (1 &amp;lt;&amp;lt; next) ? mask ^ (1 &amp;lt;&amp;lt; next) : mask;
                if(dp.has(`${next}_${nextMask}`)) continue;
                dp.set(`${next}_${nextMask}`, true);
                queue.push([next, nextMask]);
            }
        }
        count++;
    }
    return count;
};&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;full code는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 시작하고자하는 모든 node를 큐에 담는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그와 관련된 상태도 큐에 담자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp의 key는 &quot;node 번호 _ 마스크&quot; 이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 node array에 map을 이용해서 mask를 dp할 수도 있지만 설명하기 직관적이어서 이렇게 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1631717057419&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    for (let i = 0; i &amp;lt; graph.length; i++) {
        let mask = targetMask ^ (1 &amp;lt;&amp;lt; i);
        queue.push([i, mask]);
        dp.set(`${i}_${mask}`, true);
    }&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;모든 경우의 수의 seed points를 큐에 넣었다. 이제 이 큐를 빼서 path를 진행 시켜야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1631717327737&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    while (0 &amp;lt; queue.length) {
        let len = queue.length;
        for (let i = 0; i &amp;lt; len; i++) {
			......
        }
        count++;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while을 통해서 queue가 비었는지 확인하고, empty가 아닌 경우에만 while을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bfs이기때문에 들어온 queue의 값이 모두 소진될때까지 for문을 돌리자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 queue는 하나의 node의 상태전이를 뜻함으로 count를 하나씩 늘려간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count의 위치가 중요한다. for문 안에서 queue가 소진될때 node를 visit한다고 생각하기 때문에, 꼭 count는 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;이 부분에서 오해를 하면 안되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ikpp1/btrfbP4BiDP/q1vjWKHuUIwVnwk3dxo7jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIkpp1%2FbtrfbP4BiDP%2Fq1vjWKHuUIwVnwk3dxo7jk%2Fimg.png&quot; width=&quot;351&quot; height=&quot;531&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;2124&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;이와같은 bfs방식으로 depth를 알고자 하는 것이아니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDEJjh/btrfcXODk3W/tIpQAkph0DduK7CbbnJc2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDEJjh%2FbtrfcXODk3W%2FtIpQAkph0DduK7CbbnJc2k%2Fimg.png&quot; width=&quot;1561&quot; height=&quot;532&quot; data-origin-width=&quot;6244&quot; data-origin-height=&quot;2128&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;위와 같이 어떤 node에서 파생된 독립된 다음 node 상태를 한 단계씩 쫒아가는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;여기에서 count는 node하나에서 다음 node로 한칸 욺직인 것을 나타내는 것이다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631717527644&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        for (let i = 0; i &amp;lt; len; i++) {
            const [position, mask] = queue.shift();
            if(mask === 0) return count;

            for(let next of graph[position]){
                let nextMask = mask &amp;amp; (1 &amp;lt;&amp;lt; next) ? mask ^ (1 &amp;lt;&amp;lt; next) : mask;
                if(dp.has(`${next}_${nextMask}`)) continue;
                dp.set(`${next}_${nextMask}`, true);
                queue.push([next, nextMask]);
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 queue에서 node를 하나 갖어와서 다음 next로 갈수있는 node를 찾는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 node번호와 mask가 visited되었다면 다른 path가 이미 선점한 것으로, 본 노드를 통한 path진행은 의미가 없다. 큐에 push를 하지않고 끝낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 다음 node가 visited된적이 없는 mask를 갖고 있다면 큐에 push한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 mask가 가장먼저 0을 만나게 되면,&lt;/p&gt;
&lt;pre id=&quot;code_1631717771612&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            const [position, mask] = queue.shift();
            if(mask === 0) return count;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더이상 path를 진행 시키는 것이 무의미 함으로 현재에서 종료한다.&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;Time Complexity는 쉽게 떠오르지 않는데, 다른 틀리더라도 남겨보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node의 Path를 뽑을 수 있는 경우의 수는 factorial이 될것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개의 node가 있을때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 node 선택 수는 3번째 선택 노드 수는 3번, 다음은 2번, 1번 임으로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1,2,3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1,3,2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2,1,3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2,3,1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3,1,2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3,2,1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ 3! = 6 $ 가지 경우의 수가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 동일 노드를 한번더 접근이 가능함으로 (예를 들자면 1 -&amp;gt; 2 -&amp;gt; 1 이런식으로 되돌아 오는 경우가 있을 수 있음. 하지만 한번 하고 나면 다시는 못함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ O(Node! * 2) $의 time complexity가 생기는것으로 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이것을 masking 방식으로 계산하면 또 다른 방식으로 가능할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어낼 수 있는 최대 path길이를 Node * 2라고 하면, 이 길이만큼 11111 &amp;lt;- 이럼 masking이 생길 것이고 이 masking은 선택하고나 미선택 하는 방식으로 $ O(2^{Node * 2}) $ 으로 나타낼 수 있을 것 같기도 하다.&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;</description>
      <category>Problem Solving</category>
      <category>DP</category>
      <category>masking</category>
      <category>Shortest Path Visiting All Nodes</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/237</guid>
      <comments>https://enumclass.tistory.com/237#entry237comment</comments>
      <pubDate>Thu, 16 Sep 2021 00:14:22 +0900</pubDate>
    </item>
    <item>
      <title>Masking을 활용한 DP 문제 풀기 (Partition to K Equal Sum Subsets)</title>
      <link>https://enumclass.tistory.com/236</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 또는 Backtracking을 하거나 하는 방식으로 문제를 풀게 되면, 완전검색(Exhaustive Search)을 고려하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 문제를 보았을때 이것이 완전검색을 원하는 것인지 아니면 최적의 P를 찾아낼 수 있는지를 판단하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 쉬운 판단법은 전제사항을 유심히 보는 것이다. 대부분 문제를 보면 숫자의 범위가 0에서 100,000이상의 범위를 갖게 된다. 그런데 이 문제만 이상하게 24, 32이런식으로 매우 작은 수로 범위를 갖는 경우가 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴때는 완전검색을 고려해볼 필요가있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 하나 풀어보면서 Masking과 DP를 어떻게 활용하는지 같이 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/partition-to-k-equal-sum-subsets/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/partition-to-k-equal-sum-subsets/&lt;/a&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;num으로 이루어진 Array가 주어지고, k라고 하는 파티션의 갯수가 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 array가 k개의 subsets으로 이루어지고, 그 각각의 subset의 합이 모두 동일할 경우를 찾는 문제이다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;381&quot; height=&quot;61&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;244&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVPGB3/btreLLCcFIT/D6D6sa8KzUSFzh75BcDlWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVPGB3/btreLLCcFIT/D6D6sa8KzUSFzh75BcDlWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVPGB3/btreLLCcFIT/D6D6sa8KzUSFzh75BcDlWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVPGB3%2FbtreLLCcFIT%2FD6D6sa8KzUSFzh75BcDlWK%2Fimg.png&quot; width=&quot;381&quot; height=&quot;61&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;244&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;[4,3,2,3,5,2,1]의 array를 4개의 group으로 나누고 그 sum이 동일한 값이 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 4개의 그룹으로 나누어서 같은 값이 나오려면, 주어진 모든 값의 sum/4를 했을때 나오는 값이 목표값이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 $ 5 = \frac{4+3+2+3+5+2+1}{ 4 } $가 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 목표값을 찾았다. 위의 array에서 5를 구성할 수 있는 subsets가 4개가 있는지 알아보면 된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;문제 변경&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;421&quot; height=&quot;61&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;244&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHQBfz/btreLgvGCPR/GLHUnnILDpa038vlFgLwEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHQBfz/btreLgvGCPR/GLHUnnILDpa038vlFgLwEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHQBfz/btreLgvGCPR/GLHUnnILDpa038vlFgLwEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHQBfz%2FbtreLgvGCPR%2FGLHUnnILDpa038vlFgLwEk%2Fimg.png&quot; width=&quot;421&quot; height=&quot;61&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;244&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;주어진 array에서 목표값 5가 있는지 확인해보자.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈으로 보면 (4,1), (3,2), (5), (1,2,2) 등이 나타난다. 이것을 프로그램으로 찾기 위해서는&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;421&quot; height=&quot;291&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1164&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duZWtM/btreLKwu4N4/QiC6rU99LXpGsqsBNktA7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duZWtM/btreLKwu4N4/QiC6rU99LXpGsqsBNktA7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duZWtM/btreLKwu4N4/QiC6rU99LXpGsqsBNktA7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduZWtM%2FbtreLKwu4N4%2FQiC6rU99LXpGsqsBNktA7K%2Fimg.png&quot; width=&quot;421&quot; height=&quot;291&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1164&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;이런식으로 모든 경우의 수를 찾아가야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우는 4와 1이라고 하는 한가지 경우만을 찾았는데 만약 (2,2,1)을 찾아야 한다면 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;421&quot; height=&quot;361&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1444&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUfHXT/btreLgJbvup/uPKkVuKegYGIN5zKZtUWe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUfHXT/btreLgJbvup/uPKkVuKegYGIN5zKZtUWe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUfHXT/btreLgJbvup/uPKkVuKegYGIN5zKZtUWe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUfHXT%2FbtreLgJbvup%2FuPKkVuKegYGIN5zKZtUWe1%2Fimg.png&quot; width=&quot;421&quot; height=&quot;361&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1444&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;현재의 선택값 이전의 선택값 그리고 이후에 선택할 선택값이 모두 visited할 때까지&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목표값을 찾으면 true를 return&lt;/li&gt;
&lt;li&gt;목표값보다 낮은 값을 찾으면, 한번더 다음 값을 search&lt;/li&gt;
&lt;li&gt;목표값 보다 높으면 false를 return&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고하는 기본 Rule을 갖고 dfs를 진행해 나가야 한다.&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;Mask 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 visited에 대해서 언급을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backtracking의 경우에는&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;461&quot; height=&quot;401&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1604&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RQMQg/btreK7d4yT0/870PujKM1g4cOEvLKy6Ch1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RQMQg/btreK7d4yT0/870PujKM1g4cOEvLKy6Ch1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RQMQg/btreK7d4yT0/870PujKM1g4cOEvLKy6Ch1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRQMQg%2FbtreK7d4yT0%2F870PujKM1g4cOEvLKy6Ch1%2Fimg.png&quot; width=&quot;461&quot; height=&quot;401&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1604&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;어떤 상태를 try하고 실패 (또는 성공 포함) 했을 경우 해당 상태를 원복해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우를 보자면 4를 선택하고 3을 선택하면 목표값 5가 넘어가기 때문에, 3을 다시 visited하지 않았다고 상태를 원복해 줬다. 그이유는 (3,2)라고 하는 5의 목표 선택지가 있는데 visited 를 그냥 두게 되면 해당 해답지를 영원히 찾지 못하는 상태가 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Masking을 만들어주는 방법은 일반적으로 Array를 이용해서 처리해도 되지만 위의 backtracking이 복잡해지거나 DP처리 하기에는 적절하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 DP로 어떻게 만들겠는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4와 3을 (index 0,1) 선택하면 false임을 어디에 어떤형태로 저장해 두지? 만약에 최대 2depth까지 밖에 없다면 2차원 array를 이용해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp[0][1] = false&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 만들수 있지만 몇개까지 depth가 갈지 예측할 수 없는 현 상황에서는 불가하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 bit masking을 이용해서 이것을 쉽게 처리가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 모두 7자리로 이루어져 있다. visited를 0으로 기본 값을 1로 설정해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'1111111' &amp;lt;- 이것이 우리가 원하는 making이 될것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 다음과 같은 방법으로 쉽게 mask를 만들 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;&amp;lt; length - 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 위의 경우는&lt;/p&gt;
&lt;pre id=&quot;code_1631452646028&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let mask = (1 &amp;lt;&amp;lt; nums.length) - 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;span style=&quot;color: #6897bb;&quot;&gt;1 &lt;/span&gt;&amp;lt;&amp;lt; nums.&lt;span style=&quot;color: #9876aa;&quot;&gt;length&lt;/span&gt;) = 10000000&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 1을 빼게 되면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1111111&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7자리의 1이 만들어 지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Mask를 한자리씩 지워가보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 첫번째 num을 선택했다고 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index 0번인 4를 선택했음으로&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;0111111 (보기 좋게 하기 위해서 reverse를 했다. 1111110 &amp;lt;- 이게 맞다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 4는 목표값 5보다 작음으로 1을 target값으로 해서 다음 값을 찾아야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;421&quot; height=&quot;81&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;324&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LmBwP/btrePWXfH8b/QLCXqTzOuqJ5y9zXjnOlCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LmBwP/btrePWXfH8b/QLCXqTzOuqJ5y9zXjnOlCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LmBwP/btrePWXfH8b/QLCXqTzOuqJ5y9zXjnOlCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLmBwP%2FbtrePWXfH8b%2FQLCXqTzOuqJ5y9zXjnOlCk%2Fimg.png&quot; width=&quot;421&quot; height=&quot;81&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;324&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;즉, 결과 mask는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'0111110' 이고 이 결과 값은 true가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 코드로 만들면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1631453229968&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let rst = dfs(mask ^ (1 &amp;lt;&amp;lt; i), k - nums[i]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i가 현재 선택한 위치 index이다.&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;'1111111' xor '1000000' 이면 결과 값은 '0111111'이 되게 되고&lt;/li&gt;
&lt;li&gt;k(5) - nums[i](4) = 1이 됨으로, 다음으로 찾아야 할 대상은 target 1이 되게 되고,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 loop에서는 0번 index는 사용하면 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1631453347287&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(!(mask &amp;amp; (1 &amp;lt;&amp;lt; i))) continue;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 코드로 나타내면 위와 같다. i번째 mask가 false 즉 0이라면 다음 번째로 넘어가시오.&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;Mask에 DP 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드중에&lt;/p&gt;
&lt;pre id=&quot;code_1631453476495&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let rst = dfs(mask ^ (1 &amp;lt;&amp;lt; i), k - nums[i]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 보면 우리는 다음과 같이 DP를 활용할 수 있다는 것을 알 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 mask의 결과 값이 0보다 크다면 target값을 찾은 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'0111110' 은 5를 찾은 masking이고, 10진수로 76이다.&lt;/li&gt;
&lt;li&gt;즉, 76은 true가 될수있고 또한&lt;/li&gt;
&lt;li&gt;'0111111'도 77도 true가 될 수 있다. (보기 좋게 하기 위해서 reverse를 했다. 1111110 &amp;lt;- 이게 맞다 126이다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용하면 어떤 mask에 목표값이 동일하게 들어오는 경우 더이상 뒤를 확인할 필요 없이, dp에서 결과 값을 보내주면 되게 된다.&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_1631453688655&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var findSubset = function(nums, k) {
    let mask = (1 &amp;lt;&amp;lt; nums.length) - 1;
    let dp = new Map();
    return dfs(mask, k);

    function dfs(mask, k) {
        if(dp.has(mask)) return dp.get(mask);

        for (let i = 0; i &amp;lt; nums.length; i++) {
            if(!(mask &amp;amp; (1 &amp;lt;&amp;lt; i))) continue;

            if(nums[i] === k) {
                return mask ^ (1 &amp;lt;&amp;lt; i);
            } else {
                if(0 &amp;lt; k - nums[i]){
                    let rst = dfs(mask ^ (1 &amp;lt;&amp;lt; i), k - nums[i]);
                    if(0 &amp;lt;= rst) {
                        dp.set(mask, rst);
                        return rst;
                    }
                }
            }
        }
        dp.set(mask, -1);
        return -1;
    }

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 backtracking에서 사용한 visited를 다시 살려주는 행위를 할 필요도 없고, dp를 적용하는것도 매우 쉽게 가능하다. 결국은 10진수 값에 결과를 넣는 것이기 때문이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;mask와 dp를 만들어준다&lt;/li&gt;
&lt;li&gt;dfs에서 visited가 되지 않은 한 자리를 선택하고 dfs를 다시 try한다&lt;/li&gt;
&lt;li&gt;k값이 0이거나 target보다 커지면 dfs를 종료한다.&lt;/li&gt;
&lt;/ol&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;위에서 target값을 찾는 방법을 코드로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 문제는 동일한 값을 갖는 group이 k번 나오는지를 확인하는게 목표였다.&lt;/p&gt;
&lt;pre id=&quot;code_1631453918486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    for (let i = 0; i &amp;lt; k; i++) {
        mask = findSubset(nums, findNum, mask);
        // console.log(findNum, mask.toString(2));
        if(mask &amp;lt; 0) return false;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k번의 subsets을 찾기만 한다면 결과는 true가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 할 것은 findSubset은 한번을 찾는 것이기 때문에 기 활용된 mask를 visited상태로 유지해서 다음 findSubset에서 재활용 해줘야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 입력값에 mask가 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 위 프로그램에 단점이 있는데, 왼쪽에서부터 target을 만들때까지 모두 visited를 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1,1,1,1,2,2,2,2] k = 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제가 주어지게 되면, 1,1,1 이렇게 3개를 미리 사용해 버림으로써 뒤에 (2,1),(2,1),(2,1)의 기회를 빼았게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Group의 크기를 최대한 작게 선택하게 하기 위해서 역 sorting처리가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[2,2,2,2,1,1,1,1] k = 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 최종 코드로 나타내면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1631454198593&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var canPartitionKSubsets = function(nums, k) {
    let findNum = nums.reduce((cum, cur) =&amp;gt; cum + cur) / k;
    if(findNum === 0 || findNum % 1 !== 0) return false;

    let mask = (1 &amp;lt;&amp;lt; nums.length) - 1;

    nums.sort((a,b)=&amp;gt; b-a);

    for (let i = 0; i &amp;lt; k; i++) {
        mask = findSubset(nums, findNum, mask);
        if(mask &amp;lt; 0) return false;
    }

    return true;
};

var findSubset = function(nums, k, mask) {
    let dp = new Map();
    return dfs(mask, k);

    function dfs(mask, k) {
        if(dp.has(mask)) return dp.get(mask);

        for (let i = 0; i &amp;lt; nums.length; i++) {
            if(!(mask &amp;amp; (1 &amp;lt;&amp;lt; i))) continue;

            if(nums[i] === k) {
                return mask ^ (1 &amp;lt;&amp;lt; i);
            } else {
                if(0 &amp;lt; k - nums[i]){
                    let rst = dfs(mask ^ (1 &amp;lt;&amp;lt; i), k - nums[i]);
                    if(0 &amp;lt;= rst) {
                        dp.set(mask, rst);
                        return rst;
                    }
                }
            }
        }
        dp.set(mask, -1);
        return -1;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도를 findSubset을 통해서 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'1111111' N개의 bit가 있을때 우리는 매 loop마다 2가지의 선택이 가능하다. 선택 또는 선택하지 않음 그럼으로 이것을 숫자로 나타내면 2이고 매 loop마다 2의 선택이 생김으로 $ O(2^N) $이 된다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;111111' 여기서 첫번째 숫자를 선택할 수 있다. 일단 한번 선택하면 이 첫번째 숫자로 인해서 target이 만들어질 수 있는 모든 경우의 수를 search하게 된다.&amp;nbsp; (보기좋게 하기 위해서 첫번째를 맨 왼쪽이라고 했다. 프로그램 상으로는 '1111110'이 된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;'0&lt;/b&gt;&lt;/span&gt;11111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;' 그리고 마지막 1을 선택하면서 target을 찾아 냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 1과 마지막 1까지의 사이에 그중간에서 우리는 다음과 같은 선택들을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;'0&lt;/b&gt;0&lt;/span&gt;1111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;'01&lt;/b&gt;0&lt;/span&gt;111&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&gt;&lt;/span&gt;'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;'00&lt;/b&gt;00&lt;/span&gt;11&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0&lt;/b&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;즉, 모든 case를 선택해보고 또는 선택하지 않아 보면서 target이 있는지 모든 경우를 찾은 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1이 선택할 수 있는 모든 경우는 선택 또는 선택하지 않음 2가지 선택지 임으로 하나의 target을 찾기위해서 최대&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;$ O(2^N) $ 의 시간복잡도를 활용하게 된다.&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;마지막으로 Partition의 갯수 k만큼 findsubset을 하게 됨으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;$ O(k * 2^N) $&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;오늘 leet code post에 잘 정리된 내용이 있어서 이곳에 link한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/discuss/general-discussion/1125779/Dynamic-programming-on-subsets-with-examples-explained&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/discuss/general-discussion/1125779/Dynamic-programming-on-subsets-with-examples-explained&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;이하는 Masking을 사용하는 문제 리스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/partition-to-k-equal-sum-subsets/&quot;&gt;https://leetcode.com/problems/partition-to-k-equal-sum-subsets/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/matchsticks-to-square/&quot;&gt;https://leetcode.com/problems/matchsticks-to-square/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/beautiful-arrangement/&quot;&gt;https://leetcode.com/problems/beautiful-arrangement/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/shortest-path-visiting-all-nodes/&quot;&gt;https://leetcode.com/problems/shortest-path-visiting-all-nodes/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/find-the-shortest-superstring/&quot;&gt;https://leetcode.com/problems/find-the-shortest-superstring/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/number-of-ways-to-wear-different-hats-to-each-other/&quot;&gt;https://leetcode.com/problems/number-of-ways-to-wear-different-hats-to-each-other/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/parallel-courses-ii/&quot;&gt;https://leetcode.com/problems/parallel-courses-ii/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/distribute-repeating-integers/&quot;&gt;ttps://leetcode.com/problems/distribute-repeating-integers/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximize-grid-happiness/&quot;&gt;https://leetcode.com/problems/maximize-grid-happiness/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/minimum-incompatibility/&quot;&gt;https://leetcode.com/problems/minimum-incompatibility/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/k-similar-strings/&quot;&gt;https://leetcode.com/problems/k-similar-strings/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximize-score-after-n-operations/&quot;&gt;https://leetcode.com/problems/maximize-score-after-n-operations/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-students-taking-exam/&quot;&gt;https://leetcode.com/problems/maximum-students-taking-exam/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/smallest-sufficient-team/&quot;&gt;https://leetcode.com/problems/smallest-sufficient-team/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>DP</category>
      <category>Exhaustive Search</category>
      <category>masking</category>
      <category>Partition to K Equal Sum Subsets</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/236</guid>
      <comments>https://enumclass.tistory.com/236#entry236comment</comments>
      <pubDate>Sun, 12 Sep 2021 23:00:53 +0900</pubDate>
    </item>
    <item>
      <title>Vanilla javascript URL Router 만들기 (web components)</title>
      <link>https://enumclass.tistory.com/235</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Web Component를 주로 이용하게 되는 이유는 Single Page Application을 만들기 위한 용도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 Page가 변경한다고 생각하지만 Page는 변경되고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 사용자들에게 익숙한 Browser Navigation Button을 활용하기 어렵다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;172&quot; data-origin-height=&quot;55&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o9Ejp/btrd65m3UPf/49vQlEyqNsMdQjYYaT4FvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o9Ejp/btrd65m3UPf/49vQlEyqNsMdQjYYaT4FvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o9Ejp/btrd65m3UPf/49vQlEyqNsMdQjYYaT4FvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo9Ejp%2Fbtrd65m3UPf%2F49vQlEyqNsMdQjYYaT4FvK%2Fimg.png&quot; data-origin-width=&quot;172&quot; data-origin-height=&quot;55&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;이 부분을 해결하기 위해서 windows.history 객체를 통해서 browser url search bar에 url router를 만들어 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만들어볼 페이지는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;640&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3vtYa/btrd1Q5fcr8/LifHAEhBrledAsy4ep9c4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3vtYa/btrd1Q5fcr8/LifHAEhBrledAsy4ep9c4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3vtYa/btrd1Q5fcr8/LifHAEhBrledAsy4ep9c4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3vtYa%2Fbtrd1Q5fcr8%2FLifHAEhBrledAsy4ep9c4K%2Fimg.png&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;640&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;최초 접근시에는 root path를 기본으로 하고 각 box를 클릭하면,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;862&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9yOua/btrd9lwsqGq/Tuzh2oYs3oHgvaja2mOHMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9yOua/btrd9lwsqGq/Tuzh2oYs3oHgvaja2mOHMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9yOua/btrd9lwsqGq/Tuzh2oYs3oHgvaja2mOHMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9yOua%2Fbtrd9lwsqGq%2FTuzh2oYs3oHgvaja2mOHMK%2Fimg.png&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;862&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;1170&quot; data-origin-height=&quot;842&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciV7Q9/btrd1xFcpPk/MkcHlKuqf2DXurku0mNb3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciV7Q9/btrd1xFcpPk/MkcHlKuqf2DXurku0mNb3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciV7Q9/btrd1xFcpPk/MkcHlKuqf2DXurku0mNb3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciV7Q9%2Fbtrd1xFcpPk%2FMkcHlKuqf2DXurku0mNb3k%2Fimg.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;842&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;/page/ 디렉토리 밑에 각 box별 numbering 0에서 부터 3번까지 page를 통해서 page 전환 효과를 나타낼 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 back버튼을 누르면 페이지가 뒤로 가도록 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 forward 버튼을 누르면 페이지가 앞으로 갈것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 url에서 최종 화면을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://theyoung.github.io/VanillaUrlRouterSample/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://theyoung.github.io/VanillaUrlRouterSample/&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;Component 디자인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Component 디자인은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.16 - [Web] - 기존 web site를 components 로 다시 만들기 (No State management)&lt;/a&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;4개의 box가 있고 click하면 box가 커지면서 image를 표현하는 html이다.&lt;/p&gt;
&lt;pre id=&quot;code_1630844827286&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-/root
 |-index.html
 |-MainApp.js
 |-style.css
 |-/components
   |-PageZero.js
   |-PageOne.js
   |-PageTwo.js
   |-PageThree.js&lt;/code&gt;&lt;/pre&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;i&gt;&lt;b&gt;index.html&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630844866094&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;title&amp;gt;History Management&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;main-app&amp;gt;&amp;lt;/main-app&amp;gt;

    &amp;lt;script type=&quot;module&quot; src=&quot;MainApp.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main-app은 4개의 page(boxes)가 위치할 사실상 root tag가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래가 우리가 만들 main-app의 최종 모습이다.&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;i&gt;&lt;b&gt;MainApp.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630844985820&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import PageOne from &quot;./components/PageOne.js&quot;;
import PageTwo from &quot;./components/PageTwo.js&quot;;
import PageThree from &quot;./components/PageThree.js&quot;;
import PageZero from &quot;./components/PageZero.js&quot;;

export default class MainApp extends HTMLElement {
    constructor(){
        super();
        this.pages = new Array();
        this.index = -1;
        this.pathname = new URL(window.location.href).pathname;
        this.render();
        this.router();
    }

    render(){
        this.innerHTML = this.getTemplate();
    }

    getTemplate(){
        return `
            &amp;lt;main&amp;gt;
                &amp;lt;page-zero id='page0' class=&quot;page&quot;&amp;gt;&amp;lt;/page-zero&amp;gt;
                &amp;lt;page-one id='page1' class=&quot;page&quot;&amp;gt;&amp;lt;/page-one&amp;gt;
                &amp;lt;page-two id='page2' class=&quot;page&quot;&amp;gt;&amp;lt;/page-two&amp;gt;
                &amp;lt;page-three id='page3' class=&quot;page&quot;&amp;gt;&amp;lt;/page-three&amp;gt;
            &amp;lt;/main&amp;gt;
        `;
    }

    router(idx, evt){
        if(!evt){
            history.replaceState({},'Home',this.pathname+'root/');
            this.updateMain(-1);
        } else {
            history.pushState({idx:idx},'Home',this.pathname + `root/page/${idx}`);
            this.updateMain(idx);
        }
    }

    updateMain(idx){
        if(this.index === idx) return;
        this.index = idx;

        this.pages.forEach(page=&amp;gt; {
            page.classList.remove('expand');
            page.updatedClassList();
        });
        if(0 &amp;lt;= idx){
            this.pages[idx].classList.add('expand');
            this.pages[idx].updatedClassList();
        }
    }

    moveBack(nav){
        if(0 &amp;lt;= nav.state.idx){
            this.updateMain(nav.state.idx);
        } else {
            this.updateMain(-1);
        }
    }

    connectedCallback(){
        this.pages = this.querySelectorAll('.page');
        this.pages.forEach((page,idx)=&amp;gt;{
            page.addEventListener('click',this.router.bind(this,idx));
        })

        window.onpopstate = this.moveBack.bind(this);
    }

    disconnectedCallback(){

    }
};


customElements.get('main-app')?? customElements.define('main-app',MainApp);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Main App은 아래와 같이 4개의 Page를 갖는다.&lt;/p&gt;
&lt;pre id=&quot;code_1630845021639&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            &amp;lt;main&amp;gt;
                &amp;lt;page-zero id='page0' class=&quot;page&quot;&amp;gt;&amp;lt;/page-zero&amp;gt;
                &amp;lt;page-one id='page1' class=&quot;page&quot;&amp;gt;&amp;lt;/page-one&amp;gt;
                &amp;lt;page-two id='page2' class=&quot;page&quot;&amp;gt;&amp;lt;/page-two&amp;gt;
                &amp;lt;page-three id='page3' class=&quot;page&quot;&amp;gt;&amp;lt;/page-three&amp;gt;
            &amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 페이지는 미리 main에 정의 되어있는 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의해서 봐야할 것이있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;미리 Loading 해 놓을 것인가? 필요할 때 Loading할 것인가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 미리 Loading해 놓는 방식을 취하고 있다. 필요할 때 Loading하는 방식은 SSR(server side rendering)에 조금더 맞는 형태라고 생각하고, 실제로 이와 같은 화면이 필요할 경우 SSR을 통해서 처리하는 것을 고려하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;미리 4개의 Page를 Loading해 놓고 필요할 때 각 Page의 view를 보여주는 형태로 진행 하고자 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&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;i&gt;&lt;b&gt;PageOne.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630845469275&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class PageOne extends HTMLElement {
    constructor(){
        super();
        this.render();
    }

    render(){
        this.innerHTML = this.classList.contains('expand') ? this.getTemplate() : this.innerHTML = &quot;&quot;;

    }

    getTemplate(){
        return `
            &amp;lt;img src=&quot;https://cdn.pixabay.com/photo/2021/08/30/16/28/dewdrops-6586339_960_720.jpg&quot;&amp;gt;&amp;lt;/img&amp;gt;
        `;
    }
    updatedClassList(){
        console.log('class upldated');
        this.render();
    }
    connectedCallback(){

    }

    disconnectedCallback(){

    }
};

customElements.get('page-one')?? customElements.define('page-one',PageOne);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;PageTwo.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630845487887&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class PageTwo extends HTMLElement {
    constructor(){
        super();
        this.render();
    }

    render(){
        this.innerHTML = this.classList.contains('expand') ? this.getTemplate() : this.innerHTML = &quot;&quot;;

    }

    getTemplate(){
        return `
            &amp;lt;img src=&quot;https://cdn.pixabay.com/photo/2021/08/24/15/38/sand-6570980_960_720.jpg&quot;&amp;gt;&amp;lt;/img&amp;gt;
        `;
    }
    updatedClassList(){
        console.log('class upldated');
        this.render();
    }
    connectedCallback(){

    }

    disconnectedCallback(){

    }
};

customElements.get('page-two')?? customElements.define('page-two',PageTwo);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;PageThree.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630845507062&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class PageThree extends HTMLElement {
    constructor(){
        super();
        this.render();
    }

    render(){
        this.innerHTML = this.classList.contains('expand') ? this.getTemplate() : this.innerHTML = &quot;&quot;;
    }

    getTemplate(){
        return `
            &amp;lt;img src=&quot;https://cdn.pixabay.com/photo/2021/08/25/17/22/flowers-6574079_960_720.jpg&quot;&amp;gt;&amp;lt;/img&amp;gt;
        `;
    }
    updatedClassList(){
        console.log('class upldated');
        this.render();
    }
    
    connectedCallback(){

    }

    disconnectedCallback(){

    }
};

customElements.get('page-three')?? customElements.define('page-three',PageThree);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;PageZero.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630845526193&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class PageZero extends HTMLElement {
    constructor(){
        super();
        this.render();
    }

    render(){
        this.innerHTML = this.classList.contains('expand') ? this.getTemplate() : this.innerHTML = &quot;&quot;;

    }

    getTemplate(){
        return `
            &amp;lt;img src=&quot;https://cdn.pixabay.com/photo/2021/08/27/10/16/baby-6578335_960_720.jpg&quot;&amp;gt;&amp;lt;/img&amp;gt;
        `;
    }

    updatedClassList(){
        console.log('class upldated');
        this.render();
    }

    connectedCallback(){
    }

    disconnectedCallback(){

    }
};

customElements.get('page-zero')?? customElements.define('page-zero',PageZero);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 페이지의 소스는 모두 동일한 형태를 지녔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달라지는 점은 딱하나 image tag에서 보여줄 이미지의 url만 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단지 주의해서 봐야할 것은 이부분이다.&lt;/p&gt;
&lt;pre id=&quot;code_1630845708072&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    render(){
        this.innerHTML = this.classList.contains('expand') ? this.getTemplate() : this.innerHTML = &quot;&quot;;

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재의 tag가 화면에 표현되고 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;expand라고 하는 style class가 있으면 화면을 점유하고 있는 형태로 디자인이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이부분은 답이 없다. 어떻게 만들어 갈지는 어떤 형태의 디자인이 오는가에 따라서 달라진다.&lt;/p&gt;
&lt;pre id=&quot;code_1630845754996&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    updatedClassList(){
        console.log('class upldated');
        this.render();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Page자체가 화면에서 사라지지 않고 남아 있으면서 언제 Contents를 보여줄것인가? 가 주요 개발 포인트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 각 Page가 expand라고하는 class 추가를 통해서 보여지게 됨으로 classList가 변화를 할때마다 render를 실행하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 classList가 update될때 event로 capture했었는데, 최근에 deprecated되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 MutationOberver로 확인하기에도 Overhead가 너무 크다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이와같이 components디자인을 간단히 끝을 냈다. 이제 URL을 어떻게 관리할지 알아보자.&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;최초 Router 개발&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MainApp.js 가 실행되는 시점에 우리는 url의 형태를 알수 있는가?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://localhost:3040/index.html&lt;/li&gt;
&lt;li&gt;http://github.com:999/theyoung/index.html&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 index.html이 어떤 root path를 갖고 실행하게 될지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단지 우리고 서비스를 만들면서 디자인 할 수 있는 부분은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;App이 실행되면 'root/' path로 시작한다&lt;/li&gt;
&lt;li&gt;Page는 'root' path이하 'page/' + 'index'로 표시된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 설계를 반영하기 위해서는 MainApp에 최초 page로가 되었을 경우 pathname을 알아내야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630846388969&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class MainApp extends HTMLElement {
    constructor(){
...
        this.pathname = new URL(window.location.href).pathname;
...
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 pathname은 main-app tag가 page reload되기 전까지 동일한 값을 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://github.com:999/theyoung/index.html&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 url로 시작이 되었을 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'http://github.com:999/theyoung/' 여기까지가 pathname이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 최초 실행은 root로 실행 되어야 함으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://github.com:999/theyoung/root/&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 나와야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630846609014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    router(){
            history.replaceState({},'Home',this.pathname+'root/');
            this.updateMain(-1);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 constructor이후 router를 실행 시키면, 최초 실행임으로 무조건 root를 붙여서. replaceState하는 것을 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;replaceState : history의 마지막 State를 수정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 url이 최초에 왔을 때, 해당 url은 불필요 함으로 강제 rewirting한다고 생각하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 : http://github.com:999/theyoung/index.html&amp;nbsp;&lt;/li&gt;
&lt;li&gt;수정본 : http://github.com:999/theyoung/root/&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 history stack을 증가시키지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지의 화면이 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;897&quot; width=&quot;828&quot; height=&quot;549&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uHwt9/btrd9lQPlvb/OWO6kgStaqtnIYlkiDuiG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uHwt9/btrd9lQPlvb/OWO6kgStaqtnIYlkiDuiG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uHwt9/btrd9lQPlvb/OWO6kgStaqtnIYlkiDuiG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuHwt9%2Fbtrd9lQPlvb%2FOWO6kgStaqtnIYlkiDuiG0%2Fimg.png&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;897&quot; width=&quot;828&quot; height=&quot;549&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;h2 data-ke-size=&quot;size26&quot;&gt;PushState&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 각 box를 click했을 때 어떻게 url을 변화 시킬지 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 url에 + /page/ 와 + 'index'를 더한 url을 넣어 줘야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://github.com:999/theyoung/root/&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 화면에서 box 0번을 클릭했을 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;http://github.com:999/theyoung/root/page/0&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 되어야 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 각 box별로 click event를 걸어주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;MainApp.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630847131502&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    connectedCallback(){
        this.pages = this.querySelectorAll('.page');
        this.pages.forEach((page,idx)=&amp;gt;{
            page.addEventListener('click',this.router.bind(this,idx));
        })
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;page class tag를 모두 읽어와서 click event를 설정해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;click이 일어날 경우 router를 실행 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;router는 click된 index를 파마메터로 받아서 pushState를 통해서 url을 변화시켜 주면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1630847219708&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    router(idx, evt){
        if(!evt){
            history.replaceState({},'Home',this.pathname+'root/');
            this.updateMain(-1);
        } else {
            history.pushState({idx:idx},'Home',this.pathname + `root/page/${idx}`);
            this.updateMain(idx);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 evt(event)가 나온이유는 click을 통해 event로 실행되는 경우와 최초 event없이 실행 되는 경우를 구분하기 위해 넣어줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 최초실행과 달리 evt가 발생함으로&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630847284557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        } else {
            history.pushState({idx:idx},'Home',this.pathname + `root/page/${idx}`);
            this.updateMain(idx);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 처리가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pushState에 대해서 잠깐 설명을 하자면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫번째 파라메터 : 각 State를 대표하면 Status 값을 유지한다. 페이지별로 하나라고 생각하면 된다.&lt;/li&gt;
&lt;li&gt;두번재 파라메터 : 페이지 타이틀이다. 사용되는 브라우저없다. (21년기준으로)&lt;/li&gt;
&lt;li&gt;세번째 파라메터 : url을 변형시킬 url이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세번재 파라메터에서 this.pathname은 최초 실행될때 root path를 유지하기 위해서 넣어 놓았다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참고로 3번째 파라메터의 String 시작이&lt;br /&gt;'/' &amp;lt;-- 이걸로 시작하면 url host(http://localhost:3030/ &amp;lt;- 이렇게 생긴부분까지만 host임)이후를 모두 교체한다.&lt;br /&gt;'abc/'이렇게 시작되면, 'http://localhost:3030/test' 가 'http://localhost:3030/test/abc/' &lt;br /&gt;이런식으로 host + pathname + 추가 pathname을 붙힌 url이 만들어지게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pushState를 하게 되면 history.length가 늘어나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 다른점은 popState가 되면 history.length가 줄어들어야 하는데 그렇지 않다. 그래서 history.length는 프로그램 작성시 사용하지 말자. 신뢰할 수 없는 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 &lt;b&gt;첫번째 파라메터는 반듯이 작성 되어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1630847864868&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            history.pushState({idx:idx},'Home',this.pathname + `root/page/${idx}`);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서는 page에 변화에 따른 page view의 형태 변화가 index로만 변경이 됨으로 해당 url이었을때 index를 유지함으로써 아래 onpopstate 즉 navigation의 변화시 어떻게 화면을 구성할지에 대한 guide data가 된다.&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;onpopstate&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에까지는 pushState를 통해서 어떻게 stack에 url을 쌓는지 알아 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리가 목표로 하는 back 버튼을 눌리웠을때 어떻게 작동해야 하는지 알아보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1630847971313&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    connectedCallback(){
        this.pages = this.querySelectorAll('.page');
        this.pages.forEach((page,idx)=&amp;gt;{
            page.addEventListener('click',this.router.bind(this,idx));
        })

        window.onpopstate = this.moveBack.bind(this);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.onpopstate를 method bind한 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 back버튼 혹은 forward버튼이 눌리워지면 무조건 this.moveBack이라는 method 가 불리게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1630848019973&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    moveBack(nav){
        if(0 &amp;lt;= nav.state.idx){
            this.updateMain(nav.state.idx);
        } else {
            this.updateMain(-1);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;moveBack은 현재 state에 들어있는 값을 바탕으로 화면을 재 구성하는 행위를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서 pushState 첫번째 파라메터를 꼭 넣으라고 한 이유가 이것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를통해 현재 navigation이 바라는 화면의 모습을 유추할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1630848126381&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    updateMain(idx){
        if(this.index === idx) return;
        this.index = idx;

        this.pages.forEach(page=&amp;gt; {
            page.classList.remove('expand');
            page.updatedClassList();
        });
        if(0 &amp;lt;= idx){
            this.pages[idx].classList.add('expand');
            this.pages[idx].updatedClassList();
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택된 index의 page에 expand라고 하는 선택 class를 넣어주고, 해당 page에 updatedClassList() method를 call해 줌으로써 페이지의 변화를 다시 일어나게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 절대 중요한 것은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BackState함수에 pushState나 replaceState를 하는 행위를 지양해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번 url 내용이 꼬이기 시작하면 답이 없다.&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;분명 간단한 내용이지만 SPA를 개발하는 사람이라면 누구나 url 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;url을 받아내는 방법은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;server에서 request mapping을 통해서&lt;/li&gt;
&lt;li&gt;client에서 pathname 및 param 분석으로 ui를 그리는 방법을 통해서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 2가지가 있는데, server에서 request mapping을 통하는 방법의 경우는 client에서 할수 있는 일이 많지 않다.&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;https://theyoung.github.io/VanillaUrlRouterSample/root/&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 url에 사용자가 직접 넣으면 오류가 날것이다. 해당 서비스 접근을 통해서 해당 url에 화면이 잘 나오는 것을 분명히 확인했는데 어떤 차이일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것은 해당 url이 server에 request를 했느냐의 차이가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 url을 navigation bar에 직접 넣게 되면 서버에 해당 url을 요청하게 되는데, 서버에서는 해당 url에 대한 정보가 없음으로 page not found가 나게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 SPA기반 개발은 많은 request mapping을 index.html을 바라보게 하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;client side에서 해당 url을 parsing해서 필요한 화면을 보여주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 github page는 그런 기능이 없음으로 직접 입력은 오류를 발생 시킨다.&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;&lt;a href=&quot;https://github.com/theyoung/VanillaUrlRouterSample&quot;&gt;https://github.com/theyoung/VanillaUrlRouterSample&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고하세요~&lt;/p&gt;</description>
      <category>Web</category>
      <category>History</category>
      <category>onpopstate</category>
      <category>pushState</category>
      <category>Router</category>
      <category>vanilla javascript</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/235</guid>
      <comments>https://enumclass.tistory.com/235#entry235comment</comments>
      <pubDate>Sun, 5 Sep 2021 22:27:54 +0900</pubDate>
    </item>
    <item>
      <title>Vanilla javascript와 Redux (Web component)</title>
      <link>https://enumclass.tistory.com/234</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 Vanilla javascript로 web component를 통해서 어떻게 Component화 하는지와 어떻게 상태관리를 할 수 있는지 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 library를 활용해서 상태관리(state management)를 해보고자 한다.&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;a href=&quot;https://enumclass.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.16 - [Web] - 기존 web site를 components 로 다시 만들기 (No State management)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 만들어진 todo 웹사이트를 이용해서 redux를 적용하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 source는 아래&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/webcomponents/tree/webComWithStateManagement/statementforwebcomponent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/webcomponents/tree/webComWithStateManagement/statementforwebcomponent&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redux의 이해&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux는 Facebook에서 Flux라는 개념을 실체화 한것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux? 어디서 많이 들어본 이름인데, 가장 유명한것이 Web-flux 일것 같다.&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;Spring의 MVVM을 실체화시키는 Core library이자 Observer Tool 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=nYkdrAPrdcw&amp;amp;t=15s&quot;&gt;https://www.youtube.com/watch?v=nYkdrAPrdcw&amp;amp;t=15s&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 위의 youtube와 아래 내용을 읽어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/230&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.20 - [Web] - MVC, MVP, MVVM 패턴의 이해&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1t2Mq/btrdXxxR1kX/V5N4QCqeWpu90YI1wjxY10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1t2Mq/btrdXxxR1kX/V5N4QCqeWpu90YI1wjxY10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1t2Mq/btrdXxxR1kX/V5N4QCqeWpu90YI1wjxY10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1t2Mq%2FbtrdXxxR1kX%2FV5N4QCqeWpu90YI1wjxY10%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;603&quot; height=&quot;295&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 개발방식을 보면 View에서 활용하는 Model에 대한 수정이 자체적으로 이루어 진다.&lt;/p&gt;
&lt;pre id=&quot;code_1630721319722&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        this.items.push(item);
        this.renderList();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 해당 class의 new-item-field tag에 있는 값을 자신의 this.items에 push하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this.items를 model이라고 보았을때 이 model이 영향을 줄수있는 범위는 자신의 class 범위이다. 이것을 벗어나기 위해서는 custom event등을 통한 dispatch를 사용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630721431865&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    dispatchItems(){
        this.dispatchEvent(new CustomEvent('changed',{
            detail : {data : this.items.length},
            bubbles : true
        }));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 보면 특정 item array의 size가 변화하였다는 event를 외부로 공지하는 형태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 tightly하게 묶여있던 model과 business logic 그리고 view를 분리하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3604&quot; data-origin-height=&quot;1124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpw3gT/btrd5jZnDvn/Eslytg8D9mbaVHjEGY7ook/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpw3gT/btrd5jZnDvn/Eslytg8D9mbaVHjEGY7ook/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpw3gT/btrd5jZnDvn/Eslytg8D9mbaVHjEGY7ook/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpw3gT%2Fbtrd5jZnDvn%2FEslytg8D9mbaVHjEGY7ook%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;901&quot; height=&quot;281&quot; data-origin-width=&quot;3604&quot; data-origin-height=&quot;1124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 도식으로 보자면 위와 같다. 하나의 view안에 있었던 model과 logic을 view와 완전한 분리를 시킨다. 이를 통해서 관점의 분리가 이루어지는데 여기까지는 concept가 된다. 이것을 실체화 시킨 library가 Redux이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux에서는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model을 state로&lt;/li&gt;
&lt;li&gt;logic(action)을 reduce로 나타내고 있다.&lt;/li&gt;
&lt;/ul&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;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Single directional data Flow&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것만 이해하자. data의 흐름은 한 방향으로만 진행된다.&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;Redux의 기능&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;redux를 이해하기 위해서 가장 먼저 만나봐야할 웹사이트는 당연히 redux 공식 사이트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://redux.js.org/tutorials/essentials/part-1-overview-concepts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redux.js.org/tutorials/essentials/part-1-overview-concepts&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 tutorial이라는게 하나하나 읽자니 잘 읽히지도 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 문제점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nodejs를 이용한 개발환경 구성을 전제하고 있다&lt;/li&gt;
&lt;li&gt;그리고 react를 쓰라고 한다. 왜? 난 상태관리만 쓰고 싶은데... 왜?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react와 가장 잘 어울린다. vue와도 잘 어울린다 뭐 그런 목적이겠지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것이 가장 접근하기 힘들게 만드는 요인이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux를 쓰기 위해서 난왜 nodejs, react, vue, angular등을 같이 봐야하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 꼭 이해하자. Redux는 Redux자체로 작동하는 library이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux의 주요 API 몇개만 이해하고, 어떻게 Redux를 사용할 수 있는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redux.js.org/api/api-reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redux.js.org/api/api-reference&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/api/store#getState&quot;&gt;getState()&lt;/a&gt; : 현 Store의 최신 State를 갖어올 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/api/store#dispatchaction&quot;&gt;dispatch(action)&lt;/a&gt; : 특정 Action(business logic, reducer)를 실행 할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/api/store#subscribelistener&quot;&gt;subscribe(listener)&lt;/a&gt; : State가 변환되면, 변환되었다는 정보를 실시간으로 받고 싶다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/api/createstore&quot;&gt;createStore(reducer, [preloadedState], [enhancer])&lt;/a&gt; : State와 reducer를 관리하는 Store를 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/api/combinereducers&quot;&gt;combineReducers(reducers)&lt;/a&gt; : 여러개의 reducers를 등록하고 State를 만들어 낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에도 몇가지 api가 더 있는데, 위의 api만 알면 redux를 사용하는데 전혀 문제가 없다.&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;Redux import하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nodejs를 이용한 redux를 사용하지 않을 예정이기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.html에 다음과 같이 javascript를 import하자.&lt;/p&gt;
&lt;pre id=&quot;code_1630725104606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 javascript는 global하게 Redux라고 하는 변수로 접근 가능하다.&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;Store.js 작성&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;본 서비스는 몇가지 기능이 있는 웹사이트인지 생각해 보자.&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;703&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XNLJf/btrd03Q0dkT/Kk5GsFsgN3TlhKB4qCkcv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XNLJf/btrd03Q0dkT/Kk5GsFsgN3TlhKB4qCkcv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XNLJf/btrd03Q0dkT/Kk5GsFsgN3TlhKB4qCkcv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXNLJf%2Fbtrd03Q0dkT%2FKk5GsFsgN3TlhKB4qCkcv0%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;703&quot; height=&quot;411&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;411&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;3가지 이다.&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;DELETE_ITEM : TODO를 삭제한다&lt;/li&gt;
&lt;li&gt;ADD_ITEM : TODO를 추가한다&lt;/li&gt;
&lt;li&gt;Counter : TODO 숫자를 센다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3가지는 각각을 action이라고 명명할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View에서 사용자의 입력을 통해서 logic이 작동하고 state를 업데이트 한다. 그리고 그 업데이트된 정보로 화면을 rerendering하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 도식은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3284&quot; data-origin-height=&quot;1008&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GCpTW/btrdZWZnnxa/baC0WxD8wLl0HRC87mrXI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GCpTW/btrdZWZnnxa/baC0WxD8wLl0HRC87mrXI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GCpTW/btrdZWZnnxa/baC0WxD8wLl0HRC87mrXI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGCpTW%2FbtrdZWZnnxa%2FbaC0WxD8wLl0HRC87mrXI1%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;821&quot; height=&quot;252&quot; data-origin-width=&quot;3284&quot; data-origin-height=&quot;1008&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 도식을 기초로 해서 Store를 만들어 보자.&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;State 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한것은 state이다. 어떻게 state를 관리 할 것인가?&lt;/p&gt;
&lt;pre id=&quot;code_1630723429729&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let initStatus = {
    todos : ['test123','test456'],
    counter :2
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 내가 생각하는 기본 state이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;todos에 item을 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 item의 갯수를 counter에 입력해주고 이 값이 view에 표현될 수 있도록 한다.&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;Reducer만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 도식에서 reducer를 2개 그려놨는데 이 reducer가 business logic을 관리한다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 2개의 reducer를 디자인했다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;todos라는 items을 추가하거나 삭제하는 reducer&lt;/li&gt;
&lt;li&gt;items의 갯수를 업데이트하는 reducer&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1630723806719&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}

function counterdd(state = 0, action){
    state = action.counter?? state;
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의해야 할 것은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에 입력되는 state는 reducer별로 다르다. 동일한 state가 아니다&lt;/li&gt;
&lt;li&gt;action값은 모든 reducers가 공유한다.&lt;/li&gt;
&lt;li&gt;reducers간에 순서가 있다. Object.keys()의 순서에 따른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점을 주의해서 상위 두개의 reducer를 하나로 합쳐보자.&lt;/p&gt;
&lt;pre id=&quot;code_1630724058590&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const reducers = Redux.combineReducers({
    todos : todosdd,
    counter : counterdd
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 간단해 보이지만 여러가지 의미를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. State의 구조를 확정했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;initStatus로 확정된것이 아닌가? 라는 의문이 있겠지만, 아니다! State의 구조는 reducer의 형태 define을 통해서 확정하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1630724175735&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    todos : todosdd,
    counter : counterdd
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이부분이다. todos라는 키를 갖고 그 키가 갖는 값은 Array이다.&lt;/p&gt;
&lt;pre id=&quot;code_1630724206932&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 위의 기본 state가 Array이기 때문이다.&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;그럼 counter는?&lt;/p&gt;
&lt;pre id=&quot;code_1630724234468&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function counterdd(state = 0, action){
    state = action.counter?? state;
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 state값이 Number형태인 값이 기본 state 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 각 reducer가 나타내는 state라는 입력값은 모두 각자의 형태를 갖고 있다. 동일한 것이 아니다.&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. Reducer의 실행 순서를 확정했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;combine하는 시점에 Object.keys()에 따른 reducer의 순서를 결정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630724371019&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    todos : todosdd,
    counter : counterdd
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;todos에 정의된 reducer가 실행되고, counter의 reducer가 실행 된다.&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;3. action은 공유된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counter의 값을 counterdd라고 하는 pure function은 어떻게 얻어낼 수 있을가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store의 todos Array를 접근 가능한가? 아니.. 불가하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;action을 통해서 공유하자.&lt;/p&gt;
&lt;pre id=&quot;code_1630724485384&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;action이라고 하는 payload에 action.counter를 추가해서 todos의 길이를 주입해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1630724517195&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;action.counter = state.length;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 주입된 값은&lt;/p&gt;
&lt;pre id=&quot;code_1630724531702&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function counterdd(state = 0, action){
    state = action.counter?? state;
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;counterdd에서 사용가능하다.&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; store를 만들어내는 부분이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Create Store&lt;/h3&gt;
&lt;pre id=&quot;code_1630725188886&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default Redux.createStore(reducers,initStatus);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export default 를 해줌으로써 이 Store.js파일을 import하는 모든 모듈은 Singleton화가 된 동일한 Store를 접근 할 수밖에 없게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1630726753335&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let initStatus = {
    todos : ['test123','test456'],
    counter :2
}

function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}

function counterdd(state = 0, action){
    state = action.counter?? state;
    return state;
}


const reducers = Redux.combineReducers({
    todos : todosdd,
    counter : counterdd
});

export default Redux.createStore(reducers,initStatus);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 Store.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;이제 각 모듈에서 이 Store를 import해서 사용을 시작하자.&lt;/p&gt;
&lt;pre id=&quot;code_1630725915752&quot; class=&quot;javascript&quot; style=&quot;display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Store from &quot;../Store.js&quot;;&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;AppInput.js 에 ADD_ITEM 반영&lt;/h2&gt;
&lt;pre id=&quot;code_1630725403187&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        this.items.push(item);
        this.dispatchItems();
        this.renderList();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 item(todo) 입력을 요청받으면 그에 따라서, local items를 업데이트 시키고 외부로 todos가 변경되었음을 알리고, 현 화면을 rerendering시키는 행위를 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 redux를 통해 한방향으로 flow가 흐르게 바꾸어 줘야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1630725484011&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        // this.items.push(item);
        // this.dispatchItems();
        // this.renderList();
        Store.dispatch({type:'ADD_ITEM',data:item});
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store에 ADD_ITEM이라고 하는 action실행을 요청한다.&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;Store에 reducer인 todosadd가 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1630725532034&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해서 state의 todos가 변경되게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 변경된 값을 어떻게 view에 적용시킬 것인가?&lt;/p&gt;
&lt;pre id=&quot;code_1630725610326&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    connectedCallback(){
        let that = this;
        this.querySelector('form').addEventListener('submit',(e)=&amp;gt; {
            e.preventDefault();
            that.addItem();
        });

        const unsub = Store.subscribe(()=&amp;gt; {
            this.items = Store.getState()['todos'];
            this.renderList();
        })

        this.subscribes.push(unsub);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dom이 최초 html페이지에 적용될때 불리워지는 method이다. 이때 Store의 subscribe를 진행 함으로써, state가 변경됨과 동시에 renderList를 실행 시키도록 만들었다.&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;AppInput.js 에 DELETE_ITEM 반영&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1630725730841&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    renderList(){
        let that = this;
        this.querySelector('.js-items').innerHTML = `
            &amp;lt;ul&amp;gt;
                ${this.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.items.splice(idx, 1);
                that.renderList();
                that.dispatchItems();
            });
        });
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 item에 있는 x button을 클릭하면 현 class에 있는 items를 삭제하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 Redux를 이용해서 data flow를 변경해 주겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1630725779845&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                // that.items.splice(idx, 1);
                // that.renderList();
                // that.dispatchItems();
                Store.dispatch({type:'DELETE_ITEM',idx:idx});
            });
        });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DELETE_ITEM action과 삭제할 위치를 reducer에서 넘겨준다.&lt;/p&gt;
&lt;pre id=&quot;code_1630725813217&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function todosdd(state = [], action){
    switch(action.type){
        case 'ADD_ITEM':
            state.push(action.data);
            action.counter = state.length;
            break;
        case 'DELETE_ITEM':
            state.splice(action.idx,1);
            action.counter = state.length;
            break;
    }
    
    return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 todosdd에서 DELETE ITEM이 실행되고 state를 변화시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것 역시 앞서 subscribe를 해 놓았기 때문에 자동으로 view가 update되게 된다.&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;APPStatus.js Counter 업데이트&lt;/h2&gt;
&lt;pre id=&quot;code_1630726021235&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    getTemplate(){
        return `
            &amp;lt;aside class=&quot;app__status&quot;&amp;gt;
            &amp;lt;p role=&quot;status&quot; class=&quot;visually-hidden&quot;&amp;gt;You have done &amp;lt;span class=&quot;js-status&quot;&amp;gt;1 thing&amp;lt;/span&amp;gt; today!&amp;lt;/p&amp;gt;
            &amp;lt;div class=&quot;[ app__decor ] [ js-count ]&quot; aria-hidden=&quot;true&quot;&amp;gt;
                &amp;lt;small&amp;gt;You've done&amp;lt;/small&amp;gt;
                &amp;lt;span&amp;gt;${this.count}&amp;lt;/span&amp;gt;
                &amp;lt;small&amp;gt;things today  &amp;lt;/small&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/aside&amp;gt;
        `;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 local에 정의한 this.count를 이용해서 해당 화면을 업데이트 시켰다면, 이제는 Store를 직접 연결해서 해당 value를 표현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1630726059189&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    getTemplate(){
        return `
            &amp;lt;aside class=&quot;app__status&quot;&amp;gt;
            &amp;lt;p role=&quot;status&quot; class=&quot;visually-hidden&quot;&amp;gt;You have done &amp;lt;span class=&quot;js-status&quot;&amp;gt;1 thing&amp;lt;/span&amp;gt; today!&amp;lt;/p&amp;gt;
            &amp;lt;div class=&quot;[ app__decor ] [ js-count ]&quot; aria-hidden=&quot;true&quot;&amp;gt;
                &amp;lt;small&amp;gt;You've done&amp;lt;/small&amp;gt;
                &amp;lt;span&amp;gt;${Store.getState()['counter']}&amp;lt;/span&amp;gt;
                &amp;lt;small&amp;gt;things today  &amp;lt;/small&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/aside&amp;gt;
        `;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 가장 중요한 것은&lt;/p&gt;
&lt;pre id=&quot;code_1630726081304&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    connectedCallback(){
        const unsubscribe = Store.subscribe(()=&amp;gt;{
            this.render();
        });

        this.subscribes.push(unsubscribe);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store에 subscribe하는 것을 잊지 말아야 한다.&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;위에까지 개발이 되었다면, Redux를 이용해서 해당 서비스가 잘 작동하는 것을 확인할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Unsubscribe하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않는 class의 dom은 삭제가 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux는 기본적으로 state의 변화가 일어나면, 어떤 값이 변화 되었는지와 상관없이 call하는 방식이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭 사용하지 않을 때는 subscribe를 해지시켜 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1630726326894&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    connectedCallback(){
        const unsubscribe = Store.subscribe(()=&amp;gt;{
            this.render();
        });

        this.subscribes.push(unsubscribe);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 subscribe의 결과로 return되는 unsubscribe 객체를 갖고 있다가&lt;/p&gt;
&lt;pre id=&quot;code_1630726357218&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    disconnectedCallback(){
        this.subscribes.forEach((unsubscribe)=&amp;gt;{
            unsubscribe();
        });
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dom이 삭제되는 시점에 앞서 subscribe되어있던 모든 값들을 unsubscribe시켜 주면 된다.&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;다시 말하지만 위의 내용들은 앞선 선행내용 webcomponent를 이해하고 있어야 한다.&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;a href=&quot;https://github.com/theyoung/webcomponents/tree/WebCompWithRedux/statewithredux&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/webcomponents/tree/WebCompWithRedux/statewithredux&lt;/a&gt;&lt;/p&gt;</description>
      <category>Web</category>
      <category>Redux</category>
      <category>vallinajs</category>
      <category>webcomponent</category>
      <category>상태관리</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/234</guid>
      <comments>https://enumclass.tistory.com/234#entry234comment</comments>
      <pubDate>Sat, 4 Sep 2021 12:35:02 +0900</pubDate>
    </item>
    <item>
      <title>Jump Game II</title>
      <link>https://enumclass.tistory.com/233</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/jump-game-ii/&quot;&gt;Jump Game II&lt;/a&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;주어진 숫자 Array가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Array index 0에서 출발한다고 할때, Jump가 가능한 범위는 arr[i]라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 arr[0]이 3이면 arr[1], arr[2], arr[3]으로 자리를 옮길 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array의 마지막 Index까지 갈수 있는 최소의 Jump 횟수를 구하시오.&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;DP를 이용해서 문제를 풀면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이렇게 간단히 최적화 되는 문제가 아니다 보니 글을 남기게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP로 접근하고 이후 Greedy로 접근해 보겠다.&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;DP 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 문제가 주어졌다고 생각해 보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XFFwM/btrdwSC6yeh/Teh4uFcBT8sUugrKFhkxe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XFFwM/btrdwSC6yeh/Teh4uFcBT8sUugrKFhkxe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XFFwM/btrdwSC6yeh/Teh4uFcBT8sUugrKFhkxe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXFFwM%2FbtrdwSC6yeh%2FTeh4uFcBT8sUugrKFhkxe0%2Fimg.png&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&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;dp는 현재 index에서 마지막 index 7인 '2'까지 최소한의 jump를 기록 한다고 정의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에서 부터 접근해 보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUoxeM/btrdBqeOpK6/z7jIgFhkQatsA0L8P5ErSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUoxeM/btrdBqeOpK6/z7jIgFhkQatsA0L8P5ErSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUoxeM/btrdBqeOpK6/z7jIgFhkQatsA0L8P5ErSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUoxeM%2FbtrdBqeOpK6%2Fz7jIgFhkQatsA0L8P5ErSK%2Fimg.png&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&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;6 index는 1번의 jump로 접근 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;169&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;676&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfC4eU/btrdyke5078/z6g69LOKySiHTyvBTkCMOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfC4eU/btrdyke5078/z6g69LOKySiHTyvBTkCMOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfC4eU/btrdyke5078/z6g69LOKySiHTyvBTkCMOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfC4eU%2Fbtrdyke5078%2Fz6g69LOKySiHTyvBTkCMOk%2Fimg.png&quot; width=&quot;401&quot; height=&quot;169&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;676&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;5번 index는 6으로 한번 Jump해야함으로 2가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;169&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;676&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QfwaB/btrdBpNKgG3/uEuMZrjAkAQrEqmOe2gkHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QfwaB/btrdBpNKgG3/uEuMZrjAkAQrEqmOe2gkHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QfwaB/btrdBpNKgG3/uEuMZrjAkAQrEqmOe2gkHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQfwaB%2FbtrdBpNKgG3%2FuEuMZrjAkAQrEqmOe2gkHK%2Fimg.png&quot; width=&quot;401&quot; height=&quot;169&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;676&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;4번 index는 바로 접근 가능함으로 1번&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;179&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;716&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUaOIB/btrdES2guUl/3FI8XVIr5nNYvrmb2TPm7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUaOIB/btrdES2guUl/3FI8XVIr5nNYvrmb2TPm7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUaOIB/btrdES2guUl/3FI8XVIr5nNYvrmb2TPm7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUaOIB%2FbtrdES2guUl%2F3FI8XVIr5nNYvrmb2TPm7k%2Fimg.png&quot; width=&quot;401&quot; height=&quot;179&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;716&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;같은 방식으로 계속 해보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;179&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;716&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRWbyt/btrduVfMY8g/HdQiBK6WfNfKRwGguUpZn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRWbyt/btrduVfMY8g/HdQiBK6WfNfKRwGguUpZn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRWbyt/btrduVfMY8g/HdQiBK6WfNfKRwGguUpZn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRWbyt%2FbtrduVfMY8g%2FHdQiBK6WfNfKRwGguUpZn1%2Fimg.png&quot; width=&quot;401&quot; height=&quot;179&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;716&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;2번 index는 3번과 4번중에 선택 가능한데, 가장 작은 jump를 갖는 4번 index를 선택했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;219&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;876&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHunnj/btrdETtkli5/3IbmUkPlz8YMXuZduw3IFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHunnj/btrdETtkli5/3IbmUkPlz8YMXuZduw3IFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHunnj/btrdETtkli5/3IbmUkPlz8YMXuZduw3IFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHunnj%2FbtrdETtkli5%2F3IbmUkPlz8YMXuZduw3IFK%2Fimg.png&quot; width=&quot;401&quot; height=&quot;219&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;876&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;1번 index는 4번과 6번 아무것이나 선택 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Y4dp/btrdwTaXbXh/kKVQsumyQRdwA1iSh2TW51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Y4dp/btrdwTaXbXh/kKVQsumyQRdwA1iSh2TW51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Y4dp/btrdwTaXbXh/kKVQsumyQRdwA1iSh2TW51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Y4dp%2FbtrdwTaXbXh%2FkKVQsumyQRdwA1iSh2TW51%2Fimg.png&quot; width=&quot;401&quot; height=&quot;141&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;564&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;최종적으로 0번 index는 jump 2를 골라야 함으로 3이 답이된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 행위를 0번에서 부터 end index가지 dfs search를 하게 되면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1630330412496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def canJump(self, nums: List[int]) -&amp;gt; bool:

        @lru_cache(None)
        def helper(position, move):
            print(position, move)
            if position == (len(nums) - 1): return True
            if len(nums) &amp;lt;= position: return False

            for idx in range(position + 1,position + move + 1):
                if helper(idx, nums[idx]):
                    return True
            return False

        return helper(0, nums[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡도는 $ O(N^2) $이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 하나의 index에서 길이만큼 오른쪽을 search해야 하는데 최악의 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4,3,2,1,0 &amp;lt;- 이런식으로 두번 search가 가능하기 때문이다.&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;Greedy 접근&lt;/h3&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;121&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebnUZb/btrdEjsdwDD/QXa2kllkNbqjVL0aYT9pYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebnUZb/btrdEjsdwDD/QXa2kllkNbqjVL0aYT9pYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebnUZb/btrdEjsdwDD/QXa2kllkNbqjVL0aYT9pYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebnUZb%2FbtrdEjsdwDD%2FQXa2kllkNbqjVL0aYT9pYK%2Fimg.png&quot; width=&quot;411&quot; height=&quot;121&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;484&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;Farthest Range라는 개념을 설명하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정 먼저 시작하는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;index 0는 무조건 접근해야하는 선택가능한 범위이다. 이것은 피할 수 없다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Range의 범위는 자기 자신으로 끝나게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VDUyq/btrdqRxQrT0/g9CbY5OMXdzeOwFpKfr7CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VDUyq/btrdqRxQrT0/g9CbY5OMXdzeOwFpKfr7CK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VDUyq/btrdqRxQrT0/g9CbY5OMXdzeOwFpKfr7CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVDUyq%2FbtrdqRxQrT0%2Fg9CbY5OMXdzeOwFpKfr7CK%2Fimg.png&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&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;그럼 다음 Move는 어디에서 이루어질까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNmaa9/btrdyj1CjLV/to29a7XTavJfIGVI8MX021/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNmaa9/btrdyj1CjLV/to29a7XTavJfIGVI8MX021/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNmaa9/btrdyj1CjLV/to29a7XTavJfIGVI8MX021/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNmaa9%2Fbtrdyj1CjLV%2Fto29a7XTavJfIGVI8MX021%2Fimg.png&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&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;1번, 2번, 3번 index의 range는 역시 피할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 어떤 값을 선택하든 2번째 Jump를 이 녹색안에서 일어나야 한다는것은 명확하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmWGDW/btrdrvH1Xdo/q7QH8WaT5aw8IaM4CkDaVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmWGDW/btrdrvH1Xdo/q7QH8WaT5aw8IaM4CkDaVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmWGDW/btrdrvH1Xdo/q7QH8WaT5aw8IaM4CkDaVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmWGDW%2FbtrdrvH1Xdo%2Fq7QH8WaT5aw8IaM4CkDaVk%2Fimg.png&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&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;그럼 3번째 Range는 어디가 될까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejWBVa/btrdDyDbDWN/wvM81x3AxJYr0dcSgpGFq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejWBVa/btrdDyDbDWN/wvM81x3AxJYr0dcSgpGFq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejWBVa/btrdDyDbDWN/wvM81x3AxJYr0dcSgpGFq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejWBVa%2FbtrdDyDbDWN%2FwvM81x3AxJYr0dcSgpGFq0%2Fimg.png&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&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;5로 인해서 5,6,7번 index가 선택 되게 된다. 2번 1번 index도 해당 범위 내임으로 변하는 것은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 피할 수 없는 jump의 위치는 어디인가?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MlDfj/btrdC52gAor/ai6oHJsGfhLOMpw040zJjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MlDfj/btrdC52gAor/ai6oHJsGfhLOMpw040zJjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MlDfj/btrdC52gAor/ai6oHJsGfhLOMpw040zJjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMlDfj%2FbtrdC52gAor%2Fai6oHJsGfhLOMpw040zJjk%2Fimg.png&quot; width=&quot;411&quot; height=&quot;175&quot; data-origin-width=&quot;1644&quot; data-origin-height=&quot;700&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;3번째 jump는 어떤한 경우에라도 통과해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 index는 jump의 횟수에 넣을 필요 없음으로&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;416&quot; height=&quot;175&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ryrs0/btrdDK4tP6f/M1DQCEFR53cJFhdzdkTVDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ryrs0/btrdDK4tP6f/M1DQCEFR53cJFhdzdkTVDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryrs0/btrdDK4tP6f/M1DQCEFR53cJFhdzdkTVDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fryrs0%2FbtrdDK4tP6f%2FM1DQCEFR53cJFhdzdkTVDk%2Fimg.png&quot; width=&quot;416&quot; height=&quot;175&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;700&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;최소 jump 횟수는 3이된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Range 방식의 Greedy가 가능한 이유는 전제사항에&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반듯이 end에 접근할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 전제가 있기 때문에 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 다음 Range 범위가 1 이상 늘어나지 않는다면 false처리 하는 방법도 있을꺼 같긴 하다.&lt;/p&gt;
&lt;pre id=&quot;code_1630331244585&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def canJump(self, nums:List[int]) -&amp;gt; bool:
        lastIdx = len(nums) - 1

        for i in range(len(nums)-1, -1, -1):
            if lastIdx &amp;lt;= i + nums[i] :
                lastIdx = i

        return True if lastIdx == 0 else False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡도는 $ O(N) $ 이 된다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>DP</category>
      <category>greedy</category>
      <category>Jump Game</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/233</guid>
      <comments>https://enumclass.tistory.com/233#entry233comment</comments>
      <pubDate>Mon, 30 Aug 2021 22:48:00 +0900</pubDate>
    </item>
    <item>
      <title>Android CTS/GTS UnofficialApisUsageTest 비 인터페이스 제한 대응</title>
      <link>https://enumclass.tistory.com/232</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android를 이용한 Hidden API 및 Refection을 활용한 Access가 제한되어있는 API를 통한 개발이 Android 9에서 부터 적용되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android APP을 개발하고 App Store 검증을 받을 때, restriction에 걸리면 릴리즈가 제한된다.&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;Hidden API 확인 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 제한되는 API는 점점 많아지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 URL을 들어가면 확인 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dl.google.com/developers/android/sc/non-sdk/hiddenapi-flags.csv&quot;&gt;hiddenapi-flags.csv&lt;/a&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;Landroid/view/autofill/AutofillManager;-&amp;gt;mServiceClientCleaner:Lsun/misc/Cleaner; lo-prio max-target-o&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우에는 sun.misc.cleaner를 사용하면 안된다.&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;Hidden API 사용 확인 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런것을 쉽게 check하기 위해서 Google에서는 Tool을 제공해 주는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://android.googlesource.com/platform/prebuilts/runtime/+archive/master/appcompat.tar.gz&quot;&gt;veridex 도구&lt;/a&gt; 를 통해서 테스트 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 Google Manual에 이미 있는 것이기 때문에 그것을 적고자 Posting 하는 것은 아니고,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GTS 중에 UnofficialApisUsageTest&amp;nbsp; 오류가 나는 경우에 대해서 쓰고자 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 CTS에서 Test가 되던 것인데, 언젠가 부터는 GTS로 테스트 되는 시점이 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 CTS와 다르게 GTS는 별도의 Test Tool이 일반인에게 Open 되지 않는다. 그렇기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UnofficialApisUsageTest Disallowed type ref 오류가 나면 난감하다. 왜냐하면 내가 만든 소스에 없는 코드 조차도 오류대상이 되기 때문이다. 어떤 Library에서 해당 오류가 나는지도 알 수가 없다.&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;UnofficialApisUsageTest 프로그램 사용법&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;&lt;a href=&quot;https://android.googlesource.com/platform/cts/+/810c260130d1d8c7714d2949e8c2fdb3df444a76/hostsidetests/api/src/com/android/cts/api/UnofficialApisUsageTest.java&quot;&gt;https://android.googlesource.com/platform/cts/+/810c260130d1d8c7714d2949e8c2fdb3df444a76/hostsidetests/api/src/com/android/cts/api/UnofficialApisUsageTest.java&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 참고해서 간단한 check 프로그램을 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1629810600560&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class RunClass {
    private static final Map&amp;lt;String, DefinedClass&amp;gt; definedClassesInDex = new HashMap&amp;lt;&amp;gt;();

    public static void main(String[] args) throws Exception{
        File file = new File(&quot;dex/apk_name.apk&quot;);

        DexBackedDexFile dexFile = DexFileFactory.loadDexEntry(file, &quot;classes.dex&quot;, true, Opcodes.getDefault()).getDexFile();

        dexFile.getClasses().stream().forEach(dexBackedClassDef -&amp;gt; {
            DefinedClass dx = DexAnalyzer.definedClassFrom(dexBackedClassDef);
            definedClassesInDex.put(dx.getName(), dx);
        });
        System.out.println(String.valueOf(dexFile.getClasses().size()));

        List list0 = dexFile.getReferences(0); //String Ref
        List list1 = dexFile.getReferences(1); // Type Ref
        List list2 = dexFile.getReferences(2); // Field Ref
        List list3 = dexFile.getReferences(3); // Method Ref

        System.out.println(&quot;String Ref&quot; + list0.size());
        System.out.println(&quot;Type Ref&quot; + list1.size());
        System.out.println(&quot;Field Ref&quot; + list2.size());
        System.out.println(&quot;Method Ref&quot; + list3.size());

        List&amp;lt;DexBackedTypeReference&amp;gt; filtered = (List&amp;lt;DexBackedTypeReference&amp;gt;) list1.stream().filter((Predicate&amp;lt;DexBackedTypeReference&amp;gt;) typeRef-&amp;gt; {
            return 0 &amp;lt; typeRef.getType().indexOf(&quot;sun&quot;);
        }).collect(Collectors.toList());

        System.out.println(&quot;Error Count = &quot; + String.valueOf(filtered.size()));
        filtered.forEach((item)-&amp;gt;{
            System.out.println(item.getType());
        });

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 Dex 폴더 밑에&lt;/p&gt;
&lt;pre id=&quot;code_1629810624928&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;File file = new File(&quot;dex/apk_name.apk&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석할 apk를 넣고, 위의 apk name을 변경해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1629810655259&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        List&amp;lt;DexBackedTypeReference&amp;gt; filtered = (List&amp;lt;DexBackedTypeReference&amp;gt;) list1.stream().filter((Predicate&amp;lt;DexBackedTypeReference&amp;gt;) typeRef-&amp;gt; {
            return 0 &amp;lt; typeRef.getType().indexOf(&quot;sun&quot;);
        }).collect(Collectors.toList());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 났다고 하는 이름을 (위는 sun) 지정해 주고 몇개의 Error count가 나오면 되는지 보면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 별거 아니긴 한데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 라이브러리에서 해당 오류 대상 Object를 참조하는지 알 방법이 없고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 앱개발사에서 Manufacture에게 GTS 실시간으로 돌려달라고 할 수도 없는 경우, 사용하는 라이브러리를 지워가면서 Test 하는 방법말고는 다른 방법이 없어 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code는 여기 있고 주의 해야할 것은 프로젝트 구성시 lib 밑에있는 baksmali 를 import 시켜줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/DexUnofficialChecker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/DexUnofficialChecker&lt;/a&gt;&lt;/p&gt;</description>
      <category>Android</category>
      <category>Android</category>
      <category>CTS</category>
      <category>GTS</category>
      <category>UnofficialApisUsageTest</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/232</guid>
      <comments>https://enumclass.tistory.com/232#entry232comment</comments>
      <pubDate>Tue, 24 Aug 2021 22:16:10 +0900</pubDate>
    </item>
    <item>
      <title>Web Component 상태관리 만들기 (Vanillajs)</title>
      <link>https://enumclass.tistory.com/231</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Web Component를 통해서 Vanillajs 기반 웹서비스를 만드는 방법을 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.16 - [Web] - 기존 web site를 components 로 다시 만들기 (No State management)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Statement를 통해서 Component를 관리 하는 방법을 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;u&gt;&lt;b&gt;본 내용은 위의 링크 내용의 연장이다.&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 내용을 더 이야기 하기 전에 MVVM이란 무었인지 꼭 이해하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/230&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.20 - [Web] - MVC, MVP, MVVM 패턴의 이해&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 유명한 Statement 관리 library는 redux이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redux를 통해 어떤식으로 Statement가 관리 되는지 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot; width=&quot;509&quot; height=&quot;382&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhtAoN/btrcVkk8DzW/oU2FU3PREaDFMoBcTa4PFk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhtAoN/btrcVkk8DzW/oU2FU3PREaDFMoBcTa4PFk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhtAoN/btrcVkk8DzW/oU2FU3PREaDFMoBcTa4PFk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bhtAoN/btrcVkk8DzW/oU2FU3PREaDFMoBcTa4PFk/img.gif&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot; width=&quot;509&quot; height=&quot;382&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;&lt;a href=&quot;https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UI에서 Event가 Dispatch 되면 Action을 발생시킨다.&lt;/li&gt;
&lt;li&gt;Action은 해당 Action과 연결된 reducer를 작동시킨다.&lt;/li&gt;
&lt;li&gt;Reducer는 State를 update 시킨다.&lt;/li&gt;
&lt;li&gt;update된 State는 연결된 UI를 rendering 시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 redux의 전체적인 data flow이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 data flow를 아래 참고했던 url의 내용과 비교해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://css-tricks.com/build-a-state-management-system-with-vanilla-javascript/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://css-tricks.com/build-a-state-management-system-with-vanilla-javascript/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;611&quot; height=&quot;341&quot; data-origin-width=&quot;2444&quot; data-origin-height=&quot;1364&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p4G45/btrcVj0QqHt/QXjQxXpwUhbmUheAiNQmX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p4G45/btrcVj0QqHt/QXjQxXpwUhbmUheAiNQmX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p4G45/btrcVj0QqHt/QXjQxXpwUhbmUheAiNQmX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp4G45%2FbtrcVj0QqHt%2FQXjQxXpwUhbmUheAiNQmX1%2Fimg.png&quot; width=&quot;611&quot; height=&quot;341&quot; data-origin-width=&quot;2444&quot; data-origin-height=&quot;1364&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;큰 작동의 원리는 이름을 제외하고는 동일하다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 위의 참조 링크내용을 바탕으로 Web component에서 Vanilla로 State management를 만들어 보도록하자.&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;h2 data-ke-size=&quot;size26&quot;&gt;Mediator&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Publish/Subscribe 라고 하는 이름으로 많이 알려진 패턴이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;391&quot; height=&quot;242&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;968&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKA8hW/btrcVkrUFf9/nK0K9nokrYv4CGKcMAikRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKA8hW/btrcVkrUFf9/nK0K9nokrYv4CGKcMAikRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKA8hW/btrcVkrUFf9/nK0K9nokrYv4CGKcMAikRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKA8hW%2FbtrcVkrUFf9%2FnK0K9nokrYv4CGKcMAikRK%2Fimg.png&quot; width=&quot;391&quot; height=&quot;242&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;968&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;어떤 채널에 다수개의 Subscriber가 있을 때, 해당 채널로 하나의 Publisher가 Data를 발행하면 Subscriber들은 해당 Data를 무조건 받는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subscriber는 Publisher를 알 수 없고, Subscriber는 Publisher를 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observer 패턴에서는 Publisher를 Observable, Subscriber를 Observer라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 내용을 간단히 만든 코드가 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1629639318031&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class Mediator {
    constructor(events = {}){
        this.events = events;
    }

    subscribe(key, callback){
        if(!this.events.hasOwnProperty(key)){
            this.events[key] = new Array();
        }

        this.events[key].push(callback);
    }

    publish(key, params = {}){
        if(!this.events.hasOwnProperty(key)) return [];

        return this.events[key].map(callback =&amp;gt; {
            callback(params);
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 events는 channel 명이라고 생각하면 된다. subscribe는 events에 특정키에 Publish가 발생하면 수신을 받을 Subscriber 의 명시적 행위이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;391&quot; height=&quot;282&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;1128&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTpEch/btrcNjVd1HP/XDLf8PWROKerCGYCHrnJo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTpEch/btrcNjVd1HP/XDLf8PWROKerCGYCHrnJo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTpEch/btrcNjVd1HP/XDLf8PWROKerCGYCHrnJo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTpEch%2FbtrcNjVd1HP%2FXDLf8PWROKerCGYCHrnJo1%2Fimg.png&quot; width=&quot;391&quot; height=&quot;282&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;1128&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;코드와 그림사이의 이름이 달라서 이해하기 어려울 수 있기때문에, 그림을 코드에 맞추어 보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;subscribe에서 key명으로 callback을 등록하고&lt;/li&gt;
&lt;li&gt;publish에서 key명으로 callback을 call한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Store&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Store를 만들어 보자. Store는 많은 일을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서는 각 Component에서 State 또는 Data에 대한 수정 대응 행위를 했다고 하면 이제부터는 Store에서만 해당 행위가 가능해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Action을 수신 받을 수 있어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;받은 Action에 따라서 실제 Action을 실행 할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;Action이 완료된 이후 Data의 수정을 요청(Commit) 받을 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;요청 받은 Commit에 따라서 Mutation을 실행 할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;Mutation으로 인해 수정된 State를 관련된 Component에게 통보하여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다소 복잡한데 이를 그림으로 보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;761&quot; height=&quot;361&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;1444&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8Y6ot/btrcStWYoAD/ddbpeLbkWhtVnJ0FstVnuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8Y6ot/btrcStWYoAD/ddbpeLbkWhtVnJ0FstVnuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8Y6ot/btrcStWYoAD/ddbpeLbkWhtVnJ0FstVnuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8Y6ot%2FbtrcStWYoAD%2FddbpeLbkWhtVnJ0FstVnuK%2Fimg.png&quot; width=&quot;761&quot; height=&quot;361&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;1444&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;위의 그림은 Component가 각각 ADD_ITEM, DELETE_ITEM Action을 CALL하고 Strore에서 이에 관련있는 Action을 찾아 Dispatch하고 Action은 Business Logic을 수행하고 Mutation에 Commit함으로써 State를 최종적으로 Update하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할점은 State의 Update는 반듯이 Mutation이 완료된이 후 Store를 통해서 이루어 진다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 관리하기 위해서 Store의 상태를 관리해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;761&quot; height=&quot;361&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;1444&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YYQII/btrcXk6gKu5/KIJh1RbrOkKc6ifS4hF8jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YYQII/btrcXk6gKu5/KIJh1RbrOkKc6ifS4hF8jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YYQII/btrcXk6gKu5/KIJh1RbrOkKc6ifS4hF8jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYYQII%2FbtrcXk6gKu5%2FKIJh1RbrOkKc6ifS4hF8jk%2Fimg.png&quot; width=&quot;761&quot; height=&quot;361&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;1444&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;위의 붉은 색을 보면 Action이 이러나기 전의 store상태는 resting, Action중은 action, Mutation은 mutation그리고 Commit은 store가 mutation인 상태에서만 가능하게 만들면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자이제 필요한 내용들을 같이 개발해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629640560230&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import PubSub from &quot;./pubsub.js&quot;;

export default class Store {
    constructor(params){
        this.actions = {};
        this.mutations = {};
        this.state = {};
        this.status = 'resting';
        this.events = new PubSub();
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store에 필요한 것은&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;actions : Action을 담을 공간&lt;/li&gt;
&lt;li&gt;mutations : Mutation을 담을 공간&lt;/li&gt;
&lt;li&gt;state : 본 서비스 전체에서 사용될 모든 값&lt;/li&gt;
&lt;li&gt;status : Store의 현재 상태&lt;/li&gt;
&lt;li&gt;events : Subcriber를 한 components들에게 state가 업데이트 되었을 때 Event를 공지하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1629640717603&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import PubSub from &quot;./pubsub.js&quot;;

export default class Store {
    constructor(params){
        this.actions = {};
        this.mutations = {};
        this.state = {};
        this.status = 'resting';
        this.events = new PubSub();

        if(params.hasOwnProperty('actions')){
            this.actions = params.actions;
        }
        if(params.hasOwnProperty('mutations')){
            this.mutations = params.mutations;
        }

        const that = this;

        this.state = new Proxy(
            (params.state || {}), {
                set : function(state, key, value){
                    state[key] = value;
                    console.log(`stateChange: ${key}: ${value}`);
                    that.events.publish('stateChange',that.state);
                    that.status = 'resting';
                    return true;
                }
            }
        );
    }

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;actions와 mutations는&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629640742141&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        if(params.hasOwnProperty('actions')){
            this.actions = params.actions;
        }
        if(params.hasOwnProperty('mutations')){
            this.mutations = params.mutations;
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 입력해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어려운 부분은 state에 대한 부분이다.&lt;/p&gt;
&lt;pre id=&quot;code_1629640770149&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        this.state = new Proxy(
            (params.state || {}), {
                set : function(state, key, value){
                    state[key] = value;
                    console.log(`stateChange: ${key}: ${value}`);
                    that.events.publish('stateChange',that.state);
                    that.status = 'resting';
                    return true;
                }
            }
        );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Proxy를 사용했는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 기능은 java의 refection에 있는 dynamic proxy와 비슷한 목적으로 갖고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;371&quot; height=&quot;291&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1164&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5OVRf/btrcNkT71G9/FoUzSOWN7ZKVByO3Wa9nO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5OVRf/btrcNkT71G9/FoUzSOWN7ZKVByO3Wa9nO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5OVRf/btrcNkT71G9/FoUzSOWN7ZKVByO3Wa9nO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5OVRf%2FbtrcNkT71G9%2FFoUzSOWN7ZKVByO3Wa9nO0%2Fimg.png&quot; width=&quot;371&quot; height=&quot;291&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;1164&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;우리가 일반적인 개발을 할때, this.state에 어떤값을 assign하거나 get을 할 때 바로 &quot;=&quot;을 사용하던가, 그냥 &quot;this.state.items&quot; 이런식으로 retrive를 하면 된다. 그런데 그사이에 끼어들어서 무언가를 더 하고 싶을때 Proxy라고 하는 기능이 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 Spring에서는 Point cut이라는 개념과 비슷하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 다시 보자면 이런 의미다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;this.state에 어떤 값을 assign하면 state의 값을 update하고&lt;/li&gt;
&lt;li&gt;stateChange라고 하는 Log를 콘솔에 찍고,&lt;/li&gt;
&lt;li&gt;events에 stateChange라는 event를 publish하고 store의 상태를 resting으로 변환하라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서 Store에서 만들어야 하는 기능 중 하나가 위의 내용으로 해결이 되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Action을 수신 받을 수 있어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;받은 Action에 따라서 실제 Action을 실행 할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;Action이 완료된 이후 Data의 수정을 요청(Commit) 받을 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;요청 받은 Commit에 따라서 Mutation을 실행 할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Mutation으로 인해 수정된 State를 관련된 Component에게 통보하여야 한다.&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 수신받는 부분과 Action을 실행하는 부분을 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629641404050&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import PubSub from &quot;./pubsub.js&quot;;

export default class Store {
    constructor(params){
        this.actions = {};
        this.mutations = {};
        this.state = {};
        this.status = 'resting';
        this.events = new PubSub();

        if(params.hasOwnProperty('actions')){
            this.actions = params.actions;
        }
        if(params.hasOwnProperty('mutations')){
            this.mutations = params.mutations;
        }

        const that = this;

        this.state = new Proxy(
            (params.state || {}), {
                set : function(state, key, value){
                    state[key] = value;
                    console.log(`stateChange: ${key}: ${value}`);
                    that.events.publish('stateChange',that.state);
                    that.status = 'resting';
                    return true;
                }
            }
        );
    }

    dispatch(actionKey, payload){
        if(typeof this.actions[actionKey] !== 'function'){
            console.error(`Action ${actionKey} does not exist`);
            return false;
        }

        this.status = 'action';
        this.actions[actionKey](this,payload);
        return true;
    }

};&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;dispatch이다. component는 어떤 Action을 요청할 것이지를 action key로 요청을 하고 해당 action key에 관련 data를 같이 넘겨주면 action을 실행 하는 형태이다. 물론 action을 실행하면서 status를 action으로 변경하는 것을 잊으면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 action은 어떻게 생겼을까?&lt;/p&gt;
&lt;pre id=&quot;code_1629641474536&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default {
    &quot;ADD_ITEM&quot; : function(context, params){
        context.commit(&quot;addItem&quot;, params);
    },
    &quot;DELETE_ITEM&quot; : function(context, params){
        context.commit(&quot;deleteItem&quot;, params);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 Action은 대문자로 표현된다. 각 Action은 독립적으로 어떤 행위를 하고 최종적으로 this.state르 업데이트해달라고 요청하게 된다. 이 요청을 우리는 commit이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보자면 하나의 action에 하나의 commit이 발생하는데, 이건 경우에 따라서 1:n의 commit이 발생 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1629641602592&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Mediator from &quot;./Mediator.js&quot;;

export default class Store {
    constructor(state = {}, actions, mutations){
        let that = this;
        this.actions = actions;
        this.mutations = mutations;
        this.events = new Mediator();
        this.status = 'resting'; // 'resting' 'action' 'mutation'
        this.state = new Proxy(state,{
                get: function(target,prop,recever){
                    return target[prop]?? null;
                },
                set: function(state, key, value){
                    state[key] = value;
                    //TODO : 해당 state 에 연결된 화면 render필요
                    that.events.publish('stateChange',state);
                    return true;
                }
            }
        );
    }

    dispatch(actionKey, prams){ //Component -&amp;gt; Action Dispatch
        if(!this.actions.hasOwnProperty(actionKey)){
            console.error(`ActionKey ${actionKey} does not exist`);
            return false;
        }
        this.status = 'action';
        return this.actions[actionKey](this, prams);
    }

    commit(mutationKey, prams){ //Action -&amp;gt; mutation commit -&amp;gt; proxy trigger
        if(!this.mutations.hasOwnProperty(mutationKey)){
            console.error(`MutationKey ${mutationKey} does not exist`);
            return false;
        }
        this.status = 'mutation';
        let newState = this.mutations[mutationKey](this.state, prams); //action은 state를 return 해야한다.

        Object.assign(this.state, newState);
        return true;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store의 마지막 코드인 commit이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Action과 비슷한 코드이다. 다른점이 있다면 실행된 결과값을 this.state로 반환시키고 이것으로 Object.assign함으로써 위의 Proxy 모듈이 작동 되도록 만든것이 다르다면 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 mutations는 어떻게 생긴 코드일까?&lt;/p&gt;
&lt;pre id=&quot;code_1629642895661&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default {
    addItem : function(state,item){
        state.items.push(item);
        return state;
    }, 
    deleteItem : function(state,index){
        state.items.splice(index,1);
        return state;
    }
}&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;state값에 items를 push하거나 splice하는 방식으로 state의 값을 변화 시킨다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;여기서 의문이 하나 생긴다. Proxy는 어떠한 set이나 get이 발생할 경우 모두 catch할 수있다. 그리고 Proxy Object도 Object임으로 Memory Address를 arguments에 Passing 할 것이다.&lt;br /&gt;그래서 Object.assign이 없다고 해도 mutations내에서 state의 형태를 변화 시키면서 Proxy의 set이 발생할 것이라고 예상했지만 그렇지 않다.&lt;br /&gt;정확한 사유는 모르겠지만 this Scope를 잃는 순간 Proxy는 더이상 작동이 안되는 것 같다.&lt;br /&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;위의 내용까지 해서 Store에 대한 작동은 모두 알아 보았다. 이제 component에 해당 행위를 이식하기만 하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;Action을 수신 받을 수 있어야 한다.&amp;nbsp;&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;받은 Action에 따라서 실제 Action을 실행 할 수 있어야 한다.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Action이 완료된 이후 Data의 수정을 요청(Commit) 받을 수 있어야 한다.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;요청 받은 Commit에 따라서 Mutation을 실행 할 수 있어야 한다.&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;Mutation으로 인해 수정된 State를 관련된 Component에게 통보하여야 한다.&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Store를 다 만들었다. 이제 Store에 들어가야할 state와 actions 그리고 mutations의 Instance를 만들자.&lt;/p&gt;
&lt;pre id=&quot;code_1629727502185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Store from &quot;./Store.js&quot;
import mutations from &quot;../props/mutations.js&quot;
import actions from &quot;../props/actions.js&quot;
import state from &quot;../props/state.js&quot;;

export default new Store(state, actions, mutations);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인스턴스는 본 서비스 전반에서 사용되는 서비스가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parameters로 Passing되지 않고 Singletone으로 단하나의 Store로 작동 된다는 것을 꼭 이해해야 한다.&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;App Input 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 만들어 놓은 AppInput 파일을 리뷰해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629727683710&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class AppInput extends HTMLElement {
    constructor(){
        super();
        this.items = new Array();

        this.render();
    }

    render(){
        this.innerHTML = this.getTemplate();
    }

    getTemplate(){
        return `
            &amp;lt;h2 class=&quot;app__heading&quot;&amp;gt;What you've done&amp;lt;/h2&amp;gt;
            &amp;lt;div class=&quot;js-items&quot; aria-live=&quot;polite&quot; aria-label=&quot;A list of items you have done&quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;form class=&quot;[ new-item ] [ boilerform ] [ js-form ]&quot;&amp;gt;
                &amp;lt;div class=&quot;boilerform&quot;&amp;gt;
                    &amp;lt;!-- Form styles from the https://boilerform.design boilerplate --&amp;gt;
                    &amp;lt;label for=&quot;new-item-field&quot; class=&quot;[ new-item__label ] [ c-label ]&quot;&amp;gt;Add a new item&amp;lt;/label&amp;gt;
                    &amp;lt;input type=&quot;text&quot; class=&quot;[ new-item__details ] [ c-input-field ]&quot; id=&quot;new-item-field&quot; autocomplete=&quot;off&quot; /&amp;gt;
                    &amp;lt;button class=&quot;[ c-button ] [ new-item__button ]&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/form&amp;gt;
        `;
    }

    connectedCallback(){
        let that = this;
        this.querySelector('form').addEventListener('submit',(e)=&amp;gt; {
            e.preventDefault();
            that.addItem();
        });
    }
    
    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        this.items.push(item);
        this.dispatchItems();
        this.renderList();
    }

    renderList(){
        let that = this;
        this.querySelector('.js-items').innerHTML = `
            &amp;lt;ul&amp;gt;
                ${this.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.items.splice(idx, 1);
                that.renderList();
                that.dispatchItems();
            });
        });
    }

    dispatchItems(){
        this.dispatchEvent(new CustomEvent('changed',{
            detail : {data : this.items.length},
            bubbles : true
        }));
    }

    disconnectedCallback(){

    }

    static get observedAttributes(){
        return [];
    }

    attributeChangedCallback(name, oldValue, newValue){

    }
};

customElements.get('app-input')?? customElements.define('app-input',AppInput);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주요하게 사용되는 기능은 2가지이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;addItem : 새로운 Todo 를 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1629727854965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        this.items.push(item);
        this.dispatchItems();
        this.renderList();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deleteItem : click이 된 Todo를 삭제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1629727870505&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.items.splice(idx, 1);
                that.renderList();
                that.dispatchItems();
            });
        });&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;이 두가지를 Store를 이용해서 기능 변경을 하도록 하겠다.&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;Add Item &amp;amp; Delete Item Action 연결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addItem에 대한 Action과 그 값을 commit하는 Mutation에 대해서 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629727932244&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//actions.js
export default {
    &quot;ADD_ITEM&quot; : function(context, params){
        context.commit(&quot;addItem&quot;, params);
    },
    &quot;DELETE_ITEM&quot; : function(context, params){
        context.commit(&quot;deleteItem&quot;, params);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;actions.js에 위와 같이 2가지 Action을 정의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두개 모두 별다른 business 로직이 필요로 하진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Item의 Add와 Item의 삭제를 작동시키는 부분을 만들자.&lt;/p&gt;
&lt;pre id=&quot;code_1629728010401&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//mutations.js
export default {
    addItem : function(state,item){
        state.items.push(item);
        return state;
    }, 
    deleteItem : function(state,index){
        state.items.splice(index,1);
        return state;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;mutations.js이다. 앞서 action에서부터 넘어온 item 정보를 받아서 기존 state에 추가하거나. 삭제하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 행위를 기반으로 기존 코드를 변경해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629728113893&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 소스 AppInput.js    
    
    connectedCallback(){
        let that = this;
        this.querySelector('form').addEventListener('submit',(e)=&amp;gt; {
            e.preventDefault();
            that.addItem();
        });
    }
    
    addItem(){
        let item = this.querySelector('#new-item-field').value.trim();
        this.items.push(item);
        this.dispatchItems();
        this.renderList();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 소스에서 addItem하는 부분을 Store의 Action을 Call하는 것으로 변경해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1629728161475&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//수정된 소스
	connectedCallback(){
        let that = this;
        this.querySelector('form').addEventListener('submit',(e)=&amp;gt; {
            e.preventDefault();
            that.addItemWithStore(); //for state management
        });
    }
    
    addItemWithStore(){
        let item = this.querySelector('#new-item-field').value.trim();   
        StoreInstance.dispatch(&quot;ADD_ITEM&quot;, item);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존과 달라진 점은 기존에는 Items의 Data를 AppInput내에서 직접 관리했다고 하면, Store를 이용하면 더 이상 Items를 직접 관리할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점이 가능 큰 차이점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위의 수정사항도 보면 new-item-field에서 item의 data만 갖어올뿐이지 state.items를 직접 control 하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 state는 어떻게 생겼을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 state.js이다.&lt;/p&gt;
&lt;pre id=&quot;code_1629728278496&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default {
    items : ['my first','status management'],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리하고자 하는 Data나 state는 모두 이 값을 initial value로 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 값이 없다고 하더라도, 나중에 유지보수가 안될 수 있음으로 여기에 모든 key를 등록해 주자.&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;Delete code도 마저 수정해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1629728496994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//기존 AppInput.js
    renderList(){
        let that = this;
        this.querySelector('.js-items').innerHTML = `
            &amp;lt;ul&amp;gt;
                ${this.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.items.splice(idx, 1);
                that.renderList();
                that.dispatchItems();
            });
        });
    }

    dispatchItems(){
        this.dispatchEvent(new CustomEvent('changed',{
            detail : {data : this.items.length},
            bubbles : true
        }));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AppInput.js를 보면 Redering을 완료한 이후 rendering된 button에 click event를 통해서 어떤 Todo가 삭제 되어야 하는지 알아내는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 보면 자신이 갖고 있는 items에서 splice를 통해서 직접 Todo를 삭제 하는 모습을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1629728611631&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                that.items.splice(idx, 1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 모두 Store의 Action으로 형태를 변경해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1629728641937&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//변경된 코드

    renderList(){
        let that = this;
        const list = `
            &amp;lt;ul&amp;gt;
                ${this.store.state.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelector('.js-items').innerHTML = list;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.dispatchItemsWithStore(idx);
            });
        });
    }

    dispatchItemsWithStore(idx){
        this.store.dispatch(&quot;DELETE_ITEM&quot;,idx);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;store에 dispatch를 통해서 Action을 요청한 모습을 확인 할 수있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Action이 발생하고 나면 나타날 작동 순서를 생각해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;1031&quot; height=&quot;141&quot; data-origin-width=&quot;4124&quot; data-origin-height=&quot;564&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHukL/btrc13wX6oY/Haqpm9dDWddOgEuzflGLZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHukL/btrc13wX6oY/Haqpm9dDWddOgEuzflGLZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHukL/btrc13wX6oY/Haqpm9dDWddOgEuzflGLZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHukL%2Fbtrc13wX6oY%2FHaqpm9dDWddOgEuzflGLZk%2Fimg.png&quot; width=&quot;1031&quot; height=&quot;141&quot; data-origin-width=&quot;4124&quot; data-origin-height=&quot;564&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;AppInput component에서 dispatch를 발생시키면 순서에 따라서 action과 commit 그리고 events가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;events가 발생하면 어떤 현상이 일어나야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 해당 events를 Observe하고 있는 Components들은 모두 해당 state의 변화에 따라서 Rendering이 일어나야 한다.&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;Component에 Event 등록하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 state의 값이 변경되면 Redering이 일어날 수 있도록 각 Component를 store의 event에 등록하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Channel명은 'stateChange'로 일괄로 통일 시키자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 커지면 특정 state의 수정상태만 monitoring해서 처리하도록 고도화 되어야 하지만,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도까지만 개발 했으면 된거 아닐까? 이제부터는 그냥 Redux 쓰는 걸로 하자.... ㅎㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AppStatus.js event 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1629729170986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import StoreInstance from &quot;../statem/StoreInstance.js&quot;;

export default class AppStatus extends HTMLElement {
    constructor(){
        super();

        this.store = StoreInstance;
        this.store.events.subscribe('stateChange',this.render.bind(this));

        this.render();
    }

    render(){
        this.innerHTML = this.getTemplate();
    }

    getTemplate(){
        return `
            &amp;lt;aside class=&quot;app__status&quot;&amp;gt;
            &amp;lt;p role=&quot;status&quot; class=&quot;visually-hidden&quot;&amp;gt;You have done &amp;lt;span class=&quot;js-status&quot;&amp;gt;1 thing&amp;lt;/span&amp;gt; today!&amp;lt;/p&amp;gt;
            &amp;lt;div class=&quot;[ app__decor ] [ js-count ]&quot; aria-hidden=&quot;true&quot;&amp;gt;
                &amp;lt;small&amp;gt;You've done&amp;lt;/small&amp;gt;
                &amp;lt;span&amp;gt;${this.store.state.items.length}&amp;lt;/span&amp;gt;
                &amp;lt;small&amp;gt;things today  &amp;lt;/small&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/aside&amp;gt;
        `;
    }

    connectedCallback(){

    }

    disconnectedCallback(){

    }

    static get observedAttributes(){
        // return ['item-count'];
    }

    attributeChangedCallback(name, oldValue, newValue){
        // if(name = 'item-count'){
        //     this.count = newValue;
        //     this.render();
        // }

    }
};

customElements.get('app-status')?? customElements.define('app-status',AppStatus);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위코드에서 기존과 바뀐점은&lt;/p&gt;
&lt;pre id=&quot;code_1629729189459&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    constructor(){
        super();

        this.store = StoreInstance;
        this.store.events.subscribe('stateChange',this.render.bind(this));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 store의 stateChange를 subscribe 했다. 이제 해당 이벤트가 오면 render Method가 불리워 질것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1629729202057&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    getTemplate(){
        return `
            &amp;lt;aside class=&quot;app__status&quot;&amp;gt;
            &amp;lt;p role=&quot;status&quot; class=&quot;visually-hidden&quot;&amp;gt;You have done &amp;lt;span class=&quot;js-status&quot;&amp;gt;1 thing&amp;lt;/span&amp;gt; today!&amp;lt;/p&amp;gt;
            &amp;lt;div class=&quot;[ app__decor ] [ js-count ]&quot; aria-hidden=&quot;true&quot;&amp;gt;
                &amp;lt;small&amp;gt;You've done&amp;lt;/small&amp;gt;
                &amp;lt;span&amp;gt;${this.store.state.items.length}&amp;lt;/span&amp;gt;
                &amp;lt;small&amp;gt;things today  &amp;lt;/small&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;/aside&amp;gt;
        `;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Render Method는 getTemplate를 Call하게 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존과 다른점은 this.store.state.itmes를 직접 접근 하고 있다는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1629729213713&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    static get observedAttributes(){
        // return ['item-count'];
    }

    attributeChangedCallback(name, oldValue, newValue){
        // if(name = 'item-count'){
        //     this.count = newValue;
        //     this.render();
        // }

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 item count를 갖고 오기위해 attribute를 oberve했던 부분은 삭제해준다.&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;AppInput.js event 등록하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1629729349883&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import StoreInstance from &quot;../statem/StoreInstance.js&quot;;
export default class AppInput extends HTMLElement {
    constructor(){
        super();
        this.store = StoreInstance;
        this.store.events.subscribe('stateChange',this.renderList.bind(this));

        this.render();
        this.renderList();
    }

    render(){

        this.innerHTML = this.getTemplate();
    }

    getTemplate(){
        return `
            &amp;lt;h2 class=&quot;app__heading&quot;&amp;gt;What you've done&amp;lt;/h2&amp;gt;
            &amp;lt;div class=&quot;js-items&quot; aria-live=&quot;polite&quot; aria-label=&quot;A list of items you have done&quot;&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;form class=&quot;[ new-item ] [ boilerform ] [ js-form ]&quot;&amp;gt;
                &amp;lt;div class=&quot;boilerform&quot;&amp;gt;
                    &amp;lt;!-- Form styles from the https://boilerform.design boilerplate --&amp;gt;
                    &amp;lt;label for=&quot;new-item-field&quot; class=&quot;[ new-item__label ] [ c-label ]&quot;&amp;gt;Add a new item&amp;lt;/label&amp;gt;
                    &amp;lt;input type=&quot;text&quot; class=&quot;[ new-item__details ] [ c-input-field ]&quot; id=&quot;new-item-field&quot; autocomplete=&quot;off&quot; /&amp;gt;
                    &amp;lt;button class=&quot;[ c-button ] [ new-item__button ]&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/form&amp;gt;
        `;
    }

    connectedCallback(){
        let that = this;
        this.querySelector('form').addEventListener('submit',(e)=&amp;gt; {
            e.preventDefault();
            that.addItemWithStore(); //for state management
        });
    }
    
    addItemWithStore(){
        let item = this.querySelector('#new-item-field').value.trim();   
        StoreInstance.dispatch(&quot;ADD_ITEM&quot;, item);
    }

    renderList(){
        let that = this;
        const list = `
            &amp;lt;ul&amp;gt;
                ${this.store.state.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelector('.js-items').innerHTML = list;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.dispatchItemsWithStore(idx);
            });
        });
    }

    dispatchItemsWithStore(idx){
        this.store.dispatch(&quot;DELETE_ITEM&quot;,idx);
    }

    disconnectedCallback(){

    }

    static get observedAttributes(){
        return [];
    }

    attributeChangedCallback(name, oldValue, newValue){

    }
};

customElements.get('app-input')?? customElements.define('app-input',AppInput);&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;this.store의 events에 역시 subscribe하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 AppState.js와 다른 부분은 render를 callback으로 처리하지 않고 renderList를 별도로 만들어 줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 form부분까지 redering이 불필요하게 일어나기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점을 꼭 생각해 주자.&lt;/p&gt;
&lt;pre id=&quot;code_1629729365574&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    constructor(){
        super();
        this.store = StoreInstance;
        this.store.events.subscribe('stateChange',this.renderList.bind(this));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;renderList에서는 this.store.state.itmes를 역시 직접 연동해서 Data를 갖어온다.&lt;/p&gt;
&lt;pre id=&quot;code_1629729379006&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    renderList(){
        let that = this;
        const list = `
            &amp;lt;ul&amp;gt;
                ${this.store.state.items.map((item,idx)=&amp;gt; `&amp;lt;li&amp;gt; ${item} &amp;lt;button class=&quot;rm&quot; aria-label=&quot;Delete this item&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;`).join('')}
            &amp;lt;/ul&amp;gt;
        `;

        this.querySelector('.js-items').innerHTML = list;

        this.querySelectorAll('.rm').forEach((btn,idx)=&amp;gt;{
            btn.addEventListener('click',(e)=&amp;gt;{
                that.dispatchItemsWithStore(idx);
            });
        });
    }&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 서비스 -&amp;gt; Web Component화 -&amp;gt; StateManagement 기능 추가&lt;/li&gt;
&lt;/ul&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;하지만 이를 통해 꼭 기억해야 할 것은 아래 남겨둔다.&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;Component에서 Data를 관리하지 않는다. Store를 통해서 Data(state)를 주입받는다.&lt;/li&gt;
&lt;li&gt;Action은 component가 하지 않는다. 모든 Action은 Store에 위임한다.&lt;/li&gt;
&lt;li&gt;Action은 State자체를 수정하지 않는다. 수정하기 전에 필요한 행위를 할 뿐이다.&lt;/li&gt;
&lt;li&gt;Mutation을 통해 State의 값을 수정한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그러나 Mutation이 최종적인 Commit은 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Store를 통해서 state는 최종적으로 update된다.&lt;/li&gt;
&lt;li&gt;update된 state는 Mediator를 통해서 Subscribe하고 있는 Component들에게 변화 되었음을 알려준다.&lt;/li&gt;
&lt;li&gt;state의 변화를 받은 Component는 즉시 redering을 다시 처리한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단, 부분영역 rendering이 필요할 경우 주의 하자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;마지막으로 MutationObserver 등과 같은 Web Component기능에 대해서는 시간날때 더 추가해서 작성하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;branch 이름이 꼬였지만 위의 코드들이 있는 위치는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/theyoung/webcomponents/tree/webComOnly/statementforwebcomponent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/theyoung/webcomponents/tree/webComOnly/statementforwebcomponent&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에게는 도움이 되길...&lt;/p&gt;</description>
      <category>Web</category>
      <category>Redux</category>
      <category>State Management</category>
      <category>vanilla</category>
      <category>web component</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/231</guid>
      <comments>https://enumclass.tistory.com/231#entry231comment</comments>
      <pubDate>Sun, 22 Aug 2021 23:41:27 +0900</pubDate>
    </item>
    <item>
      <title>MVC, MVP, MVVM 패턴의 이해</title>
      <link>https://enumclass.tistory.com/230</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;MVVM 패턴이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM을 이해하기 위해서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC -&amp;gt; MVP -&amp;gt; MVVM 순서대로 아키텍처 패턴에 대한 이해가 필요하다.&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;MVC 패턴&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;2112&quot; width=&quot;307&quot; height=&quot;338&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQnr5J/btrcA1uoL4n/kdx6Cg5PkjbKb87D8cikb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQnr5J/btrcA1uoL4n/kdx6Cg5PkjbKb87D8cikb1/img.png&quot; data-alt=&quot;MVC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQnr5J/btrcA1uoL4n/kdx6Cg5PkjbKb87D8cikb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQnr5J%2FbtrcA1uoL4n%2Fkdx6Cg5PkjbKb87D8cikb1%2Fimg.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;2112&quot; width=&quot;307&quot; height=&quot;338&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;MVC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용되는 패턴으로 Spring MVC라는 Back-end framework이름이 있을 정도로 많이 사용된다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 controller(Action)를 통해서 Model을 변화시키면 View가 Update되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Business Logic이 Controller에 있을 수 있고 또한 View에도 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model에 있는게 사실 정석이지만 말이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Business Layer를 Model이라고 부른다. 하지만 spring MVC를 기준으로 Business Logic 자체가 개발되는 곳이 어디인가 보면 Practical하게 Controller(service) 영역으로 보는게 맞다고 생각한다.&lt;br /&gt;Model에 넣는게 맞는건 아는데.. 그게...&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;Controller를 Call하는것은 View일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽤 유연하다는 느낌을 받을 수 있다. 그래서 인지 가장 많이 사용되면서도 공격을 받는 포인트도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 대표적인게 view를 하나 만들면 controller도 만들어야하나? 수정되면 controller도 수정되나? Model 업데이트를 Controller가 아니어도 가능한가? 이런 종류이다. 그래서 나온 패턴이 MVP이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 사실 어떤게 어느 Layer냐? 이런것은 다소 철학적(?) 토론의 영역이 될 수 있음으로 깊게 들어가지 말자.&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;MVP(Presenter) 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVP에서는 Controller라는 단어가 사라졌다. 그렇다고 그 기능이 사라지진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Presenter의 영역은 Controller의 영역을 포함하고 여기에 plus&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;view의 Interface를 포함한다고 생각하면 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC에서는 Controller가 Model과 View를 선택하면 View가 해당 Model을 바탕으로 UI를 그렸다고 볼수 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 의미는 Controller가 View를 정확히 인지할 수 있다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View와 controller의 이 밀접한 관계를 끊고, 서로를 인지 할 수 없게 만드는 목표를 갖는 패턴이라고 생각하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;214&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HTd3D/btrcGreu45G/wJ549p5hWqxLNVV13XA3P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HTd3D/btrcGreu45G/wJ549p5hWqxLNVV13XA3P0/img.png&quot; data-alt=&quot;mvp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HTd3D/btrcGreu45G/wJ549p5hWqxLNVV13XA3P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHTd3D%2FbtrcGreu45G%2FwJ549p5hWqxLNVV13XA3P0%2Fimg.png&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;214&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;mvp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter&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;일단 Presenter에 Interface View를 만들어 놓으면, 다양한 View들은 해당 Interface view에 맞게 상속받아 각자의 View를 그림으로써 View와 Presenter간의 사이를 나누어 놓는 것이다.&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;a href=&quot;https://www.infragistics.com/community/blogs/b/todd_snyder/posts/mvc-or-mvp-pattern-whats-the-difference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.infragistics.com/community/blogs/b/todd_snyder/posts/mvc-or-mvp-pattern-whats-the-difference&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;MVVM(View Model) 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC와 MVP 두가지의 Data의 흐름으로 보면 아래와 같다고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;651&quot; height=&quot;191&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;764&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eJhzgP/btrcIVth1j3/opbTMJ8PjmVM37wKtn6Rw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eJhzgP/btrcIVth1j3/opbTMJ8PjmVM37wKtn6Rw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eJhzgP/btrcIVth1j3/opbTMJ8PjmVM37wKtn6Rw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeJhzgP%2FbtrcIVth1j3%2FopbTMJ8PjmVM37wKtn6Rw0%2Fimg.png&quot; width=&quot;651&quot; height=&quot;191&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;764&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;blockquote data-ke-style=&quot;style2&quot;&gt;여기에서 Repository가 Controller와 Model 사이에 있는 이유는 인터넷 상에 누군가는 repository를 controller 영역이라고 하고, 또 누구는 Model 영역이라고 해서 어중간하게 저 위치에 붙여 놓았다.&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;상기의 흐름은 어떤 event가 외부로 부터 들어오고 해당 event로 인해서 Model의 data를 갖어 온다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model의 입장에서 보자면&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1530&quot; width=&quot;397&quot; height=&quot;211&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHjfNG/btrcNlqTrf3/Qw8eqBG4xjKIGPm0AqVxgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHjfNG/btrcNlqTrf3/Qw8eqBG4xjKIGPm0AqVxgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHjfNG/btrcNlqTrf3/Qw8eqBG4xjKIGPm0AqVxgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHjfNG%2FbtrcNlqTrf3%2FQw8eqBG4xjKIGPm0AqVxgk%2Fimg.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;1530&quot; width=&quot;397&quot; height=&quot;211&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;갑분싸 &quot;슈뢰딩거의 고양이&quot; 느낌이다. 누군가 나를 읽어가기 전에는 나의 상태가 어떻게 변화되었는지 알수 없을껄!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model의 입장을 아래와 같이 한번 바꾸어 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;651&quot; height=&quot;191&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;764&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/02HkZ/btrcFV8PgW1/bQWVngUg9cXbeTIhmB4Sj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/02HkZ/btrcFV8PgW1/bQWVngUg9cXbeTIhmB4Sj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/02HkZ/btrcFV8PgW1/bQWVngUg9cXbeTIhmB4Sj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F02HkZ%2FbtrcFV8PgW1%2FbQWVngUg9cXbeTIhmB4Sj0%2Fimg.png&quot; width=&quot;651&quot; height=&quot;191&quot; data-origin-width=&quot;2604&quot; data-origin-height=&quot;764&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;Model이 변경됨으로써 Repository도 update되고 View도 업데이트 되고, 즉 데이터의 변화가 그와 관련된 서비스(UI 포함해서)의 상태 변화가 된다고 보는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런것을 Observer Pattern이라고 하는데, 이것을 중심으로 구현해 놓은 것을 MVVM이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드에서 공식 UI개발 아키텍처로써 MVVM은 Observer 역할을 하는 Livedata라고 하는 객체를 사용해서 하기의 아키텍처를 구현하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phhfe/btrcEKMZ7l5/Kk216JcmfvUKXKSY7aMjW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phhfe/btrcEKMZ7l5/Kk216JcmfvUKXKSY7aMjW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phhfe/btrcEKMZ7l5/Kk216JcmfvUKXKSY7aMjW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fphhfe%2FbtrcEKMZ7l5%2FKk216JcmfvUKXKSY7aMjW1%2Fimg.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&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;&lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Data Observing을 이용한 아키텍처 패턴을 만들기 위해서 나온 라이브러리는&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;RxJava : 안드로이드에서 많이 쓰임&lt;/li&gt;
&lt;li&gt;WebFlux : Backend에서 많이 쓰임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도가 되겠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;web에서는 ObservedAttributes 또는 MutationObserver를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reflection을 쓰면 decorator pattern을 통해서 다 구현 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 갑작이 디자인패턴을 알아본 이유는 Web component State management를 설명하기 위해서 인데, 시간 나면 아래 포스트도 읽어 보면 좋다.&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;003&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/003.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/003.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.16 - [Web] - 기존 web site를 components 로 다시 만들기 (No State management)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/231&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.08.22 - [Web] - Web Component 상태관리 만들기 (Vanillajs)&lt;/a&gt;&lt;/p&gt;</description>
      <category>Web</category>
      <category>mvc</category>
      <category>MVP</category>
      <category>MVVM</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/230</guid>
      <comments>https://enumclass.tistory.com/230#entry230comment</comments>
      <pubDate>Fri, 20 Aug 2021 17:24:31 +0900</pubDate>
    </item>
    <item>
      <title>Sparse Matrix Multiplication</title>
      <link>https://enumclass.tistory.com/229</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/sparse-matrix-multiplication/&quot;&gt;Sparse Matrix Multiplication&lt;/a&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;Matrix Dot(곱)을 연산 하시오&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;591&quot; height=&quot;309&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;1236&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vqkwg/btrci90nYbc/owddcK0ZnzVgpbZ9LydZgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vqkwg/btrci90nYbc/owddcK0ZnzVgpbZ9LydZgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vqkwg/btrci90nYbc/owddcK0ZnzVgpbZ9LydZgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvqkwg%2Fbtrci90nYbc%2FowddcK0ZnzVgpbZ9LydZgk%2Fimg.png&quot; width=&quot;591&quot; height=&quot;309&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;1236&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;그런데 의외로 이런 문제를 코드로 변환 시키려고 보면 머리가 멈춘다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A 배열을 arows * acols, B배열을 brows * bcols 라고 명명할 수 있다.&lt;/li&gt;
&lt;li&gt;acols와 brows의 크기가 같아야 곱셈이 가능하다&lt;/li&gt;
&lt;li&gt;결과 배열은 arows * bcols가 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 3가지를 이용해서 문제를 접근했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 loop를 어떻게 돌것이냐? 가 가장 중요한 첫번째 step이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A배열을 loop 시킬까? 아니면 B배열을 loop 시킬까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 Result 배열인 arow * bcols를 loop 시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 result[arow][bcols] 에 각 A의 Col과 B의 Row가 곱해서 Sum이 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for arows inc&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;for bcols inc&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;result[arows][bcols] = A[arow][??] * B[??][bcols]&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;위의 ?? 부분은 aclos와 brows의 길이가 같기때문에 해당 길이만큼 순환 시켜주면 목표한 result[arows][bcols]에 곱셈이 되어서 더해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어진 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1629211833609&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var multiply = function(mat1, mat2) {
    let result = new Array(mat1.length).fill(0).map(_=&amp;gt; new Array(mat2[0].length).fill(0));
    
    for(let arow = 0; arow &amp;lt; mat1.length; arow++){
        for(let bcol = 0; bcol &amp;lt; mat2[0].length; bcol++){
            for(let brow = 0; brow &amp;lt; mat2.length; brow++){
                result[arow][bcol] += mat1[arow][brow] * mat2[brow][bcol];
            }
        }
    }
    
    
    return result;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 위 코드가 최적화 코드가 아닌가 해서 다른 사람코드를 찾아봤는데, 이게 최적화 코드 맞았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Matrix연산은 Concurrent 개발을 통해서&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1629211920350&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            for(let brow = 0; brow &amp;lt; mat2.length; brow++){
                result[arow][bcol] += mat1[arow][brow] * mat2[brow][bcol];
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 동시 계산 가능하도록 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enumclass.tistory.com/169&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.05.31 - [JAVA] - Java의 동기화 및 Locking 기법 들 (Synchronized, ReentrantLock, Semaphore, Atomic Package, varHandle)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 참고하면 좋다.&lt;/p&gt;</description>
      <category>Problem Solving</category>
      <category>Matrix Multiplication</category>
      <category>Matrix곱</category>
      <category>동시성제어</category>
      <author>enumclass</author>
      <guid isPermaLink="true">https://enumclass.tistory.com/229</guid>
      <comments>https://enumclass.tistory.com/229#entry229comment</comments>
      <pubDate>Tue, 17 Aug 2021 23:53:49 +0900</pubDate>
    </item>
  </channel>
</rss>