<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>천천히 꾸준히 성장하기</title>
    <link>https://worldi.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 22 Aug 2026 17:14:26 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>worldi</managingEditor>
    <image>
      <title>천천히 꾸준히 성장하기</title>
      <url>https://tistory1.daumcdn.net/tistory/5043228/attach/6ee702614a864b7ba1e8405a1b8229f6</url>
      <link>https://worldi.tistory.com</link>
    </image>
    <item>
      <title>이번 5월달에 읽을 책!</title>
      <link>https://worldi.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;5월&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 엘라스틱 서치 실무 가이드&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이벤트 소싱과 MSA&amp;nbsp;&lt;/p&gt;</description>
      <category>책 일지</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/70</guid>
      <comments>https://worldi.tistory.com/70#entry70comment</comments>
      <pubDate>Tue, 6 May 2025 18:14:28 +0900</pubDate>
    </item>
    <item>
      <title>분산 ID 생성기</title>
      <link>https://worldi.tistory.com/67</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;1841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIzMxu/btsNAR2MSNd/EPf2AXhzKLsXIGi7EkCtdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIzMxu/btsNAR2MSNd/EPf2AXhzKLsXIGi7EkCtdk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIzMxu/btsNAR2MSNd/EPf2AXhzKLsXIGi7EkCtdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIzMxu%2FbtsNAR2MSNd%2FEPf2AXhzKLsXIGi7EkCtdk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1308&quot; height=&quot;1841&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;1841&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/67</guid>
      <comments>https://worldi.tistory.com/67#entry67comment</comments>
      <pubDate>Sun, 27 Apr 2025 16:20:33 +0900</pubDate>
    </item>
    <item>
      <title>안정해쉬</title>
      <link>https://worldi.tistory.com/66</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdGDRx/btsNCyAERSd/gTKoFb6Qx1tDRIFtHX6kv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdGDRx/btsNCyAERSd/gTKoFb6Qx1tDRIFtHX6kv1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdGDRx/btsNCyAERSd/gTKoFb6Qx1tDRIFtHX6kv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdGDRx%2FbtsNCyAERSd%2FgTKoFb6Qx1tDRIFtHX6kv1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1200&quot; height=&quot;1687&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1687&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/66</guid>
      <comments>https://worldi.tistory.com/66#entry66comment</comments>
      <pubDate>Sun, 27 Apr 2025 16:04:54 +0900</pubDate>
    </item>
    <item>
      <title>키값 저장소 설계</title>
      <link>https://worldi.tistory.com/62</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;2266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYfTp9/btsNdzHvhOZ/kOM3Rt2kFP1tKKy2hqhB2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYfTp9/btsNdzHvhOZ/kOM3Rt2kFP1tKKy2hqhB2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYfTp9/btsNdzHvhOZ/kOM3Rt2kFP1tKKy2hqhB2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYfTp9%2FbtsNdzHvhOZ%2FkOM3Rt2kFP1tKKy2hqhB2k%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;1488&quot; height=&quot;2266&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;2266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>하루하루 성장일기</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/62</guid>
      <comments>https://worldi.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 9 Apr 2025 03:38:19 +0900</pubDate>
    </item>
    <item>
      <title>2025.04.08</title>
      <link>https://worldi.tistory.com/61</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;1773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ge3Ly/btsNeTLMeXK/YzrQhiUHBP3JePkmULN9z1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ge3Ly/btsNeTLMeXK/YzrQhiUHBP3JePkmULN9z1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ge3Ly/btsNeTLMeXK/YzrQhiUHBP3JePkmULN9z1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGe3Ly%2FbtsNeTLMeXK%2FYzrQhiUHBP3JePkmULN9z1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1253&quot; height=&quot;1773&quot; data-origin-width=&quot;1253&quot; data-origin-height=&quot;1773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>하루하루 성장일기</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/61</guid>
      <comments>https://worldi.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 9 Apr 2025 02:00:29 +0900</pubDate>
    </item>
    <item>
      <title>2025.04.07</title>
      <link>https://worldi.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;배포 전략: Rolling, Blue/Green, Canary&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;3076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lFQAM/btsNcoMHlug/J3BFzkUIggzSeDu4dwpfxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lFQAM/btsNcoMHlug/J3BFzkUIggzSeDu4dwpfxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lFQAM/btsNcoMHlug/J3BFzkUIggzSeDu4dwpfxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlFQAM%2FbtsNcoMHlug%2FJ3BFzkUIggzSeDu4dwpfxk%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;1110&quot; height=&quot;3076&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;3076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>하루하루 성장일기</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/60</guid>
      <comments>https://worldi.tistory.com/60#entry60comment</comments>
      <pubDate>Mon, 7 Apr 2025 23:41:44 +0900</pubDate>
    </item>
    <item>
      <title>EC2 인스턴스 스토리지</title>
      <link>https://worldi.tistory.com/59</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;EBS Volume&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;- 한번에 두개의 인스턴스에 연결할 수 없다. 한 인스턴스에 두개의 ebs를 연결할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EC2 인스턴스에 쉽게 detached 할수도, 다른 인스턴스에 쉽게 부착할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AZ 가용영역에 국한되어 있다.&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; 기본설정으로는 루트 볼륨에 설정되어 있다. 다른 볼륨은 비활성화 되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EBS 스냅샷&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EBS 볼륨의 backup을 만들어 놓는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복원하는데 24시간-72시간이 걸린다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 휴지통에서 복원하는 시간은 1일 - 1 year 로 설정할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMI&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EC2 인스턴스의 customization을 할 수 있다.&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;EC2 Instance Store&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Better I/O performance&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EBS 볼륨 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- gp2 / gp3&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- io 1 / io2&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- st1 ,sc1&amp;nbsp; (cannot be a boot volume)&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;다중 연결&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 같은 AZ 에서 여러개의 EC2에 붙을 수 있는 것을 말함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- io1, io2 만 가능함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 최대 16개 인스턴스에 한해서 가능함.&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;Amazon EFs&amp;nbsp;&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;- 여러 Ec2에 붙을 수 있다. (다른 AZ에 대해서도)&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;- Linux 인스턴스에서만 사용 가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EBS에 비해 비용이 3배 가량 비싸다. &amp;rArr; 절약하고 싶을 땐 스토리지 티어로 EFS-IA 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EFS는 사용한 용량 만큼만 비용이 청구 &amp;rArr; EBS는 드라이브 크기에 따라 비용 청구&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://maker5587.tistory.com/73&quot;&gt;https://maker5587.tistory.com/73&lt;/a&gt; [Maker's security:티스토리]&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>개발 관련 일지/AWS Certified Developer - Associate</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/59</guid>
      <comments>https://worldi.tistory.com/59#entry59comment</comments>
      <pubDate>Mon, 6 Jan 2025 01:49:10 +0900</pubDate>
    </item>
    <item>
      <title>[백준 3372] 보드 점프</title>
      <link>https://worldi.tistory.com/58</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DFS&lt;/li&gt;
&lt;li&gt;DP&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;풀이 방법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해멧던 부분 :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DFS 로 했다가 메모리 초과가 남.&lt;/li&gt;
&lt;li&gt;메모리 초과를 &amp;rarr; dp로 바꿈.&lt;/li&gt;
&lt;li&gt;하향식에 실패해서 상향식으로 바꿈.&lt;/li&gt;
&lt;li&gt;2^63보다 큰 숫자는 long으로 해결이 안됨. bigInteger를 써야 함.&lt;/li&gt;
&lt;li&gt;BigIntger 를 처음에 0으로 초기화 해줬는데 메모리 초과가 남.&lt;/li&gt;
&lt;li&gt;BigInteger 배열은 처음에 Null로 초기화 되어 있음. 따라서 이때 null이 아닐 경우 바로 계산된 값 반환해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&amp;nbsp;&lt;/h2&gt;
&lt;pre id=&quot;code_1736069597557&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.StringTokenizer;
public class Main {
    public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static StringTokenizer st = null;
    public static int[][] arr;
    public static BigInteger[][] dp;
    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(br.readLine());
        arr = new int[n][n];
        dp = new BigInteger[n][n];
        for (int i = 0; i &amp;lt; n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j &amp;lt; n; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        System.out.println(dfs(0, 0, n));
    }
    private static BigInteger dfs(int x, int y, int n) {
        if (x == n - 1 &amp;amp;&amp;amp; y == n - 1) return BigInteger.ONE;
        if (dp[x][y] != null) return dp[x][y]; // 이미 계산된 값 반환
        dp[x][y] = BigInteger.ZERO; // 초기값 설정
        for (int i = 0; i &amp;lt; 2; i++) {
            int nextX = x + dx[i] * arr[x][y];
            int nextY = y + dy[i] * arr[x][y];
            if (nextX &amp;gt;= 0 &amp;amp;&amp;amp; nextX &amp;lt; n &amp;amp;&amp;amp; nextY &amp;gt;= 0 &amp;amp;&amp;amp; nextY &amp;lt; n) {
                dp[x][y] = dp[x][y].add(dfs(nextX, nextY, n));
            }
        }
        return dp[x][y];
    }
    public static int dx[] = {1, 0};
    public static int dy[] = {0, 1};
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/58</guid>
      <comments>https://worldi.tistory.com/58#entry58comment</comments>
      <pubDate>Sun, 5 Jan 2025 18:33:22 +0900</pubDate>
    </item>
    <item>
      <title>[백준 11559번] JAVA Puyo Puyo</title>
      <link>https://worldi.tistory.com/57</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;접근 방식&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제는 구현(implementation) 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 요구사항은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 4개 이상 '.'이 아니고, 인접한 문자가 같을 때 연쇄적으로 터지고, 이를 '.' 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 연쇄적으로 터진 이후, 배열이 재배열 된다. (문자가 있을 경우, 맨 아래까지 내려간다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 연쇄적인 것들은, 여러개가 터져도 하나로 기록한다.&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;코드&amp;nbsp;&lt;/h2&gt;
&lt;pre id=&quot;code_1736069243818&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.StringTokenizer;

public class Main {
    public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static StringTokenizer st = null;

    public static class Puyo {
        public char[][] arr;
        public boolean process = false;

        Puyo(char[][] arr) {
            this.arr = arr;
        }

        public void move() {
            boolean[][] visited = new boolean[12][6];
            move(visited);
        }

        public void draw() {
            for (int i = 0; i &amp;lt; 6; i++) {
                Queue&amp;lt;Character&amp;gt; st = new LinkedList&amp;lt;&amp;gt;();
                for (int j = 11; j &amp;gt;= 0; j--) {
                    if (arr[j][i] != '.') {
                        st.add(arr[j][i]);
                        arr[j][i] = '.';
                    }
                }
                int cur = 11;
                while (!st.isEmpty()) {
                    arr[cur][i] = st.poll();
                    cur--;
                }
            }
        }

        private void move(boolean[][] visited) {
            process = false;
            for (int i = 0; i &amp;lt; 12; i++) {
                for (int j = 0; j &amp;lt; 6; j++) {
                    if (!visited[i][j] &amp;amp;&amp;amp; arr[i][j] != '.') {
                        bfs(i, j, visited);
                    }
                }
            }
        }

        private void bfs(int x, int y, boolean[][] visited) {
            visited[x][y] = true;

            int cnt = 1;
            Queue&amp;lt;int[]&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
            q.add(new int[]{x, y});
            Queue&amp;lt;int[]&amp;gt; draw = new LinkedList&amp;lt;&amp;gt;();
            draw.add(new int[]{x, y});

            while (!q.isEmpty()) {
                int[] temp = q.poll();
                for (int i = 0; i &amp;lt; 4; i++) {
                    int nextX = dx[i] + temp[0];
                    int nextY = dy[i] + temp[1];
                    if (0 &amp;lt;= nextY &amp;amp;&amp;amp; nextY &amp;lt; 6 &amp;amp;&amp;amp; 0 &amp;lt;= nextX &amp;amp;&amp;amp; nextX &amp;lt; 12) {
                        if (!visited[nextX][nextY] &amp;amp;&amp;amp; arr[x][y] == arr[nextX][nextY]) {
                            cnt++;
                            visited[nextX][nextY]= true;
                            q.add(new int[]{nextX, nextY});
                            draw.add(new int[]{nextX, nextY});
                        }
                    }
                }
            }
            if (cnt &amp;gt;= 4) {
                process = true;
                while (!draw.isEmpty()) {
                    int[] temp = draw.poll();
                    arr[temp[0]][temp[1]] = '.';
                }
            }
        }

        public boolean process() {
            return process;
        }
    }

    public static void main(String[] args) throws IOException {

        /*
        * 총 12개의 줄에 필드의 정보가 주어지며, 각 줄에는 6개의 문자가 있다.
        이때 .은 빈공간이고 .이 아닌것은 각각의 색깔의 뿌요를 나타낸다.
        R은 빨강, G는 초록, B는 파랑, P는 보라, Y는 노랑이다.
        입력으로 주어지는 필드는 뿌요들이 전부 아래로 떨어진 뒤의 상태이다. 즉, 뿌요 아래에 빈 칸이 있는 경우는 없다.
        * */
        char[][] arr = new char[12][6];
        for (int i = 0; i &amp;lt; 12; i++) {
            String s = br.readLine();
            for (int j = 0; j &amp;lt; 6; j++) {
                arr[i][j] = s.charAt(j);
            }
        }

        int sum = 0;
        Puyo puyo = new Puyo(arr);
        while (true) {
            puyo.move();
            if (!puyo.process()) break;
            sum++;
            puyo.draw();
        }
        System.out.println(sum);
    }

    public static int[] dx = {-1, 1, 0, 0};
    public static int[] dy = {0, 0, -1, 1};
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/57</guid>
      <comments>https://worldi.tistory.com/57#entry57comment</comments>
      <pubDate>Sun, 5 Jan 2025 18:30:29 +0900</pubDate>
    </item>
    <item>
      <title>부산 3박 4일 여행 기록기</title>
      <link>https://worldi.tistory.com/55</link>
      <description>&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;앞으로 블로그에다가 배웠던 것과 공부하는 것, 일상들을 담으면서 글쓰기 실력도 키워볼 생각이다.&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;이번 12월 달에는 여행을 갔다왔는데 24일 부터 27일까지 남자친구랑 부산 여행을 갔다왔다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 수서역에서 SRT를 탔어야 했는데, 짐을 아침에 부리나케 싸느라 약간 늦을 뻔 했다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1일 차에는 도착하자마자 부산 밀면을 먹었다.&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;p data-ke-size=&quot;size16&quot;&gt;근데 그거보다도 만두가 완전 맛있었다! 호박인가 양파인가 아삭아삭한 느낌이 너무 맛있었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_3654.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9ni1y/btsLEOeQiVz/SIIFyiG2fmoKbJXkgbhKgk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9ni1y/btsLEOeQiVz/SIIFyiG2fmoKbJXkgbhKgk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9ni1y/btsLEOeQiVz/SIIFyiG2fmoKbJXkgbhKgk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9ni1y%2FbtsLEOeQiVz%2FSIIFyiG2fmoKbJXkgbhKgk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;466&quot; height=&quot;621&quot; data-filename=&quot;IMG_3654.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;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;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYUCSs/btsLDv8o0py/wtKKErkiFZoPp8cloZTna1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYUCSs/btsLDv8o0py/wtKKErkiFZoPp8cloZTna1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_3659.jpg&quot; width=&quot;522&quot; height=&quot;696&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYUCSs/btsLDv8o0py/wtKKErkiFZoPp8cloZTna1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYUCSs%2FbtsLDv8o0py%2FwtKKErkiFZoPp8cloZTna1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WvXaz/btsLEFPJQGw/HcfRFeJ3Q4Deu1xKdffFOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WvXaz/btsLEFPJQGw/HcfRFeJ3Q4Deu1xKdffFOK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_3660.jpg&quot; width=&quot;467&quot; height=&quot;623&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WvXaz/btsLEFPJQGw/HcfRFeJ3Q4Deu1xKdffFOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWvXaz%2FbtsLEFPJQGw%2FHcfRFeJ3Q4Deu1xKdffFOK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 노을이 져서 사진도 찍었다. 오션뷰가 너무 이뻤다. 호텔에서 오션뷰가 딱 보여서 그런지 뷰만 바라봐도 기분이 너무 좋아지는 느낌..?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_3684.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYCfb8/btsLEF94iag/gjstTGomIMF4X7fnAXJ7h1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYCfb8/btsLEF94iag/gjstTGomIMF4X7fnAXJ7h1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYCfb8/btsLEF94iag/gjstTGomIMF4X7fnAXJ7h1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYCfb8%2FbtsLEF94iag%2FgjstTGomIMF4X7fnAXJ7h1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;336&quot; height=&quot;448&quot; data-filename=&quot;IMG_3684.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호텔 라운지에서 5시부터인가 칵테일을 줘서 먹으러 갔다. 칵테일이 너무 맛있어서 한 3잔 먹었는데 좀 취했다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진도 찍었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7XGIN/btsLD2koeKM/9osDFNlyovXo0hob0KhuQ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7XGIN/btsLD2koeKM/9osDFNlyovXo0hob0KhuQ0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-01-05-17-20-56 001.jpeg&quot; width=&quot;411&quot; height=&quot;548&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7XGIN/btsLD2koeKM/9osDFNlyovXo0hob0KhuQ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7XGIN%2FbtsLD2koeKM%2F9osDFNlyovXo0hob0KhuQ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/64Xxd/btsLCY4f6JP/k3QQUV1IVRRN8fwqlk0gR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/64Xxd/btsLCY4f6JP/k3QQUV1IVRRN8fwqlk0gR0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_3855.jpg&quot; width=&quot;432&quot; height=&quot;576&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/64Xxd/btsLCY4f6JP/k3QQUV1IVRRN8fwqlk0gR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F64Xxd%2FbtsLCY4f6JP%2Fk3QQUV1IVRRN8fwqlk0gR0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;부산오면 회를 너무 먹고 싶었는데, 근처에 회를 먹을 곳을 찾다가 24일이라 그런지 웨이팅이 너무 많았다. ㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호텔가서 룸서비스와 포장해서 먹을 까 아니면 간단하게 먹고 들어가서 먹을까 생각을 하다가 그냥 먹고 들어가자는 생각을 했고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 아무계획 없이 아무 식당이나 들어갔는데, 정말 맛이 없었다. 모둠회를 시켜서 방어와 광어가 나왔는데 서울에서 먹는 광어가 더 맛있다는 생각을 했고, 방어는 거의 못먹을 정도였다. 방어가 아닌 거 같기도 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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-filename=&quot;IMG_3888.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BObWe/btsLEje7EYK/BWBAo43IjJ97gcWNw8MVMK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BObWe/btsLEje7EYK/BWBAo43IjJ97gcWNw8MVMK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BObWe/btsLEje7EYK/BWBAo43IjJ97gcWNw8MVMK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBObWe%2FbtsLEje7EYK%2FBWBAo43IjJ97gcWNw8MVMK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;543&quot; data-filename=&quot;IMG_3888.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약간 삼삼하긴 했는데, 해물이 많이 들어가서 시원하고 맛있었다!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6Uyy0/btsLDvUSeHE/N1dz5F0qc0qZXT4NuIDY7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6Uyy0/btsLDvUSeHE/N1dz5F0qc0qZXT4NuIDY7K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-01-05-17-22-13 003.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6Uyy0/btsLDvUSeHE/N1dz5F0qc0qZXT4NuIDY7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6Uyy0%2FbtsLDvUSeHE%2FN1dz5F0qc0qZXT4NuIDY7K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br3nrU/btsLDHHuoSa/N35JKC42ZGrStLw0n798L1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br3nrU/btsLDHHuoSa/N35JKC42ZGrStLw0n798L1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2025-01-05-17-22-15 004.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br3nrU/btsLDHHuoSa/N35JKC42ZGrStLw0n798L1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr3nrU%2FbtsLDHHuoSa%2FN35JKC42ZGrStLw0n798L1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;흔들려서 약간 찌부된 케익 -ㅡ-&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먹고서 넘 배불러서, 케이크를 몇 입 못먹었는데 케이크를 놓쳐서 바닥에 놓쳤다 ㅜㅜ&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2일차에는 점심부터 먹으러 갔는데, 역시 웨이팅이 장난이 아니었다. 기장에서 양식하는 곳이 있길래 오픈하자마자 캐치테이블로 대기 걸어놓고 기장으로 향했다.&amp;nbsp; 돈까스하고 후토마끼는 그저그랬는데&amp;nbsp; 전복 크림 리조또가 제일 맛있었다! 그리고 갓김치를 반찬으로 주셨는데 그것도 맛있어서 계속 주워 먹었다... 느끼했는데 젤 맛있는 느낌?&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;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqywpP/btsLDfxWQOv/8kNqLT3YHvy3sQ1KAUj21K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqywpP/btsLDfxWQOv/8kNqLT3YHvy3sQ1KAUj21K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4050.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqywpP/btsLDfxWQOv/8kNqLT3YHvy3sQ1KAUj21K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqywpP%2FbtsLDfxWQOv%2F8kNqLT3YHvy3sQ1KAUj21K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btRNcQ/btsLCIG5PiY/8ekKDoDg2LcWlppkM39kZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btRNcQ/btsLCIG5PiY/8ekKDoDg2LcWlppkM39kZK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4051.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btRNcQ/btsLCIG5PiY/8ekKDoDg2LcWlppkM39kZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtRNcQ%2FbtsLCIG5PiY%2F8ekKDoDg2LcWlppkM39kZK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jm9Qc/btsLEpGkV4n/xOgHAfqyoux27v4K3pNtt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jm9Qc/btsLEpGkV4n/xOgHAfqyoux27v4K3pNtt1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4054.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jm9Qc/btsLEpGkV4n/xOgHAfqyoux27v4K3pNtt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJm9Qc%2FbtsLEpGkV4n%2FxOgHAfqyoux27v4K3pNtt1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chZamh/btsLDZ2daHz/q05KNr8l1oMnS5bvKVPN8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chZamh/btsLDZ2daHz/q05KNr8l1oMnS5bvKVPN8k/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4055.jpg&quot; style=&quot;width: 49.4186%; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chZamh/btsLDZ2daHz/q05KNr8l1oMnS5bvKVPN8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchZamh%2FbtsLDZ2daHz%2Fq05KNr8l1oMnS5bvKVPN8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;2-3일은 파크 하얏트라는 호텔에 묵었는데, 그것도 처음 들어보는 호텔이었는데 너무 너무 좋았다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 뷰가 너무너무 이뻤음..! 그래서 한 사진을 300장 찍은거 같은데 찍어준 남자친구한테 고맙따&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGMXeN/btsLCti4J6p/LR8PpLnZyUBofx89TSSt3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGMXeN/btsLCti4J6p/LR8PpLnZyUBofx89TSSt3K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4435.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGMXeN/btsLCti4J6p/LR8PpLnZyUBofx89TSSt3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGMXeN%2FbtsLCti4J6p%2FLR8PpLnZyUBofx89TSSt3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpUxpX/btsLE3ivg6t/jmsDIL5T6MWPHKdTO3DT7k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpUxpX/btsLE3ivg6t/jmsDIL5T6MWPHKdTO3DT7k/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4439.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpUxpX/btsLE3ivg6t/jmsDIL5T6MWPHKdTO3DT7k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpUxpX%2FbtsLE3ivg6t%2FjmsDIL5T6MWPHKdTO3DT7k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노을을 보다보니 저녁먹을 시간이 되어서 머먹을 까 하다가 근처에 조개 구이를 먹으러 갔다. 역시 조개구이는 정말 맛있다. 그리고 뭔가 소스가 스파게티 소스하고 사워 소스였는데 맛있었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2025-01-05-17-30-08.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tu3Wl/btsLDvm0FRO/7KMwxo23Fbhclq3B8LGcZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tu3Wl/btsLDvm0FRO/7KMwxo23Fbhclq3B8LGcZK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tu3Wl/btsLDvm0FRO/7KMwxo23Fbhclq3B8LGcZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTu3Wl%2FbtsLDvm0FRO%2F7KMwxo23Fbhclq3B8LGcZK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;4032&quot; height=&quot;3024&quot; data-filename=&quot;KakaoTalk_Photo_2025-01-05-17-30-08.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;p data-ke-size=&quot;size16&quot;&gt;그리고 탄수화물을 너무 안먹어서 김치볶음밥도 시켜먹었는데, 스팸볶음밥이었다. 흠.. 김치가 반찬으로 주어졌는데 좀 많이 맛이없었다. 이름을 스팸볶음밥으로 바꿔도 좋을듯 하다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_4509.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNfdmf/btsLCLcMX7v/MvsiPP1C8FkD8R0dVMm2vK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNfdmf/btsLCLcMX7v/MvsiPP1C8FkD8R0dVMm2vK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNfdmf/btsLCLcMX7v/MvsiPP1C8FkD8R0dVMm2vK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNfdmf%2FbtsLCLcMX7v%2FMvsiPP1C8FkD8R0dVMm2vK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;667&quot; data-filename=&quot;IMG_4509.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;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-filename=&quot;IMG_4519.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z9Ups/btsLEcG2nQW/qxlUt6RK4dPHOWc4TsUZkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z9Ups/btsLEcG2nQW/qxlUt6RK4dPHOWc4TsUZkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z9Ups/btsLEcG2nQW/qxlUt6RK4dPHOWc4TsUZkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ9Ups%2FbtsLEcG2nQW%2FqxlUt6RK4dPHOWc4TsUZkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;610&quot; height=&quot;813&quot; data-filename=&quot;IMG_4519.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오전이라 그런지 사람이 많지 않았고, 여유로웠다. 여기가 포토존이라고해서 사람이 없을 때까지 대기하다가 사진을 찍었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰가 너무 이쁘게 나오기도 하고 오빠가 너무 잘 찍어줬다!!!! 사진 보고서 너무 행복했다 ㅎㅎ&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R8Svn/btsLEM2oXK1/ROp1DJcZr15WsMoJIgOxt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R8Svn/btsLEM2oXK1/ROp1DJcZr15WsMoJIgOxt1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4731.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R8Svn/btsLEM2oXK1/ROp1DJcZr15WsMoJIgOxt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR8Svn%2FbtsLEM2oXK1%2FROp1DJcZr15WsMoJIgOxt1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPpqTU/btsLCs5mjvK/OqKXz3XDENvPWfVhwUct0k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPpqTU/btsLCs5mjvK/OqKXz3XDENvPWfVhwUct0k/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4768.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPpqTU/btsLCs5mjvK/OqKXz3XDENvPWfVhwUct0k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPpqTU%2FbtsLCs5mjvK%2FOqKXz3XDENvPWfVhwUct0k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&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;수영을 마치고 나서는 옷을 사러 갔다. 패딩이 너무 낡았어가지고 예쁜 패딩을 장만하고 싶었는데 근처에 20-30분 거리에 아울렛 매장이 있어서 들렸다 결국 충동구매아닌 충동 구매를 했다 ㅎㅎ&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_4875.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCuGNj/btsLDM27Q2p/TBEOX3OukmO7KSBmIZwn3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCuGNj/btsLDM27Q2p/TBEOX3OukmO7KSBmIZwn3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCuGNj/btsLDM27Q2p/TBEOX3OukmO7KSBmIZwn3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCuGNj%2FbtsLDM27Q2p%2FTBEOX3OukmO7KSBmIZwn3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;651&quot; data-filename=&quot;IMG_4875.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 너무 성공적이었고 묵은지 김치가 너무 맛있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차를 어디가야하지 하다가 어제 웨이팅때문에 못갔던 '문화' 이자카야가 생각이 났는데 해당 식당이 당일에도 웨이팅이 있었다 ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 중간에 주점에서 시간을 보내다 가야 겠다고 생각이 들었고 하이볼 + 맥주 폭탄주를 먹고 있다가, 30분만에 전화가 와서 급히 부랴부랴 그 옆 식당으로 이동했다.&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;p data-ke-size=&quot;size16&quot;&gt;부산 가면 꼭 다시 오고 싶은 식당이랄까 감히 이돈 주고 먹어도 되는 맛이었고 하나하나가 다 감동이었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB6Yi7/btsLEJYWr3T/kKdgfUDfvD4314vh25dsJ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB6Yi7/btsLEJYWr3T/kKdgfUDfvD4314vh25dsJ0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4881.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB6Yi7/btsLEJYWr3T/kKdgfUDfvD4314vh25dsJ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB6Yi7%2FbtsLEJYWr3T%2FkKdgfUDfvD4314vh25dsJ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd0F1m/btsLEIMr4ID/Z6JotjSJzSL4ksyY1CkYek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd0F1m/btsLEIMr4ID/Z6JotjSJzSL4ksyY1CkYek/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4882.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd0F1m/btsLEIMr4ID/Z6JotjSJzSL4ksyY1CkYek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd0F1m%2FbtsLEIMr4ID%2FZ6JotjSJzSL4ksyY1CkYek%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HIP3T/btsLEH08zCV/mSaZGfIgQ9LnQQjvgdaxvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HIP3T/btsLEH08zCV/mSaZGfIgQ9LnQQjvgdaxvK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4887.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HIP3T/btsLEH08zCV/mSaZGfIgQ9LnQQjvgdaxvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHIP3T%2FbtsLEH08zCV%2FmSaZGfIgQ9LnQQjvgdaxvK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biHME0/btsLEM2oY2Y/KEkIGUoQYnCKOQkKtmuOS0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biHME0/btsLEM2oY2Y/KEkIGUoQYnCKOQkKtmuOS0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_4888.jpg&quot; style=&quot;width: 49.4186%; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biHME0/btsLEM2oY2Y/KEkIGUoQYnCKOQkKtmuOS0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiHME0%2FbtsLEM2oY2Y%2FKEkIGUoQYnCKOQkKtmuOS0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부산 여행의 마지막을 기리기 위해 어떤 식당이 좋을까 고민했었는데, 마지막을 너무 행복하게 보낸거 같아 기분이 좋았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 옆에 앉았던 손님도 서울에서 혼자 오신 분이었는데 음식이 너무 맛있어서 행복해 하시는 것을 보고 나도 기분이 좋아졌다.&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;크리스 마스 때 개인적으로 21살 때를 마지막으로 행복했던 기억이 많지 않았다. 이런저런 이유로 바빴던 거 같기도 하고, 대충 넘겼던 기억이 많다.&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;p data-ke-size=&quot;size16&quot;&gt;감사함을 당연하게 생각하지말고, 보답할 수 있는 사람이 되어야 겠다고 생각했다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>여행/국내</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/55</guid>
      <comments>https://worldi.tistory.com/55#entry55comment</comments>
      <pubDate>Sun, 5 Jan 2025 17:47:16 +0900</pubDate>
    </item>
    <item>
      <title>EC2 - 배치 그룹</title>
      <link>https://worldi.tistory.com/54</link>
      <description>&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;250&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OB9xS/btsLDvl7UxI/Dok3eYTzkD1XNW56tInMw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OB9xS/btsLDvl7UxI/Dok3eYTzkD1XNW56tInMw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OB9xS/btsLDvl7UxI/Dok3eYTzkD1XNW56tInMw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOB9xS%2FbtsLDvl7UxI%2FDok3eYTzkD1XNW56tInMw1%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;250&quot; height=&quot;227&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;227&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;분산&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TJfsJ/btsLDgisFAP/nLoftWEaNTqBFYUrjQwIi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TJfsJ/btsLDgisFAP/nLoftWEaNTqBFYUrjQwIi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TJfsJ/btsLDgisFAP/nLoftWEaNTqBFYUrjQwIi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTJfsJ%2FbtsLDgisFAP%2FnLoftWEaNTqBFYUrjQwIi1%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;750&quot; height=&quot;230&quot; data-origin-width=&quot;750&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;데이터 소실 및 서비스 실패와 같은 위험을 감소 시키기 위해, 다수의 AZ를 사용하고,&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;파티션&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK4xFZ/btsLCJd3Jg1/Hmmp1a8yflkVLTvGKvQTvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK4xFZ/btsLCJd3Jg1/Hmmp1a8yflkVLTvGKvQTvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK4xFZ/btsLCJd3Jg1/Hmmp1a8yflkVLTvGKvQTvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK4xFZ%2FbtsLCJd3Jg1%2FHmmp1a8yflkVLTvGKvQTvK%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;400&quot; height=&quot;240&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;240&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지/AWS Certified Developer - Associate</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/54</guid>
      <comments>https://worldi.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 3 Jan 2025 00:38:20 +0900</pubDate>
    </item>
    <item>
      <title>EC2 기초</title>
      <link>https://worldi.tistory.com/53</link>
      <description>&lt;div style=&quot;color: #2d2f31; text-align: start;&quot;&gt;
&lt;div id=&quot;question-prompt&quot; data-purpose=&quot;safely-set-inner-html:rich-text-viewer:html&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온프레미스 애플리케이션을 AWS로 이전하려 합니다. 여러분의 기업에는 애플리케이션을 전용 서버에서 실행해야 한다는 엄격한 규정이 있습니다. 또한 비용 절감을 위해 전용 서버 바운드 소프트웨어 라이선스를 사용해야 합니다. 이 경우, 다음 중 어떤 EC2 구매 옵션이 적합할까요?&lt;/p&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-purpose=&quot;safely-set-inner-html:rich-text-viewer:html&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전환 가능 예약 인스턴스 컨버터블 예약 인스턴스&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-purpose=&quot;safely-set-inner-html:rich-text-viewer:html&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전용 호스트&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div data-purpose=&quot;safely-set-inner-html:rich-text-viewer:html&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스팟 인스턴스&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/njz7i/btsLB2L9VuU/rbh2NzVO1KURkdD0KWyRqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/njz7i/btsLB2L9VuU/rbh2NzVO1KURkdD0KWyRqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/njz7i/btsLB2L9VuU/rbh2NzVO1KURkdD0KWyRqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnjz7i%2FbtsLB2L9VuU%2Frbh2NzVO1KURkdD0KWyRqk%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;773&quot; height=&quot;386&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전용 호스트 같은 경우, Ec2 인스턴스 용량을 사용자 전용으로 제공하는 물리적 서버이다. 다른 AWS 고객과 물리적으로 격리 된다.&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;전용 인스턴스 같은 경우, 단일 고객 전용 하드웨어의 VPC에서 실행되는 인스턴스 이다. 다른 AWS 고객과 격리된 환경에서 처리 가능하지만, 특정 하드웨어 바인딩 라이센스 요구사항은 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스팟 인스턴스는 입찰 받는 형식으로, 시세 보다 저렴하게 이용할 수 있지만 시세 보다 높아질 경우 2분간 유예기간을 주고 인스턴스 제공을 중단한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약 인스턴스 같은 경우, 장기간 사용할 경우, 1년 또는 3년 형식으로 예약 할 수 있으며 미리 구매하고 사용하는 과금 유형이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지/AWS Certified Developer - Associate</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/53</guid>
      <comments>https://worldi.tistory.com/53#entry53comment</comments>
      <pubDate>Thu, 2 Jan 2025 23:32:22 +0900</pubDate>
    </item>
    <item>
      <title>1. 도메인 주도 설계</title>
      <link>https://worldi.tistory.com/52</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;도메인 모델이란?&amp;nbsp;&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 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;&lt;b&gt;1.1 도메인 로직 패턴&lt;/b&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;h3 data-ke-size=&quot;size23&quot;&gt;1.1.1 트랜잭션 스크립트 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 스크립트는 클라이언트가 요청한 비즈니스 로직을 하나의 프로시저가 모두 처리한다. 이는 반복적인 CRUD를 처리할 수 있는 경우에 적합하다. 이를 이용한 기술은 서블릿 또는 JSP가 있다.&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;1.1.2 테이블 모듈 패턴&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 모듈은 데이터 베이스 테이블 단위로 비즈니스 로직을 처리하는 클래스를 분리한다. JDBC가 제공하는 ResultSet을 주로 사용한다. 데이터 베이스 테이블에 대응하는 클래스를 테이블 모듈로 선언하고 대상 테이블의 데이터를 조회하거나 변경하는 프로시저를 포함한다.&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;1.1.3 서비스 레이어 패턴&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;서비스 경계를 사용하는 클라이언트와 통합을 위해 추가한 서비스 발행 레이어를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 발행 레이어(API 레이어를 뜻하는 것 같다.) 는 서비스 레이어를 변경하지 않으면서 HTTP뿐만 아니라 Socket, gRPC 등 다양한 프로토콜을 요구하는 클라이언트를 지원하게 확장할 수 있다.&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;1.1.4 도메인 모델 패턴&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;exception을 어떻게 정의하는 것이 좋을까?&lt;br /&gt;1. 시스템명 + Exception : 상황에 맞는 메세지를 생성자 파라미터로 전달&lt;br /&gt;2. 구체적인 예외클래스 선언 : 시스템을 이해 및 분석하는데 수월 (ex. ItemLimitExceedException)&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.2 헥사고날 아키텍쳐&amp;nbsp;&lt;/b&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.3 도메인 주도 설계&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 주도 설계는 관심사를 &lt;b&gt;바운디드 컨텍스트&lt;/b&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;1.3.1 유비쿼터스 언어&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;유비쿼터스 언어는 사용하는 컨텍스트에 따라 달리질 수 잇어, 사용할 수 있는 범위를 한정했을 때 비로소 '보편적'이라고 할 수 있다.&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;1.3.2 엔티티&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티는 테이블과 관계중심의 데이터 모델을 만들고, 테이블에 대응하는 클래스로 매핑하면 엔티티는 가장 기본적인 책임을 가진 인포메이션 홀더의 역할을 가져 getter와 setter 메소드만 제공한다.&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;식별자는 데이터베이스 시퀀스나 UUID를 사용할 수 있습니다. 객체 지향에서는 OID(Object Identifier) 라고도 한다.&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;1.3.3 값 객체&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자가 필요하지 않는 객체로, 다른 객체를 수식하기 위해 존재한다. 값 객체의 중요한 특징은 불이다. setter가 없어야 하고, 필요하다면 외부에서 호출할 수 없도록 private 로 선언해야 한다. 값 객체를 직접 접근해서, 속성을 변경하지 않고, 엔티티 객체를 통해서만 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 객체는 원칙적으로 불변이지만, 특별한 상황에서는 setter를 이용한 변경을 허용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 값이 자주 변경돼 메모리를 효율적으로 사용하지 못할 때,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체 생성이나 삭제에 많은 비용이 드는 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 교체로 인해 클러스터링이 제한되는 경우,&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;</description>
      <category>책 일지/이벤트 소싱과 마이크로서비스 아키텍쳐</category>
      <category>도메인</category>
      <category>도메인모델</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/52</guid>
      <comments>https://worldi.tistory.com/52#entry52comment</comments>
      <pubDate>Thu, 2 Jan 2025 07:50:37 +0900</pubDate>
    </item>
    <item>
      <title>메타 데이터 락</title>
      <link>https://worldi.tistory.com/50</link>
      <description>&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;배경&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메타 데이터락을 공부하던 도중, 의문점이 들었습니다&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제의 코드입니다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjhBiZ/btsGweJYwmp/igMA3Hzz3fSdnES38v5I51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjhBiZ/btsGweJYwmp/igMA3Hzz3fSdnES38v5I51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjhBiZ/btsGweJYwmp/igMA3Hzz3fSdnES38v5I51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjhBiZ%2FbtsGweJYwmp%2FigMA3Hzz3fSdnES38v5I51%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1280&quot; height=&quot;960&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBTMTH/btsGwOKJ2Vf/uEUMNYE4WOyEXkpYqKiys0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBTMTH/btsGwOKJ2Vf/uEUMNYE4WOyEXkpYqKiys0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBTMTH/btsGwOKJ2Vf/uEUMNYE4WOyEXkpYqKiys0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBTMTH%2FbtsGwOKJ2Vf%2FuEUMNYE4WOyEXkpYqKiys0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1280&quot; height=&quot;839&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;839&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;다음의 코드는 access_log의 구조를 바꾸기 위해 access_log_new를 생성하여 access_log의 데이터를 옮기고, access_log_new를 다시 access_log 로 만드는 과정입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;궁금증&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;P166의 중반부에 있는 예제에서 '트랜젝션을&lt;span&gt;&amp;nbsp;&lt;/span&gt;autocommit으로 실행(BEGIN 이나 START TRANSACTION으로 실행하면 안됨)' 라고 적혀있는데 왜 그럴까요? start commit 을 쓰면 안되는 이유가 있을까요?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BjQxN/btsGwCDTBhv/G6kO9deqK2Iolxkl32eTe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BjQxN/btsGwCDTBhv/G6kO9deqK2Iolxkl32eTe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BjQxN/btsGwCDTBhv/G6kO9deqK2Iolxkl32eTe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBjQxN%2FbtsGwCDTBhv%2FG6kO9deqK2Iolxkl32eTe0%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;1052&quot; height=&quot;99&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공식문서에 따르면, 트랜젝션을 시작하게 되면(start transaction..등) 기존에 있었던&lt;span&gt;&amp;nbsp;&lt;/span&gt;UNLOCK TABLE&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 수행됩니다. 이는, 코드를 보면 중간에 commit을 통해서 트랜젝션이 끝나게 됩니다. (이는 access_log의 내용이 access_log_new에 반영되어야 되기 때문에 꼭 필요한 작업입니다.) 하지만 이후, start transaction을 하게 되면 unlock을 통해서 기존에 잡혀 있던 쓰기락이 풀리게 되므로 원치않는 결과를 초래합니다..  &lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 set autocommit을 하여, 반영 사항을 그대로 커밋시키면 안될까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0VIgF/btsGwaAKUgX/W2CvSxBPWhPyRcov3vKH41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0VIgF/btsGwaAKUgX/W2CvSxBPWhPyRcov3vKH41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0VIgF/btsGwaAKUgX/W2CvSxBPWhPyRcov3vKH41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0VIgF%2FbtsGwaAKUgX%2FW2CvSxBPWhPyRcov3vKH41%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;1033&quot; height=&quot;158&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공식문서에 따르면, set autocommit=1을 할 시, 테이블 락을 걸게 되면 deadlock을 피하기 위해 innoDB에서는 내부적인 테이블 락을 놓게 된다고 합니다. 따라서 이는 사용할 수 없겠네요!&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;메타 데이터 락은 묵시적으로 실행될 때 어떤 락(readlock, writelock) 이 걸릴까요?&lt;br /&gt;write lock이 걸린다고 합니다. 생각해봐도 테이블의 구조가 바뀌는데 이전의 메타 데이터를 가져 오면 안되겠죠??&lt;/li&gt;
&lt;li&gt;access_log는 쓰기 잠금을 하는 것은 알겠는데 왜 access_new_log 또한 쓰기 잠금을 하는 걸까요? (이전 페이지에서는 여러개 쓰레드 동시 돌릴려고 쓰기 잠금 안걸음)&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4개의 쓰레드가 돌 때, 테이블에 대한 쓰기락을 안걸었는데 왜 rename 과정때 access_new_log 에 쓰기 잠금을 했을 까요?&lt;br /&gt;테이블의 이름이 바뀌는 과정은 다음과 같습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;테이블 이름 바뀌기 전&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ci2Pag/btsGxB5ld6o/GjOBKtpa7DxSMwa8TKwCH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ci2Pag/btsGxB5ld6o/GjOBKtpa7DxSMwa8TKwCH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ci2Pag/btsGxB5ld6o/GjOBKtpa7DxSMwa8TKwCH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fci2Pag%2FbtsGxB5ld6o%2FGjOBKtpa7DxSMwa8TKwCH1%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;1280&quot; height=&quot;668&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;테이블 이름 바뀐 후&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWUP7g/btsGuqkis8q/5dVhugGurfYMUO1bP9ikqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWUP7g/btsGuqkis8q/5dVhugGurfYMUO1bP9ikqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWUP7g/btsGuqkis8q/5dVhugGurfYMUO1bP9ikqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWUP7g%2FbtsGuqkis8q%2F5dVhugGurfYMUO1bP9ikqK%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;1280&quot; height=&quot;689&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같이, 테이블의 이름이 access_log_new 에서 access_log로 테이블이 바뀌게 됩니다. 그럼 이때 access_log에는 쓰기락이 걸리지 않은 상태이고, 이때 insert 과 같이 테이블 변경 작업을 하게된다면..? 이또한 원치않는결과를 초래하게 될 것입니다!  &lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;테이블 락이 걸릴 때, 메타 데이터 락도 걸린다?&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; 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;1280&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnDgnL/btsGwO42sHt/jE5dbS5jprTyBgJYiNW8lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnDgnL/btsGwO42sHt/jE5dbS5jprTyBgJYiNW8lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnDgnL/btsGwO42sHt/jE5dbS5jprTyBgJYiNW8lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnDgnL%2FbtsGwO42sHt%2FjE5dbS5jprTyBgJYiNW8lk%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;1054&quot; height=&quot;146&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;참고 자료&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html&quot;&gt;https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/metadata-locking.html&quot;&gt;https://dev.mysql.com/doc/refman/5.7/en/metadata-locking.html&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/commit.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/commit.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발 관련 일지</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/50</guid>
      <comments>https://worldi.tistory.com/50#entry50comment</comments>
      <pubDate>Wed, 10 Apr 2024 04:13:08 +0900</pubDate>
    </item>
    <item>
      <title>테스트 더블이란 무엇인가</title>
      <link>https://worldi.tistory.com/47</link>
      <description>&lt;h1&gt;테스트 더블,&amp;nbsp; 테스트 대역&lt;/h1&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;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;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;스파이 : 기능적으로 목과 같다. SUT의 간접 출력을 제공하기 위한 대역이다.&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더미는 SUT를 구성하거나 테스트를 실행하는 데 필요하지만, 테스트 하려는 요구사항에는 전혀 영향을 주지 않는 대역이다.&lt;/li&gt;
&lt;li&gt;Stub은 SUT의 간접 입력을 제공하기 위한 대역이다.&lt;/li&gt;
&lt;li&gt;Fake는 실제로 동작하지만, 운영 환경에서 사용하기에는 적합하지 않은 대역을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로, 이메일 발송은, SMTP 서버에 사이드 이펙트를 초래하는 상호 작용이다. &amp;rarr; 목&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로, 데이터 검색은 데이터 베이스에서 데이터를 검색하는 것은 내부로 들어오는 상호작용으로 사이드 이펙트를 초래하지 않는다. &amp;rarr; 스텁&lt;/p&gt;
&lt;h1&gt;도구로서의 목과, 테스트 대역으로서의 목&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 가장 크게 헷갈린 부분이다&amp;hellip; mock 클래스를 쓰는 애는 다 테스트 대역으로서의 목인줄 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 도구로서의 목은 mock 라이브러리에 있는 mock 클래스를 쓰는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 테스트 대역으로서의 목은 검증 기능을 갖는, sut 호출을 검사하는 것을 말한다.&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;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public void Sending_a_greeting_email(){
	// Arrange
	var mock = new Mock&amp;lt;IEmailGateWay&amp;gt;();
	var sut = new Controller(mock.Object);
	// Act 
	sut.GreetUser(&quot;user@email.com&quot;);
	// Assert
	mock.Verift(x-&amp;gt; x.SendGreeintgsEmail(), Times.Once)); // sut의 호출을 검사
}
&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;테스트 대역 : stub&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public void Creating_a_report(){
	// Arrange
	var stub = new Mock&amp;lt;IDatabase&amp;gt;();
	stub.setup(x=&amp;gt; x.GetNumberOfUsers()).Returns(10);	
	// Act
	var sut = new Controller(stub.Ojbect);
	Report report = sut.CreateReport();
	
	// Assert
	Assert.Equal(10, report.NumberOfUsers);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스텁은 상호 작용을 검증 X&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;h2 data-ke-size=&quot;size26&quot;&gt;목과 스텁이 함께 쓰이면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 대역이 목이면서 스텁일 수 있다. 목이라는 사실이 스텁이라는 사실보다 더 중요해서 목이라고 대체로 많이한다고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목과 스텁, 그리고 CQS&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령 : 사이드 이펙트를 일으키고, 어떤 값도 반환 X (void 타입) &amp;rarr;목&lt;/li&gt;
&lt;li&gt;조회 : 사이드 이펙트가 없고, 값을 반환. &amp;rarr; 스텁&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;참고 자료&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위테스트 책 그리고 회사 문서&lt;/p&gt;</description>
      <category>개발 관련 일지/TDD</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/47</guid>
      <comments>https://worldi.tistory.com/47#entry47comment</comments>
      <pubDate>Mon, 12 Feb 2024 04:46:32 +0900</pubDate>
    </item>
    <item>
      <title>좋은 단위 테스트의 4대 요소</title>
      <link>https://worldi.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;페어프로그래밍을 하면서 거짓 양성, 블랙 박스, 화이트 박스&amp;hellip; 같은 용어들이 생소했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 단위 테스트 책을 읽으면서 해당 관련 지식을 보충하게 되었다! 단위 테스트 책 4단원에 해당 지식이 나온다.&lt;/p&gt;
&lt;h1&gt;좋은 단위 테스트의 4가지 특성&lt;/h1&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;li&gt;유지 보수성&lt;/li&gt;
&lt;/ul&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;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;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;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&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;h3 data-ke-size=&quot;size23&quot;&gt;거짓 양성&lt;/h3&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;테스트와 sut의 구현 세부 사항이 많이 결합할 수록 허위 경보가 많이 생긴다. 즉 sut의 특정 구현 (결과 전달을 위해 sut가 수행해야 할 특정 단계)를 예상하므로 깨지기 쉽다. &amp;rarr; sut의 구현을 리팩터링하면, 모두 테스트 실패로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 테스트를 깨지지 않고, 리팩터링 내성을 높이는 방법은 SUT 의 구현 세부 사항과 테스트 간의 결합도를 낮추는 것이다.&lt;/b&gt; 즉, sut를 블랙박스 취급하고, sut가 만든 최종 결과를 검증해야 한다.&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;1600&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l51Qf/btsEFTaa5Ht/QUvoP0pVHBvgGplBS42zQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l51Qf/btsEFTaa5Ht/QUvoP0pVHBvgGplBS42zQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l51Qf/btsEFTaa5Ht/QUvoP0pVHBvgGplBS42zQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl51Qf%2FbtsEFTaa5Ht%2FQUvoP0pVHBvgGplBS42zQK%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;1600&quot; height=&quot;1200&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;두가지 특성(회귀 방지, 리팩터링 내성)의 본질적 관계&lt;/h1&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;1600&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxQLdI/btsEE0U8j2a/lx57icU2M7Z0dQ5H8kuPFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxQLdI/btsEE0U8j2a/lx57icU2M7Z0dQ5H8kuPFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxQLdI/btsEE0U8j2a/lx57icU2M7Z0dQ5H8kuPFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxQLdI%2FbtsEE0U8j2a%2Flx57icU2M7Z0dQ5H8kuPFK%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;1600&quot; height=&quot;651&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;651&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;기능 작동&amp;rarr; 테스트 통과, 기능 실패 &amp;rarr; 테스트 실패는 자연스러운 추론이다.&lt;/li&gt;
&lt;li&gt;하지만, 기능이 고장났는데 테스트를 통과하면 문제가 된다. &amp;rarr; 거짓음성이다. &amp;rarr; 회귀 방지가 훌륭하면, 거짓 음성을 최소화 할 수 있다.&lt;/li&gt;
&lt;li&gt;기능이 올바른데, 테스트가 실패하면 &amp;rarr; 거짓 양성이다. &amp;rarr; 리팩터링 내성이 높으면, 도움이 된다.&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;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;테스트 정확도 = 신호 (발견된 버그 수)/ 소음 (허위 경보 발생 수)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 소음이 적고, 버그를 잘 찾아 내면 테스트가 명확해 진다.&lt;/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;h1&gt;빠른 피드백과 유지보수성&lt;/h1&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;li&gt;테스트가 얼마나 실행하기 어려운가 : 의존성 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;좋은 단위 테스트&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;좋은 단위 테스트 = 회귀 방지 * 리팩터링 내성 * 빠른 피드백 * 유지보수성&lt;/span&gt;&lt;/blockquote&gt;
&lt;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;블랙 박스 테스트
&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;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&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;시스템을 블랙박스로 보고, 의미 있는 동작을 확인해라. &lt;br /&gt;그리고 테스트를 분석할 때 화이트 박스 방법을 사용해라!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지/TDD</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/46</guid>
      <comments>https://worldi.tistory.com/46#entry46comment</comments>
      <pubDate>Mon, 12 Feb 2024 03:38:45 +0900</pubDate>
    </item>
    <item>
      <title>CEQ, CQRS, ES 가 무엇일까?</title>
      <link>https://worldi.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 클래스 네임과 관련된 코드 리뷰를 받았는데, 클래스 명명에서도 고려해야 할 점이 있음을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 CQRS와 ES(event sourcing)에서 많이 쓰이는 패턴이다. 따라서 CQRS와 ES가 궁금해져서 간단히 찾아보았다.&lt;/p&gt;
&lt;h1&gt;CQRS &amp;amp; ES&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wtl5m/btsEHHUlCgA/5Vezp9KRVkzMy05PxZQfk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wtl5m/btsEHHUlCgA/5Vezp9KRVkzMy05PxZQfk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wtl5m/btsEHHUlCgA/5Vezp9KRVkzMy05PxZQfk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWtl5m%2FbtsEHHUlCgA%2F5Vezp9KRVkzMy05PxZQfk1%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;2000&quot; height=&quot;791&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;791&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;Commands
&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;li&gt;명령의 생산자는 때로는 순서대로 처리 되기를 원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Events
&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;producer는 대가로 어떤 응답도 기대하지 않는다.&lt;/li&gt;
&lt;li&gt;발생한 일에 대한 알림만 전달하므로 가볍다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Publisher/subscriber semantics&lt;/b&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;li&gt;일반적으로 이벤트 구독은 메세지 브로커 같은 중개자가 있다. 퍼블리셔가 이벤트를 보내면, 브로커는 관심있는 구독자에게 라우팅하는 구조이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Query
&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;쿼리는 HTTP나 gRPC같은 프로토콜을 통해 구현할 수 있는 동기식 작업이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;명령
&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;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;CEQ 패턴&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Command를 하는 경우 commandExecutor, Query를 하는 경우 queryProcessor와 같은 명명법을 쓴다. 각각의 명명법은 동사 명령형을 따른다.&lt;/li&gt;
&lt;li&gt;이벤트는 과거형을 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 멤버를 create하는 경우 CreateUserCommandExecutor, 멤버를 찾는 경우 FindUserQueryProsser 같은 형식이다. 이벤트의 경우는 과거형으로 UserCreated 같이 쓴다.&lt;/p&gt;
&lt;h1&gt;참고 자료&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/dhslrl321/cqrs-journey-guide-korean&quot;&gt;https://github.com/dhslrl321/cqrs-journey-guide-korean&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/event-driven-utopia/using-commands-events-and-queries-in-microservices-communication-3573f1fcfafe&quot;&gt;https://medium.com/event-driven-utopia/using-commands-events-and-queries-in-microservices-communication-3573f1fcfafe&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/45</guid>
      <comments>https://worldi.tistory.com/45#entry45comment</comments>
      <pubDate>Sun, 11 Feb 2024 20:27:16 +0900</pubDate>
    </item>
    <item>
      <title>그림으로 배우는 쿠버네티스 CH4 애플리케이션 노출</title>
      <link>https://worldi.tistory.com/44</link>
      <description>&lt;h1&gt;&lt;b&gt;배포한 애플리케이션을 노출하는 방법&lt;/b&gt;&lt;/h1&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;간단한 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;port-forward&lt;/li&gt;
&lt;li&gt;HostPort&lt;/li&gt;
&lt;li&gt;hostNetwork&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;NodePort&lt;/li&gt;
&lt;li&gt;LoadBalancer&lt;/li&gt;
&lt;li&gt;ExternalName&lt;/li&gt;
&lt;li&gt;ClusterIP, Headless&lt;/li&gt;
&lt;li&gt;Endpoints&lt;/li&gt;
&lt;li&gt;Ingress&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;&lt;b&gt;간단한 방법(port-forward, HostPort, hostNetwork)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;포트 포워딩&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 PC에서 가상 머신으로 접속 시 포트 포워딩 사용&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; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qiln9/btsEEXcRn3Z/7BM6RzUTS3rZBJuA6CLU1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qiln9/btsEEXcRn3Z/7BM6RzUTS3rZBJuA6CLU1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qiln9/btsEEXcRn3Z/7BM6RzUTS3rZBJuA6CLU1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQiln9%2FbtsEEXcRn3Z%2F7BM6RzUTS3rZBJuA6CLU1K%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;2000&quot; height=&quot;830&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;port-forward 옵션&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnuYr5/btsEEBVDmyB/JjnI3v8ZHCVgxk7DicniE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnuYr5/btsEEBVDmyB/JjnI3v8ZHCVgxk7DicniE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnuYr5/btsEEBVDmyB/JjnI3v8ZHCVgxk7DicniE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnuYr5%2FbtsEEBVDmyB%2FJjnI3v8ZHCVgxk7DicniE1%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;2000&quot; height=&quot;970&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;970&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;k port-forward fwd-chk-hn 80:80: 마스터 노드의 80번 포트를&amp;nbsp;fwd-chk-hn&amp;nbsp;노드의 80번 포트로 포워딩&lt;/li&gt;
&lt;li&gt;노드가 재시작 될 경우 포트 포워딩이 사라짐. 내부에서 포트를 포워딩할 때만 쓰지 영속적으로 쓸수는 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실습&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스터 노드 &amp;rarr; 워커 노드 포트포워딩하는 구조이다.&lt;/li&gt;
&lt;li&gt;마스터 노드에 127.0.0.1.80 으로 들어온 글 파드에 80으로 보내주라는 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;k port-forward fwd-chk-hn 80:80
&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;curl 192.168.1.10 이렇게 하면 failed connection이 뜬다. 하지만, port-forward &amp;mdash;address 0.0.0.0 fwd-chk-hn 80:80 이라고 하면 모든 연결에 대해서 열어 준 것이므로 다시 curl 요청하면 연결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HostPort&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baseJk/btsEGQ4PfH3/VUx8R1usfFeU85wUfKzXNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baseJk/btsEGQ4PfH3/VUx8R1usfFeU85wUfKzXNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baseJk/btsEGQ4PfH3/VUx8R1usfFeU85wUfKzXNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaseJk%2FbtsEGQ4PfH3%2FVUx8R1usfFeU85wUfKzXNk%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;2000&quot; height=&quot;958&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;958&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;containerPort: 파드의 포트&lt;/li&gt;
&lt;li&gt;hostPort: 노드의 포트&lt;/li&gt;
&lt;li&gt;노드의 IP와 포트를 알고 있어야 하므로 실용적이지 않음. 사용자가 어디에 디플로이 되었는지 알아야 함 워커노드 8080:80으로 해주는 것임&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HostNetwork&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;963&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ppc2t/btsEEINMl6z/8kUYMKg8Z9PbbGRCqxE9ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ppc2t/btsEEINMl6z/8kUYMKg8Z9PbbGRCqxE9ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ppc2t/btsEEINMl6z/8kUYMKg8Z9PbbGRCqxE9ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpc2t%2FbtsEEINMl6z%2F8kUYMKg8Z9PbbGRCqxE9ck%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;2000&quot; height=&quot;963&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;963&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;eth0을 그대로 쓸 수 있게 해주는 기능&lt;/li&gt;
&lt;li&gt;클러스터의 노드에 직접 접속하므로 실용적이지 않음. &lt;b&gt;클러스터에 어디에 배포되어있는지 알아야 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;클러스터의 대문이 없어서 사용할 수 없음. &amp;rarr; 이를 대비해서 노드포트, 로드밸런서가 있는 것이다.&lt;/li&gt;
&lt;li&gt;호스트 네트워크 점유하는 설정 그대로 가져온 것임&lt;/li&gt;
&lt;li&gt;제한적인 부분 많음, 어느 노드 deploy 되었는지 알수없어서 쓰이지 않는다.&lt;/li&gt;
&lt;li&gt;포트 포워딩은 어느정도 사용되지만 hostport와 hostNetwork는 거의 사용이 되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&lt;b&gt;노드포트(NodePort)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;nodeport.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coFUOB/btsEECUxvF8/8SgeK6Hy9h8msAeaRCK5Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coFUOB/btsEECUxvF8/8SgeK6Hy9h8msAeaRCK5Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coFUOB/btsEECUxvF8/8SgeK6Hy9h8msAeaRCK5Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoFUOB%2FbtsEECUxvF8%2F8SgeK6Hy9h8msAeaRCK5Y1%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;2000&quot; height=&quot;1019&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1019&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;/li&gt;
&lt;li&gt;selector : 노출할 오브젝트&lt;/li&gt;
&lt;li&gt;nodePort : 노드 포트, 미입력 시 랜덤으로 지정&lt;/li&gt;
&lt;li&gt;port : 서비스 포트&lt;/li&gt;
&lt;li&gt;targetPort : 파드 포트&lt;/li&gt;
&lt;li&gt;두 개의 오브젝트 구분할 때는 구분선 3개 써줘야 한다.&lt;/li&gt;
&lt;li&gt;셀렉터는 디플로이멘트를 노출한다. &amp;rarr; 이름을 노출한다.&lt;/li&gt;
&lt;li&gt;노드 포트 3만번 외부 노출
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;80번 pod &amp;rarr; 80 port : 앞에 있는 포트는 서비스에 대한 포트번호를 의미한다. 뒤는 컨테이너 pod에 대한 타켓에 대한 포트를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;노드 포트 서비스로 노출된 것이니 NodePort 서비스로 보내줘야겠군.
&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;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;989&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUJ7b6/btsEJb8uKjl/DQgqrMZeTwegu5ZsTu6AyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUJ7b6/btsEJb8uKjl/DQgqrMZeTwegu5ZsTu6AyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUJ7b6/btsEJb8uKjl/DQgqrMZeTwegu5ZsTu6AyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUJ7b6%2FbtsEJb8uKjl%2FDQgqrMZeTwegu5ZsTu6AyK%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;2000&quot; height=&quot;989&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;989&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;사용자가 워커 노드의 NodePort 서비스 포트로 접속&lt;/li&gt;
&lt;li&gt;연결을 NodePort 서비스로 보내줌&lt;/li&gt;
&lt;li&gt;NodePort는 워커 노드의 파드로 연결을 보냄&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;&lt;b&gt;로드밸런서(LoadBalancer)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;loadbalancer-11.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1033&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIjQHt/btsEEwAaH1M/ctkTZl4CjXO4fY1VKX6yt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIjQHt/btsEEwAaH1M/ctkTZl4CjXO4fY1VKX6yt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIjQHt/btsEEwAaH1M/ctkTZl4CjXO4fY1VKX6yt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIjQHt%2FbtsEEwAaH1M%2FctkTZl4CjXO4fY1VKX6yt1%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;2000&quot; height=&quot;1033&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1033&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://user-images.githubusercontent.com/52024566/157054135-0c7036b6-4131-4cbe-af6a-79ab9e2c1699.png&quot;&gt;https://user-images.githubusercontent.com/52024566/157054135-0c7036b6-4131-4cbe-af6a-79ab9e2c1699.png&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;port: 서비스 포트&lt;/li&gt;
&lt;li&gt;targetPort: 파드 포트&lt;/li&gt;
&lt;li&gt;Metallb 로 접근&lt;/li&gt;
&lt;li&gt;로드밸런서는 노드 포트 필요없고 Metallb을 통해서 구성하게 된다.&lt;/li&gt;
&lt;li&gt;한 개 더 배포하면, 포트와 타켓 포트를 통해서, 로드 밸런서를 통해서 선언된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;loadbalancer-12.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0T6Lo/btsEFLQy3Ys/8H0WKGjMBqj08kLSgsdGZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0T6Lo/btsEFLQy3Ys/8H0WKGjMBqj08kLSgsdGZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0T6Lo/btsEFLQy3Ys/8H0WKGjMBqj08kLSgsdGZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0T6Lo%2FbtsEFLQy3Ys%2F8H0WKGjMBqj08kLSgsdGZ0%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;2000&quot; height=&quot;1018&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1043&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/43BZB/btsEEzQ5i1G/6zWs3L1xG07BBdFIvruqb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/43BZB/btsEEzQ5i1G/6zWs3L1xG07BBdFIvruqb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/43BZB/btsEEzQ5i1G/6zWs3L1xG07BBdFIvruqb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F43BZB%2FbtsEEzQ5i1G%2F6zWs3L1xG07BBdFIvruqb0%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;2000&quot; height=&quot;1043&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1043&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;사용자는&amp;nbsp;ConfigMap을 통하여 EXT IP로 접속&lt;/li&gt;
&lt;li&gt;내부의 파드로 연결됨&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;k get po -n metalb-system
k apply -f ~11
k apply -f ~12 
k delete -f _/ch4/4.4/* 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 밸런서 두개 실행한다. 순서에 따라서 ip가 제공이 되기 때문에 순서가 바뀌면 안된다. 11번을 먼저 실행해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노드포트와 로드 밸런서 2개 동작을 확인한다.&lt;/b&gt;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;외부 이름(ExternalName)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;externalname-1.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqD2hu/btsEIRbbKwC/xp8yTDvkkb3IpzljKgM060/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqD2hu/btsEIRbbKwC/xp8yTDvkkb3IpzljKgM060/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqD2hu/btsEIRbbKwC/xp8yTDvkkb3IpzljKgM060/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqD2hu%2FbtsEIRbbKwC%2Fxp8yTDvkkb3IpzljKgM060%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;2000&quot; height=&quot;563&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;externalname-2.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwdnIP/btsEFSvh1mK/niNe6Ysutwaz9FGiVSm74k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwdnIP/btsEFSvh1mK/niNe6Ysutwaz9FGiVSm74k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwdnIP/btsEFSvh1mK/niNe6Ysutwaz9FGiVSm74k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwdnIP%2FbtsEFSvh1mK%2FniNe6Ysutwaz9FGiVSm74k%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;2000&quot; height=&quot;562&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;562&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;/li&gt;
&lt;li&gt;외부의 이름을 그대로 서비스하는 것이다.&lt;/li&gt;
&lt;li&gt;외부 도메인 주소. 자기가 원하는 대로 수정해야한다. 외부 주소 변경되면 코드 자체를 수정해야 한다. 외부의 서비스를 서비스화 해서 만들어준 독특한 컨셉이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bveTPr/btsEE1zxNop/nsA94aQY3QK2pemvNO28ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bveTPr/btsEE1zxNop/nsA94aQY3QK2pemvNO28ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bveTPr/btsEE1zxNop/nsA94aQY3QK2pemvNO28ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbveTPr%2FbtsEE1zxNop%2FnsA94aQY3QK2pemvNO28ik%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;2000&quot; height=&quot;921&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;921&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;컨테이너 내부에서 외부 도메인을 aliasing 하는 데에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ls _Lecture/...
k get svc
k run net --image=sysnet4admin/net-tools-ifn 

k get po
k exec net -it -- /bin/bash

nslookup ex-url-1 # 클러스터 안에서. 외부에 있는 이름을 aliasing 해준다. 
exit

k delete -f external_service 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;클러스터주소(ClusterIP)와 헤드리스(Headless)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;cluster ip.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpiWJp/btsEHpTxisp/5ysQJCQ9wSfb5asgGg6Xo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpiWJp/btsEHpTxisp/5ysQJCQ9wSfb5asgGg6Xo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpiWJp/btsEHpTxisp/5ysQJCQ9wSfb5asgGg6Xo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpiWJp%2FbtsEHpTxisp%2F5ysQJCQ9wSfb5asgGg6Xo1%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;2000&quot; height=&quot;1038&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1038&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;파드와 파드의 연결을 위한 내부 IP&lt;/li&gt;
&lt;li&gt;포트와 타깃 포트를 지정한다.&lt;/li&gt;
&lt;li&gt;클러스터 ip는 파드를 연결하는 내부의 ip이다. 클러스터 내부에서 쓰이는 목적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;headless.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1035&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAHmw/btsEEYJELfu/WX9PtKeRtNua5hQ485b2W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAHmw/btsEEYJELfu/WX9PtKeRtNua5hQ485b2W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAHmw/btsEEYJELfu/WX9PtKeRtNua5hQ485b2W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAHmw%2FbtsEEYJELfu%2FWX9PtKeRtNua5hQ485b2W1%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;2000&quot; height=&quot;1035&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1035&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;클러스터 IP를&amp;nbsp;None으로 지정하면&amp;nbsp;Headless가 됨&lt;/li&gt;
&lt;li&gt;헤드 리스는 클러스터 ip는 none. 내부에서 쓰이지만 Ip가 없는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클러스터주소와 헤드리스 비교&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤드리스를 사용할 경우 IP 주소를 소진하지 않고 스테이트 풀셋을 연결할 수 있음&lt;/li&gt;
&lt;li&gt;헤드리스는 statefulset과 밀접한 연관이있다. ip를 소진하지 않고도 내부에서 도메인 이름으로 통신할 수 있기 때문이다. 헤드리스에서 도메인 이름이 제공된다면 그 도메인 이름으로 스테이트 풀셋을 연결할 수 있다. 만약 다른 거라면 레플리카셋 이름이 해시값이어서 내부에서 도메인으로 호출되는 호스트 이름이 매번 바뀐다. 하지만 statefulset은 이름이 고정이다.&lt;/li&gt;
&lt;li&gt;스테이트 풀셋 각각의 파드는 고정된 이름을 갖고 각각의 목적을 갖고 있도록 설계할 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NodePort나&amp;nbsp;LoadBalancer를 사용할 경우 각각의 파드에 대한 접속을 통제하기 어려움&lt;/li&gt;
&lt;li&gt;따라서 헤드리스를 사용하여 스테이트 풀셋에 대한 연결을 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;sts-svc-domain-headless.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;837&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eMReiV/btsEGRJqY6Z/XFKgANQGPNpDy8FKlt720K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eMReiV/btsEGRJqY6Z/XFKgANQGPNpDy8FKlt720K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eMReiV/btsEGRJqY6Z/XFKgANQGPNpDy8FKlt720K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeMReiV%2FbtsEGRJqY6Z%2FXFKgANQGPNpDy8FKlt720K%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;2000&quot; height=&quot;837&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;837&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;nslookup sts-svc-domain
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nslookup sts-chk-hn-0.sts-svc.domain nslookup sts-chk-hn-1.sts-svc.domain nslookup sts-chk-hn-2.sts-svc.domain&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;serviceName을 통하여 노드와 헤드리스가 결합됨&lt;/li&gt;
&lt;li&gt;LoadBalancer로 노출은 가능하나 스테이트풀셋의 목적에 부합하지 않으므로 자주 사용하지는 않음&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;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;  stateFulset + headless 조합으로 쓰고 싶은 경우, service metadata의 이름과 stateFulSet의 serviceName과 매핑 시켜주어야 함!!!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;엔드포인트(Endpoints)&lt;/b&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드밸런서, 디플로이맨트 연결하면 &amp;rarr; endPoint가 만들어진다.&lt;/li&gt;
&lt;li&gt;로드벨런서 엔드 포인트 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;k apply -f dsjakdlajsl
k get endpoints #항상 복수임   
k apply -f 
k get po, svc 
k get po -o wide 
&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;loadbalancer.yaml의 엔드포인트&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zn3J5/btsEFdzL01Z/DjWPEYRpOjEoHflFUmjUK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zn3J5/btsEFdzL01Z/DjWPEYRpOjEoHflFUmjUK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zn3J5/btsEFdzL01Z/DjWPEYRpOjEoHflFUmjUK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZn3J5%2FbtsEFdzL01Z%2FDjWPEYRpOjEoHflFUmjUK0%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;2000&quot; height=&quot;948&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;948&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;/li&gt;
&lt;li&gt;외부에 노출된 서비스를 통하여 도달하는 파드의 IP를 일컬음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;service-endpoints.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfn3KE/btsEGiHijtK/QKzSO86WGtspAgdtKR5EQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfn3KE/btsEGiHijtK/QKzSO86WGtspAgdtKR5EQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfn3KE/btsEGiHijtK/QKzSO86WGtspAgdtKR5EQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfn3KE%2FbtsEGiHijtK%2FQKzSO86WGtspAgdtKR5EQk%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;2000&quot; height=&quot;1181&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1181&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;metadata.name을 클러스터와 일치시켜 수동으로 엔드포인트 생성&lt;/li&gt;
&lt;li&gt;위와 같이 로드밸런서의 IP를 엔드포인트로 지정할 경우 로드밸런서를 거쳐 최종적으로 파드에 도달&lt;/li&gt;
&lt;li&gt;엔드포인트 임의로 만들어서 경로를 매뉴얼하게 지정하는 것이라 생각하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&lt;b&gt;인그레스(Ingress)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NodePort와 인그레스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터 &amp;rarr; 엔드포인트로 연결하게 된다.&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;2000&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwrpK8/btsEGhhiY1u/zil3i7lH7jb4McXTI3rPxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwrpK8/btsEGhhiY1u/zil3i7lH7jb4McXTI3rPxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwrpK8/btsEGhhiY1u/zil3i7lH7jb4McXTI3rPxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwrpK8%2FbtsEGhhiY1u%2Fzil3i7lH7jb4McXTI3rPxK%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;2000&quot; height=&quot;1130&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1130&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;사용자가 워커 노드의 포트로 접근&lt;/li&gt;
&lt;li&gt;NodePort 서비스로 이동&lt;/li&gt;
&lt;li&gt;접속 경로에 따라 해당하는 파드로 이동&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LoadBalancer와 인그레스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에서 클러스터 서비스로 연결 되고 이는 각각 서비스의 엔드 포인트로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rfa78/btsEFigGl8c/Owa3PTBl5HEzLk3rdQ6MdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rfa78/btsEFigGl8c/Owa3PTBl5HEzLk3rdQ6MdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rfa78/btsEFigGl8c/Owa3PTBl5HEzLk3rdQ6MdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frfa78%2FbtsEFigGl8c%2FOwa3PTBl5HEzLk3rdQ6MdK%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;2000&quot; height=&quot;1131&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1131&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;사용자가 로드밸런서 서비스에 접근&lt;/li&gt;
&lt;li&gt;접속 경로에 따라 서비스로 이동&lt;/li&gt;
&lt;li&gt;각각의 서비스의 엔드포인트로 이동&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ingress.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Js4l5/btsEHn9gDRZ/D3wpKKASAMUmC5x1RCnck1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Js4l5/btsEHn9gDRZ/D3wpKKASAMUmC5x1RCnck1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Js4l5/btsEHn9gDRZ/D3wpKKASAMUmC5x1RCnck1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJs4l5%2FbtsEHn9gDRZ%2FD3wpKKASAMUmC5x1RCnck1%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;2000&quot; height=&quot;984&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;984&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;path를 지정하여 각각의 서비스와 연결&lt;/li&gt;
&lt;li&gt;각각의 디플로이먼트에 연결해 준다. 서비스 네임으로 연결해준다. 클러스터 ip의 서비스 네임!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;레이블(labels)과 애너테이션(annotations)의 차이&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l7oAG/btsEFhWqVpg/XuUoaj2jCYWJWuZ42F6TKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l7oAG/btsEFhWqVpg/XuUoaj2jCYWJWuZ42F6TKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l7oAG/btsEFhWqVpg/XuUoaj2jCYWJWuZ42F6TKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl7oAG%2FbtsEFhWqVpg%2FXuUoaj2jCYWJWuZ42F6TKk%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;2000&quot; height=&quot;893&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;893&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;Labels: 관리자, 개발자가 인식하는 부가 정보. 사람이 쓸려고 만든 정보&lt;/li&gt;
&lt;li&gt;Annotation: 시스템이 인식하는 부가 정보. 레이블도 주석, 애노테이션도 주석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로 규약에 가깝다.!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;각 타입별 ingress controller 설정의 차이&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b74uxk/btsEEHBqTZw/tsxg7HndEMC9WAinnv6yP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b74uxk/btsEEHBqTZw/tsxg7HndEMC9WAinnv6yP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b74uxk/btsEEHBqTZw/tsxg7HndEMC9WAinnv6yP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb74uxk%2FbtsEEHBqTZw%2Ftsxg7HndEMC9WAinnv6yP0%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;2000&quot; height=&quot;1038&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1038&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;NodePort의 경우 무작위로 지정되나 실습의 편의를 위해 spec 수정&lt;/li&gt;
&lt;li&gt;LoadBalancer는 포트를 지정할 필요 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실습&lt;/h3&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;k apply -f deploy-hn.yml // 세개 디플로이 먼트 진행한다. 세개 서비스 진행한다.  
k apply -f ingress.yaml // ingress 연결한다.  
// 노드 포트 말고 노드 밸런서로 할 수 있다! 
k get svc -n ingree-nginx
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/44</guid>
      <comments>https://worldi.tistory.com/44#entry44comment</comments>
      <pubDate>Sat, 10 Feb 2024 14:49:30 +0900</pubDate>
    </item>
    <item>
      <title>그림으로 배우는 쿠버네티스 CH3 애플리케이션 배포법</title>
      <link>https://worldi.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;배포 형태는 오브젝트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트는 파드, service, namespace, volumes 가 있는데, 그중 Pod와 파드와 관련된 오브젝트들을 알아본다.&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;애플리케이션으로 배포되는 오브젝트 형태&lt;/b&gt;&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파드 (Pod)&lt;/li&gt;
&lt;li&gt;디플로이먼트 (Deployment)&lt;/li&gt;
&lt;li&gt;레플리카셋 (ReplicaSet)&lt;/li&gt;
&lt;li&gt;잡(Job): 파드로 계속 떠있으면 부담이 되는 작업을 처리하기 위한 오브젝트&lt;/li&gt;
&lt;li&gt;크론잡 (CronJob) : 잡을 주기적으로 실행할 수 있도록 하는 오브젝트&lt;/li&gt;
&lt;li&gt;데몬셋 (DaemonSet) : 노드마다 1개씩만 올라가는 파드 (칼리코)&lt;/li&gt;
&lt;li&gt;스테이트풀셋 (StatefulSet) : 순서를 지키면서 배포가 되고 상태를 가지고 있는 파드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존의 Pod는 stateless이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;&lt;b&gt;파드(Pod)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;pod.yaml&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1 #적합한 API 버전
kind: Pod #종류
metadata: #메타성 데이터
    labels: #별명 
        run: po-nginx 
    name: po-nginx
spec:  #Pod에서 호출할 컨테이너 이미지 지정 
    containers:
    - image: nginx
        name: nginx
&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;apiVersion : 오브젝트를 생성하기 위해 사용하고 있는 쿠버네티스 API 버전&lt;/li&gt;
&lt;li&gt;kind : 오브젝트 종류&lt;/li&gt;
&lt;li&gt;metadata : 오브젝트를 유일하게 구분지어 줄 데이터, 레이블과 네임으로 구성됨&lt;/li&gt;
&lt;li&gt;spec : 오브젝트에 대해 의도한 상태. 파드에서 호출할 컨테이너 이미지 지정&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; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZeQjI/btsEIRhWvkr/ZGtrstZ1zM6vpvy4amfK90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZeQjI/btsEIRhWvkr/ZGtrstZ1zM6vpvy4amfK90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZeQjI/btsEIRhWvkr/ZGtrstZ1zM6vpvy4amfK90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZeQjI%2FbtsEIRhWvkr%2FZGtrstZ1zM6vpvy4amfK90%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;2000&quot; height=&quot;1048&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실습&lt;/h2&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;cat _Lect/pod.yaml

k apply -f _Lect/pod.yaml
k get po 
k get po -o wide 
k delete -f pod po-nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;디플로이먼트(Deployment)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;deployment.yaml&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1  # Deployment는 apps/
kind: Deployment 
metadata:
  labels:
    app: deploy-nginx
  name: deploy-nginx 
spec: 
  replicas: 3
  selector: # 템플릿과 연결되어있다. 
    matchLabels:
      app: po-nginx # 동일해야함 
  template: # 붕어빵 틀과 같다.
    metadata:
      labels:
        app: po-nginx #동일해야함
    spec:
      containers:
      - name: nginx
        image: nginx
&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;selector : 디플로이먼트가 관리할 파드를 찾는 방법 정의. selector가 템플릿을 선택하는 구조로, template의 app과 selector app 이 같아야 함.&lt;/li&gt;
&lt;li&gt;template : 파드를 생성하는 템플릿&lt;/li&gt;
&lt;li&gt;디플로이먼트가 파드를 포함하고 있는 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;템플릿하고 pod 구조가 같다. 디플로이먼트는 템플릿을 통해서 파드를 여러개 만든다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1033&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ez3dHT/btsEEYv4RIV/IHuXqqxHA6PkHDAa5euHUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ez3dHT/btsEEYv4RIV/IHuXqqxHA6PkHDAa5euHUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ez3dHT/btsEEYv4RIV/IHuXqqxHA6PkHDAa5euHUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fez3dHT%2FbtsEEYv4RIV%2FIHuXqqxHA6PkHDAa5euHUk%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;2000&quot; height=&quot;1033&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1033&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;레플리카셋(ReplicaSet)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;replicaset.yaml&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: ReplicaSet
metadata:
  labels:
    app: rs-nginx
  name: rs-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: po-nginx
  template:
    metadata:
      labels:
        app: po-nginx
    spec:
      containers:
      - image: nginx
        name: nginx
&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;디플로이먼트와 kind를 제외하고 코드 차이는 없음.&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; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1028&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5v2Fp/btsEEItqTEW/DKI4XImSUrIBKbyB23g0c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5v2Fp/btsEEItqTEW/DKI4XImSUrIBKbyB23g0c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5v2Fp/btsEEItqTEW/DKI4XImSUrIBKbyB23g0c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5v2Fp%2FbtsEEItqTEW%2FDKI4XImSUrIBKbyB23g0c1%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;2000&quot; height=&quot;1028&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1028&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ReplicaSet 대신 Deployment를 사용하는 이유&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l4okK/btsEGQ4OOwq/Nq4VHwO0KpgauD09qAEIRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l4okK/btsEGQ4OOwq/Nq4VHwO0KpgauD09qAEIRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l4okK/btsEGQ4OOwq/Nq4VHwO0KpgauD09qAEIRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl4okK%2FbtsEGQ4OOwq%2FNq4VHwO0KpgauD09qAEIRk%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;2000&quot; height=&quot;747&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;747&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;/li&gt;
&lt;li&gt;위의 이미지는 컨테이너 버전을 순차적으로 Update하는 Rolling Update 과정을 나타냄 (롤링 업데이트)&lt;/li&gt;
&lt;li&gt;Deployment가 1개만 있을 경우 Update를 위해서는 추가적인 Deployment가 필요&lt;/li&gt;
&lt;li&gt;그러한 구조 대신 Deployment가 ReplicaSet을 만들고 ReplicaSet의 복제본으로 업데이트를 진행&lt;/li&gt;
&lt;li&gt;Deployment가 replicaSet를 만들고 두개를 같이 유지하면서 업데이트 할 수 있기 때문에 replicaSet은 하위에서 유지하는 존재로 남아있고, 디플로이먼트는 전체 pod를 관장하는 오브젝트로 남게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;k get replicasets.app # 레플리카 오브젝트 확인
k get deployments.app # 디플리카 오브젝트 확인 
k delete deploy deploy-nginx
k delete replicasets.apps rs-nginx 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;커맨드(command)와 인자(args)&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;컨테이너에 명령이 필요한 이유&lt;/b&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;생성이 됐고, complete이 되었는데, 더 할게 없으니까 crash loop back off라고 해서 죽어버린다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;command : [&quot;/bin/sh&quot;, &quot;-c&quot;, &quot;sleep 3600&quot;] # 다음과 같은 명령어 사용함

k exec simple-command -it -- /bin/bash #들어가 봄 
nslookup kubernetes # 쿠버네티스 이름으로 리턴 ip 가 있음을 확인 
exit 
k delete pod simple-command 
&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;2000&quot; data-origin-height=&quot;903&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/urJGF/btsEEKdHBIP/sTMPs5n1K3Sq75aKWEiUAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/urJGF/btsEEKdHBIP/sTMPs5n1K3Sq75aKWEiUAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/urJGF/btsEEKdHBIP/sTMPs5n1K3Sq75aKWEiUAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FurJGF%2FbtsEEKdHBIP%2FsTMPs5n1K3Sq75aKWEiUAK%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;2000&quot; height=&quot;903&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;903&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;2000&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd10j5/btsEGkyk2jH/lzcUQomgAdcDDgb4pFoZY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd10j5/btsEGkyk2jH/lzcUQomgAdcDDgb4pFoZY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd10j5/btsEGkyk2jH/lzcUQomgAdcDDgb4pFoZY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd10j5%2FbtsEGkyk2jH%2FlzcUQomgAdcDDgb4pFoZY1%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;2000&quot; height=&quot;1022&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;command : [&quot;/bin/sh&quot; , &quot;-c&quot;, &quot;echo run multiple-command-v1 &amp;amp;&amp;amp; sleep 3600&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명령에 인자(args)를 추가하는 방법&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xAeHk/btsEEHg33w4/Ak8OspkJ01kA6kK8kQJ5K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xAeHk/btsEEHg33w4/Ak8OspkJ01kA6kK8kQJ5K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xAeHk/btsEEHg33w4/Ak8OspkJ01kA6kK8kQJ5K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxAeHk%2FbtsEEHg33w4%2FAk8OspkJ01kA6kK8kQJ5K1%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;2000&quot; height=&quot;679&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;679&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;잡(Job)&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1-job-curl-succ&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: batch/v1
kind: Job
metadata:
  name: job-curl-succ
spec:
  template:
    spec:
      containers:
      - name: net-tools
        image: sysnet4admin/net-tools
        command: [&quot;curlchk&quot;, &quot;nginx&quot;]
      restartPolicy: Never
&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;apiVersion: 일괄적으로 일어난다는 의미의&amp;nbsp;batch&lt;/li&gt;
&lt;li&gt;한 번에 여러 개의 잡을 순차적 또는 동시에 실행할 수 있음 ( 따라서 템플릿으로 감싸져 있음)&lt;/li&gt;
&lt;li&gt;restartPolicy: 지정하지 않을 경우 기본이&amp;nbsp;Always
&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;리소스를 더이상 쓰지 않고, 추후에 확인을 하고자 complete 상태를 유지하는 것. 따라서 never로 지정함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행하고 결과를 log로 확인할 목적으로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;k get po
k get svc
k logs ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;잡의 병렬 실행&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLFtK/btsEGhBB55Y/rW3ifcrvpKKQmHYmIyiLG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLFtK/btsEGhBB55Y/rW3ifcrvpKKQmHYmIyiLG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLFtK/btsEGhBB55Y/rW3ifcrvpKKQmHYmIyiLG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLFtK%2FbtsEGhBB55Y%2FrW3ifcrvpKKQmHYmIyiLG1%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;2000&quot; height=&quot;878&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;878&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;completions: 순차적 실행&lt;/li&gt;
&lt;li&gt;parallelism: 병렬 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;잡의 자동 종료&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KA8Tu/btsEFRwqPm8/xUCxNWVGHU4R0wcRtUMCS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KA8Tu/btsEFRwqPm8/xUCxNWVGHU4R0wcRtUMCS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KA8Tu/btsEFRwqPm8/xUCxNWVGHU4R0wcRtUMCS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKA8Tu%2FbtsEFRwqPm8%2FxUCxNWVGHU4R0wcRtUMCS1%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;2000&quot; height=&quot;974&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;974&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;activeDeadlineSeconds: 실행 후 완료 여부에 상관 없이 일정 시간 경과 시 종료&lt;/li&gt;
&lt;li&gt;ttlSecondsAfterFinished: TTL(Time To Live), 잡이 완료된 시점 이후 일정 시간 경과 시 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&lt;b&gt;크론잡(CronJob)&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기성을 가지고 실행되어야 할 내용이 있을 경우 사용하는 오브젝트&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;cronjob-1m-hist10-curl.yaml&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: batch/v1
kind: CronJob
metadata:
  name: cj-1m-hist3-curl
spec:
  schedule: &quot;*/1 * * * *&quot;
  successfulJobsHistoryLimit: 10
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: net-tools
            image: sysnet4admin/net-tools
            command: [&quot;curlchk&quot;, &quot;nginx&quot;]
          restartPolicy: Never
&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;successfulJobsHistoryLimit: 기본값은 3, 이 수치를 넘는 동작 완료된 크론잡은 삭제됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;크론(cron) 규칙&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u45Nr/btsEEYCUmhT/scWF4HoKC3JkUtStVoKIGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u45Nr/btsEEYCUmhT/scWF4HoKC3JkUtStVoKIGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u45Nr/btsEEYCUmhT/scWF4HoKC3JkUtStVoKIGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu45Nr%2FbtsEEYCUmhT%2FscWF4HoKC3JkUtStVoKIGk%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;2000&quot; height=&quot;547&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;데몬셋(DaemonSet)&lt;/b&gt;&lt;/h1&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;&lt;s&gt;kind는 대소문자 구분 잘해야한다.&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;daemonset.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k7h0d/btsEGhn4Dx5/enNvgXpR9RxaOcP3x2viNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k7h0d/btsEGhn4Dx5/enNvgXpR9RxaOcP3x2viNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k7h0d/btsEGhn4Dx5/enNvgXpR9RxaOcP3x2viNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk7h0d%2FbtsEGhn4Dx5%2FenNvgXpR9RxaOcP3x2viNk%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;2000&quot; height=&quot;1016&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1016&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;deployment 와 데몬 셋의 차이? 디플로이먼트는 배포 갯수를 지정해줘야 하지만 데몬셋은 워커 노드마다 1개씩만 배포되기 때문에&amp;nbsp;replicas&amp;nbsp;항목이 없음&lt;/li&gt;
&lt;li&gt;워커노드가 총 3개면 3개가 배포가 된다. 이거 이외에도 데몬셋이 있다.&lt;/li&gt;
&lt;li&gt;워커노드 하나 추가를 위해, 프로비저닝을 진행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vagrant up을 명시해주면 provisioning이 가능하다. 슈퍼푸티로 돌아가서 데몬셋 동작을 본다. 그러면 워커 노드가 하나 늘어나서 데몬셋 하나 더 추가된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데몬셋 노드 단위로 배포하기 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;k get nodes
k get po -o wide
k get daemonsets.apps -A 
k deletenode w4-k8s
k get node
k delte -f _Lecture/*...
k get po
vagrant destroy -f w4-k8s-1.22
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;b&gt;스테이트풀셋(StatefulSet)&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태를 유지하고 있는 파드&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;statefulset.yaml&lt;/b&gt;&lt;/h3&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;2000&quot; data-origin-height=&quot;853&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAXNVh/btsEEK5SoXt/DJrKzEcT2Qc9pdY2F1NPY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAXNVh/btsEEK5SoXt/DJrKzEcT2Qc9pdY2F1NPY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAXNVh/btsEEK5SoXt/DJrKzEcT2Qc9pdY2F1NPY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAXNVh%2FbtsEEK5SoXt%2FDJrKzEcT2Qc9pdY2F1NPY0%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;2000&quot; height=&quot;853&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;853&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;serviceName : 쿠버네티스에서 제공하는 서비스 오브젝트의 이름. &lt;b&gt;필수 값 (꼭 넣어야 함!!!!)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스테이트풀셋으로 배포할 경우 &lt;b&gt;해시값이 아닌 고정된 이름을 가짐&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;상태값을 유지해야한다. 정확한 순서를 알아야 한다.&lt;/li&gt;
&lt;li&gt;순서를 가지고 배포해야할 때, ELK 스택 이용할 때(? 이건 아직 잘 모르겠다. 나중에 찾아봐야 할듯) 쓴다.&lt;/li&gt;
&lt;li&gt;스케일 아웃해서 replicaSet 많이 만들면 어떻게 될까? &amp;rarr; 차례로 더 늘어나게 된다. 반대로 줄이면, 카운트 다운으로 세개로 줄어든다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>그림으로배우는쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/43</guid>
      <comments>https://worldi.tistory.com/43#entry43comment</comments>
      <pubDate>Sat, 10 Feb 2024 14:22:15 +0900</pubDate>
    </item>
    <item>
      <title>그림으로 배우는 쿠버네티스 CH2 쿠버네티스를 배우기 위한 사전 준비 작업</title>
      <link>https://worldi.tistory.com/42</link>
      <description>&lt;h1&gt;실습 버전 업그레이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습 버전을 1.2로 업시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://user-images.githubusercontent.com/52024566/156181043-6cdcf0af-7aaa-442d-b7a8-42c476fd442f.png&quot;&gt;https://user-images.githubusercontent.com/52024566/156181043-6cdcf0af-7aaa-442d-b7a8-42c476fd442f.png&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;혼동되는 용어 정리&lt;/h1&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;가 혼동되어 쓰인다.&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;즉 애플리케이션은 단일 컨테이너의 단위가 될 수도 있고, 복수의 컨테이너가 될 수도 있다. 또한 파드와 1 대1 맵핑될 수도 있고, 파드가 여러 개 모여서 동작하는 애플리케이션도 동작할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파드(Pod) : 쿠버네티스에서 생성하고 관리할 수 있는 배포 가능한 최소 컴퓨팅 단위&lt;/li&gt;
&lt;li&gt;컨테이너(Container) : 격리된 환경에서 실행되는 프로세스&lt;/li&gt;
&lt;li&gt;애플리케이션(Application) : 구조의 단위인 파드, 컨테이너와 달리 실제로 동작하는 기능의 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&lt;b&gt;자주 쓰이는 kubectl 옵션 명령어&lt;/b&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;get: 오브젝트 조회&lt;/li&gt;
&lt;li&gt;run,&amp;nbsp;create,&amp;nbsp;apply: 오브젝트 생성&lt;/li&gt;
&lt;li&gt;delete: 오브젝트 삭제&lt;/li&gt;
&lt;li&gt;exec: 파드 내부에 컨테이너로 접속&lt;/li&gt;
&lt;li&gt;scale: 파드 갯수를 늘리거나 줄임&lt;/li&gt;
&lt;li&gt;edit: 배포된 오브젝트를 수정&lt;/li&gt;
&lt;li&gt;-o yaml: 코드를 확인하는 명령어&lt;/li&gt;
&lt;li&gt;-dry-run=client: 코드를 생성하는 명령어&lt;/li&gt;
&lt;li&gt;events: 쿠버네티스 이벤트 확인&lt;/li&gt;
&lt;li&gt;describe: 배포된 오브젝트의 상태를 파악&lt;/li&gt;
&lt;li&gt;logs: 컨테이너의 로그 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;k run nginx --image=nginx
k create deploy nginx --image=nginx
k scale deployment nginx --replicas=3
k get po
k delete pod nginx-...
k apply -f ~/_~.yml

k exec apy-nginx-... -it -- /bin/bash // 터미널을 배쉬에 잡아서 파드에 들어가기
k edit deploy apy-nginx // 개수를 1개 줄일 수 있다. 
k delete deployments.apps nginx&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;h1&gt;코드 관련 명령어&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. yaml 형식으로 보여준다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl get pod nginx -o 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;2. 코드 생성 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;mdash;dry-run=client : 실행은 해보는데 실제로 적용이 안될 거야. 코드에 이상이 없는지 돌려 보는 확인 용도&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl run nginx --image=nginx  --dry-run=client -o yaml
&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;더 나아가 저장 kubectl run nginx --image=nginx --dry-run=client -o yalm &amp;gt; po-nginx.yaml&lt;/li&gt;
&lt;li&gt;k apply -f po-nginx.yml 로 실행할 수 있다.&lt;/li&gt;
&lt;li&gt;파드 말고 deployment 로 실행되는 것을 작성하고 싶다면,&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;k create deploy nginx &amp;mdash;image=nginx -o yaml &amp;mdash;dry-run=client &amp;gt; deploy-nginx.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;3. -o yaml는 주로 파드 정보 보지 않고 인그레스 같은 거 볼 때 사용한다.&lt;/p&gt;
&lt;h1&gt;쿠버네티스 이벤트 확인 (events)&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl get events : default 네임스페이스에 해당하는 이벤트들만 보인다.&lt;/li&gt;
&lt;li&gt;kubectl get events -n kube-system : 쿠버시스템 네임스페이스의 이벤트 들이 보인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;배포된 오브젝트의 상태를 파악 (describe)&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl describe pod nginx&lt;/li&gt;
&lt;li&gt;오브젝트에 연관된 모든 내용이 표시가 된다.&lt;/li&gt;
&lt;li&gt;하지만 pod 라고 생각되는 부분 안에 있는 컨테이너도 로그가 있는데, 그 부분은 describe 로 표시되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;컨테이너의 로그 확인 (logs)&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl logs nginx : 컨테이너의 로그를 확인 할 수 있다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  정리하자면, 이벤트는 네임스페이스 단위로 크게 보기 위해서 사용. 오브젝트에 관계되어 있는 이벤트, 에러 확인하기 위해서 많이 쓰이는 명령어는 describe 그리고 pod의 컨테이너에 자세하고 세부적인 내용은 확인하고 싶을때 , 비정상적인 동작 확인하고 싶을 때 log를 쓴다.&lt;/span&gt;&lt;/blockquote&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>그림으로배우는쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/42</guid>
      <comments>https://worldi.tistory.com/42#entry42comment</comments>
      <pubDate>Sun, 4 Feb 2024 19:18:42 +0900</pubDate>
    </item>
    <item>
      <title>BFF 패턴</title>
      <link>https://worldi.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 뭔가 Internal 서버의 정체를 알게되었는데, 아는 언니(멋있는 언니 완전 짱)가 자기 회사는 BFF패턴을 쓴다고 했던 게 생각이 났다. 우리 회사도 서버를 위한 서버를 두는 느낌으로 일맥상통하는 것 같다. 보통 회사들이 BFF 패턴을 많이 쓰는 것 같기도 하고 그런거 같아서, 찾아보니까 거의 다 쓴다.&lt;/p&gt;
&lt;h1&gt;BFF (Backend For Frontend)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에 표현될 데이터를 위한 백엔드 즉, 프론트엔드 데이터에 대한 책임을 백엔드가 가진다. 즉, BFF는 단순히 데이터를 제공하는 것에서 나아가 프론트엔드 친화적으로 데이터를 제공한다. 화면에 보이는 데이터를 가공하는 책임은 서버가 지고, 프론트엔드는 UI를 그리는데 집중한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zpvv1/btsDIy59dLL/ulQdmkf1Hg9q62U5NXN7jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zpvv1/btsDIy59dLL/ulQdmkf1Hg9q62U5NXN7jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zpvv1/btsDIy59dLL/ulQdmkf1Hg9q62U5NXN7jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzpvv1%2FbtsDIy59dLL%2FulQdmkf1Hg9q62U5NXN7jK%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;2000&quot; height=&quot;1122&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여러 조건에 대한 처리는 서버가 처리하고 프론트엔드에 보일 데이터만 제공하는 것이 BFF의 역할이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BFF 의 장점&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;복잡도 관점에서, 프론트가 UI 로직에만 집중할 수 있다. &amp;rarr; 캡슐화 및 유지보수에 좋다.&lt;/li&gt;
&lt;li&gt;Client 별 필요한 데이터 전달 가능(여러 API 호출에 대한 응답 수정, 조합)&lt;/li&gt;
&lt;li&gt;서비스 별 API 호출을 최소화 처리&lt;/li&gt;
&lt;li&gt;MSA 환경에서 API Endpoint 분리 시, Client 에 변경없이 처리 가능&lt;/li&gt;
&lt;li&gt;Browser 의 CORS 또는 인증 방식 등과 같은 공통 부분 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BFF 의 단점&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;코드 중복이나 재 작성을 해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;만약 BFF 패턴이 없다면?&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnh9HI/btsDIzjJ3ky/Xstnf1ciMAMWuf7Q5JH0t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnh9HI/btsDIzjJ3ky/Xstnf1ciMAMWuf7Q5JH0t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnh9HI/btsDIzjJ3ky/Xstnf1ciMAMWuf7Q5JH0t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcnh9HI%2FbtsDIzjJ3ky%2FXstnf1ciMAMWuf7Q5JH0t1%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;2000&quot; height=&quot;1549&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드가 각각의 서비스 마다 인증 + 네트워크 비용이 발생한다. &amp;rarr; 성능 저하&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;2000&quot; data-origin-height=&quot;1377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKrK74/btsDKKEyYZw/6AY46cOdbhqr8pfkTPNIa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKrK74/btsDKKEyYZw/6AY46cOdbhqr8pfkTPNIa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKrK74/btsDKKEyYZw/6AY46cOdbhqr8pfkTPNIa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKrK74%2FbtsDKKEyYZw%2F6AY46cOdbhqr8pfkTPNIa1%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;2000&quot; height=&quot;1377&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드는 BFF서버에게 궁극적으로 한번만 호출하고, 방화벽 내부에서 내부 통신하기 때문에 빠르다.&lt;/p&gt;
&lt;h1&gt;주의 할 점&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BFF가 비동기로 동작해야 한다는 점이다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;non-blocking &amp;amp; 비동기로 동작을 구현하기 위해 Spring webflux를 이용할 수 있다.&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;2000&quot; data-origin-height=&quot;1057&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccQfwL/btsDJhiLjLO/Lw0jUjnRs4lE6JRy0puXz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccQfwL/btsDJhiLjLO/Lw0jUjnRs4lE6JRy0puXz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccQfwL/btsDJhiLjLO/Lw0jUjnRs4lE6JRy0puXz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccQfwL%2FbtsDJhiLjLO%2FLw0jUjnRs4lE6JRy0puXz1%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;2000&quot; height=&quot;1057&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1057&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 전파를 Circuit breaker 를 통해 대비해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림과 같이 MYDATA관리 서비스가 고장나면, 2,3번이 연달아 되지 않는다. 따라서 장애 전파를 차단하는 것이 중요하다. 이와 관련된 circuit breaker 구현법은 나중에 필요할 때 찾아볼 예정이다. (참고자료에 1번째 자료)&lt;/p&gt;
&lt;h1&gt;참고 자료&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/finda-tech/금융서비스-msa-전환기-bff-와-circuitbreaker-적용-2편-c409e5fb28c9&quot;&gt;https://medium.com/finda-tech/금융서비스-msa-전환기-bff-와-circuitbreaker-적용-2편-c409e5fb28c9&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/bff_webflux_coroutine/&quot;&gt;https://tech.kakaopay.com/post/bff_webflux_coroutine/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/40</guid>
      <comments>https://worldi.tistory.com/40#entry40comment</comments>
      <pubDate>Mon, 22 Jan 2024 02:58:28 +0900</pubDate>
    </item>
    <item>
      <title>인수테스트와 시스템 테스트</title>
      <link>https://worldi.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;난 처음에 인수 테스트 = API 테스트 인 줄 알았다. 근데, 사수님이 인수테스트는 다양하게 쓰인다고, 실제 유저가 하는 테스트가 인수테스트에 가깝고 내가 알고 있던 인수 테스트는 시스템 테스트에 가깝다고 말씀 주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미가 궁금해져서 이를 찾아보게 되었다.&lt;/p&gt;
&lt;h1&gt;소프트웨어 개발 단계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발 단계에 따른&amp;nbsp;&lt;a href=&quot;https://terms.naver.com/entry.naver?docId=3532860&amp;amp;ref=y&quot;&gt;테스트&lt;/a&gt;는 소프트웨어 개발 단계의 순서와 짝을 이루어 테스트를 진행해나가는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 생각했던 시스템 테스트가 내가 RestAssured로 개발했던 테스트이고, 인수 테스트는 실제 유저가 원하는 대로 잘 동작하는지 보는 테스트이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEV2uf/btsDGLSwtUn/fqhsW9UixWL0GdJprTFgs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEV2uf/btsDGLSwtUn/fqhsW9UixWL0GdJprTFgs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEV2uf/btsDGLSwtUn/fqhsW9UixWL0GdJprTFgs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEV2uf%2FbtsDGLSwtUn%2FfqhsW9UixWL0GdJprTFgs1%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;2000&quot; height=&quot;1185&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1185&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;/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;인수 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템이 예상대로 동작 하는지 확인하고, 요구 사항에 맞는지 확신하기 위해 하는 &lt;a href=&quot;https://terms.naver.com/entry.naver?docId=3532860&amp;amp;ref=y&quot;&gt;테스트&lt;/a&gt;이다. 그래서 시스템을 인수하기 전 요구 분석 명세서에 명시된 대로 모두 충족시키는지를 사용자가 테스트한다. &lt;b&gt;인수 테스트가 끝나면 사용자는 인수를 승낙한 것이다.&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;알파 테스트, 베타 테스트&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;h1&gt;참고 자료&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://terms.naver.com/entry.naver?docId=3533036&amp;amp;cid=58528&amp;amp;categoryId=58528&quot;&gt;https://terms.naver.com/entry.naver?docId=3533036&amp;amp;cid=58528&amp;amp;categoryId=58528&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://terms.naver.com/entry.naver?docId=3533039&amp;amp;cid=58528&amp;amp;categoryId=58528&quot;&gt;https://terms.naver.com/entry.naver?docId=3533039&amp;amp;cid=58528&amp;amp;categoryId=58528&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발 관련 일지</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/39</guid>
      <comments>https://worldi.tistory.com/39#entry39comment</comments>
      <pubDate>Mon, 22 Jan 2024 02:40:40 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH7 강의 마무리</title>
      <link>https://worldi.tistory.com/38</link>
      <description>&lt;h1&gt;CH7 강의 끝!&lt;/h1&gt;
&lt;h1&gt;쿠버네티스 공부 방향성&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;document를 본다.&lt;/li&gt;
&lt;li&gt;쿠버네티스의 dns 정보 조회한다. (Domain Name System 정보 조회)&lt;/li&gt;
&lt;li&gt;kubenetes dns query 검색&lt;/li&gt;
&lt;li&gt;결론은 뭐 랩과 공식문서를 보라..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후속강의 &amp;ldquo;그림으로 배우는 쿠버네티스&amp;rdquo; 이당&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;li&gt;쿠버네티스의 요소 별 구성 및 관리법&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게 시작하는 쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/38</guid>
      <comments>https://worldi.tistory.com/38#entry38comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:16:10 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH6 쿠버네티스 Tips</title>
      <link>https://worldi.tistory.com/37</link>
      <description>&lt;h1&gt;kubectl 쉽게 쓰는 법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배시 자동 완성 : k만 입력하면 kubectl을 쉽게 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;k &amp;lt;tab&amp;gt; &amp;lt;tab&amp;gt; ..&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;배쉬 셸에 별명 지어 주기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alias k=kubectl&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alias ka = &amp;lsquo; kubectl apply -f &amp;lsquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alias keq = &amp;lsquo; kubectl exec &amp;hellip;&amp;rsquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같이 지어줄 수 있다.&lt;/p&gt;
&lt;h1&gt;쿠버네티스 업그레이드 버전&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업그레이드 계획을 수립한다 &amp;rarr; kudeadm 업그레이드 &amp;rarr; kubelet 업그레이드 &amp;rarr; 업그레이드 완료를 확인한다.&lt;/li&gt;
&lt;li&gt;그냥 대충 보고 나중에 다시 찾아봐야 겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;kubectl get nodes
kubeadm upgrade plan
yum upgrade kuberadm-1.25.1 -y // kubeadm 업그레이드 
kubeadm version
kubeadm upgrade apply 1.25.1 
kubelet --version // 이거도 업데이트 해줘야 함

yum upgrade kubelet-1.25.1 -y
kubelet --version 

systemctl restart kubelet // restart
systemctl daemon-reload 

k get nodes // 비로소 1.25.1 노드가 된다. 

kuberadm upgrade node

// 워커 노드 3개 모두 적용한다. 
// 모두 1.25.1로 된다. -&amp;gt; 강의 8.9에서 앤서블을 통해서 자동화 할 수 있다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;오브젝트 예약 단축어&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;935&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yEtUw/btsDGIg3Eug/aBKKtutUKjmUBuOfpkU1oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yEtUw/btsDGIg3Eug/aBKKtutUKjmUBuOfpkU1oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yEtUw/btsDGIg3Eug/aBKKtutUKjmUBuOfpkU1oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyEtUw%2FbtsDGIg3Eug%2FaBKKtutUKjmUBuOfpkU1oK%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;2000&quot; height=&quot;935&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;935&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;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;k get po
k get deploy
k get nodes 
k get no 
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게 시작하는 쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/37</guid>
      <comments>https://worldi.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:13:59 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH5 쿠버네티스 오브젝트</title>
      <link>https://worldi.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api, etcd, c-m, shed, k-proxy 등이 있다.&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;2000&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/laLS5/btsDNq6zcLh/XmxGZTAUWj6boDjfGuep11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/laLS5/btsDNq6zcLh/XmxGZTAUWj6boDjfGuep11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/laLS5/btsDNq6zcLh/XmxGZTAUWj6boDjfGuep11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlaLS5%2FbtsDNq6zcLh%2FXmxGZTAUWj6boDjfGuep11%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;2000&quot; height=&quot;1076&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트는 추구하는 상태를 기술해 둔 것이다.&lt;/p&gt;
&lt;/blockquote&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;추구하는 상태(spec)과 status를 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;kubectl get pods 
kubectl edit deployment del-deploy // 상태 편집 
// replicas = 9에서 3 으로 변경한다.
// 파드가 3으로 줄어든다. 추구하는 상태 -&amp;gt; 현재 상태랑 맞아 들어간다. 
kubectl get pods  // 3개 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;쿠버네티스의 기본 오브젝트&lt;/h1&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;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;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;이전 강의에서 노드 포트, 로드밸런서로 만들었었음&lt;/li&gt;
&lt;/ul&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;default, kube-system &amp;hellip; 여러 스페이스 존재한다.&lt;/li&gt;
&lt;li&gt;pod, svc, deploy 배포 가능.&lt;/li&gt;
&lt;/ul&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;볼륨은 영속성을 갖고 있다. 영속적인 데이터&lt;/li&gt;
&lt;li&gt;pod는 언제든지 삭제, 생성된다.&lt;/li&gt;
&lt;li&gt;파드를 만들 때 볼륨을 붙여야 한다. &amp;rarr; 데이터 저장소가 옮겨다니면 안되기 때문에.&lt;/li&gt;
&lt;li&gt;nfs 시스템을 구현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1705839022155&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;~/_Lecture_k8s/5.2/nfs-exporter.sh log // 인자를 log로 고정 입력 
cat /etc/exports // 마스터 서버 워커 노드 전부 볼 수 있는 nfs-shared 만들어 졌다.
cat ~/_Lecture_k8s/ch5/5.2/dpy....yml // nfs 볼륨 구성 되어있음을 확인 가능함.
kubectl apply -f ~/_Lecture.../dpy-chk-log.yaml // 접속 기록 저장하는 역할함 
kubectl get pods 
ls audit 

kubectl delete -f ~/_Lecture.../dpy-chk-log.yaml 
kubectl get pods 

kubectl apply -f ~/_Lecture.../dpy-chk-log.yaml // 볼륨 가지고 있는 파드 생성
// 기존 접속 기록 유지하고 있는지 확인한다. 
kubectl exec dpy-chk-log-658658f78c... -it --/bin/bash 
ls /audit // 접속 기록 그대로 남아 있는 것을 확인할 수 있다.&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/36</guid>
      <comments>https://worldi.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:11:11 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH4 문제를 통해 배우는 쿠버네티스</title>
      <link>https://worldi.tistory.com/35</link>
      <description>&lt;h1&gt;쿠버네티스 파드에 문제가 생겼다면 (GKE에서 실습 됨)&lt;/h1&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;파드만 배포된 경우, 문제가 생긴다. 파드는 단일 객체이다.&lt;/li&gt;
&lt;li&gt;디플로이먼트 형태로 배포된 파드는 괜찮다. 파드가 지워지게 되면 파드를 다시 만든다.&lt;/li&gt;
&lt;li&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;pre class=&quot;typescript&quot;&gt;&lt;code&gt;kubectl apply -f ~/_Lecture_k8s_starter.kit/ch4/4.1 // 두개의 yaml에 대한 deployment 만든다.
kubectl get pods
kubectl delete pod del-pod //파드를 지운다.
kubectl get pods // 파드가 지워진다. 
kubectl delete pod del-deploy-574f..~ // 배포의 파드를 지운다.
kubectl get pods // 파드가 새로운 것이 생성된다. 
kubectl delete deployment del-deploy //deployment 지운다. 
kubectl get pods // 다른거 생성한다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;쿠버네티스 워커 노드의 구성요소에 문제가 생겼다면 (GKE에서 실습 X)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;aside&amp;gt;   Google Kubernetes Engine (GKE)에서는 개별 노드에 대한 저수준 접근 (예: **systemctl**을 사용하여 서비스를 중지)이 제한되어 있습니다. GKE는 완전 관리형 쿠버네티스 서비스로, 노드 관리 및 유지보수 작업은 GKE에 의해 자동으로 처리됩니다. 이는 사용자가 직접 &lt;b&gt;kubelet&lt;/b&gt; 프로세스를 중지하거나 관리할 필요가 없다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;/aside&amp;gt;&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;&lt;b&gt;API 그대로 안 가져오는 곳은 kubelet이다&lt;/b&gt;. 따라서 문제가 생기는 경우, 바로 복구가 되지 않는다. 즉 제대로 배포가 이루어 지지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;systemctl stop kubelet // 쿠버랫 종료
// 배포한다. 
kubectl get pods // 파드 확인
kubectl get pods -o wide // w1-k8s 는 배포가 이루어지지 않는다.
systemctl start kubelet // 쿠버랫 살린다.
kubectl get pods -o wide -w // w1-k8s 는 배포가 된다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너 런타임(ContainerD) 중단이 되면, 해당 워커 노드에 배포할 수 없다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파드 세 개가 배포 되어 있는 상황&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;systemctl stop containerd // 중지
systemctl status containerd // 상태 확인 -&amp;gt; inactive 상태이다. 
kubectl scale deployment del-deploy --replicas=6 
kubectl get pods -o wide // 1번 런타임 종료 했기 때문에 워커 노드 1번으로는 배포되지 않는다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 워커노드 1번은 어떻게 되나? 컨테이너 런타임 종료한 이후로 5분 정도 지나면 evict라고 해서, 런타임 동작하지 않는 것을 인식하여 다른 곳으로 옮기게 된다. 중단되면, 파드를 해당 워커 노드에 배포할 수 었다.&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 class=&quot;livecodeserver&quot;&gt;&lt;code&gt;kubectl scale deployment del-deploy --relicas=9 
// 9로 바꾸어주게 되면, 가능한 균등하게 스케줄러가 배포하려고 할 것이다.
kubectl get pods -o wide // (가능한!) 균등하게 배포한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;쿠버 네티스 마스터 노드의 구성 요소에 문제가 생겼다면, (GKE에서 실습 X)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스케줄러를 삭제한다면, 삭제 되면 다시 생성된다.&lt;/h2&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;kubectl get pods -n kube-system
kubectl get pods -n kube-system - wide // 스케줄러 이름 알수있다.(master에 있음..)
kubectl delete pod kube-scheduler-m-k8s -n kube-system // 네임스페이스를 지정하고, 스케줄러를 삭제함. 
&lt;/code&gt;&lt;/pre&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;2000&quot; data-origin-height=&quot;1732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k2zFT/btsDLITWloJ/kkk6XKI3zhWhY5IQPw0CR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k2zFT/btsDLITWloJ/kkk6XKI3zhWhY5IQPw0CR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k2zFT/btsDLITWloJ/kkk6XKI3zhWhY5IQPw0CR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk2zFT%2FbtsDLITWloJ%2Fkkk6XKI3zhWhY5IQPw0CR0%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;2000&quot; height=&quot;1732&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1732&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;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;systemctl stop kubelet 

kubectl delete pod kube-scheduler-m-k8s -n kube-system
kubectl get pods -n kube-system
// 삭제 수행 되지 않는다. 
// 워커 노드에 애플리케이션 제대로 배포할 수 있을지? 의문이 든다. 
kubectl create deployment nginx --image=nginx // 정상적으로 생성된다. 

// 생성은 되는데 스케일이 안되지 않을까?
kubectl scale deployment nginx --replicas=3 // 스케일 된다. -&amp;gt; 스케줄러 제대로 동작
kubectl get pods 

// 스케줄러는 terminating이라고 나와있지만, 쿠버렛을 통해 전달되지 않아서 문제가 생기지 않는다. 
systemctl start kubelet // 쿠버렛을 살린다. 
kubectl get pods -n kube-system -w // 스케줄러가 다시 Running 된다.  
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubelet 중단되면 마스터 노드 영향받지 않고, 애플리케이션 배포 되고, 워커 노드도 영향을 받지 않는다.&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;마스터 노드에 있는 컨테이너 런타임 d를 중단해 보고 동작을 확인한다.&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1.2 도커에서 동작이 좀 차이가 있다. (vs 컨테이너d) 컨테이너d가 도커보다 훨씬 안정적임.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;systemctl stop containerd // 컨테이너 d를 삭제한다. inactive 
kubectl delete deployment nginx // 지운다.
kubectl get pods //지워졌음을 확인한다.

kubectl delete deployment nginx
kubectl get pods 

kubectl get pods -n kube-system // 소속되어 있는 애들 중에, 
kubectl delete pod kube-scheduler-m-k8s -n kube-system // 이렇게 한다면, 컨테이너 런타임 
// 이 중단되었기 때문에 전달하지도, 받지도 않는다. 
// 다시 containerd를 시작시키면, termination -&amp;gt; 다시 올리도록 한다.

kubectl create deployment nginx --image=nginx
kubectl get pods -n kube-system
&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;쿠버네티스 컨테이너 런타임이 컨테이너 d로 변경되면서 시스템 더 안정화 되고, 조회, 워커 노드에 있는 deployement 지우고 생성하는 것도 가능하다.&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;컨테이너d가 문제가 있음에도, 상태를 유지하고 있으니 nginx 지우는 것이 가능하다.&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;rarr; 마스터 노드를 안정적으로 구성하는 것이 올바른 방법이다.&lt;/p&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게 시작하는 쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/35</guid>
      <comments>https://worldi.tistory.com/35#entry35comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:05:57 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH3 쿠버네티스 인사이드</title>
      <link>https://worldi.tistory.com/34</link>
      <description>&lt;h1&gt;쿠버네티스 이루는 것들&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구역을 나누는 네임 스페이스 (Namespace)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default, kube-system, metal-system : 호실로 생각하면 된다. 서로의 구역이 나뉘어 져있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;kubectl get pods -n kube-system
&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eks에서도 쿠버네티스 구성 노드가 있는 것을 확인할 수 있다. 이는 aks나 gke도 마찬가지이다. 3사에서 차이 많이 있다. 그런 구성 요소들을 보여주지 않는다. 숨겨져 있는 마스터 노드가 관리하고 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;쿠버네티스 기본 철학&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA로 구성되어 있다. 하는 일들이 개별적으로 나뉘어져 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 서버는 움직이지 않는다. 모든 것을 감시만 한다. 모든 상태 값의 중심에 있다.&lt;/li&gt;
&lt;li&gt;Controller Manager가 파드를 생성한다.&lt;/li&gt;
&lt;li&gt;스케줄러 또한 새로운 파드를 워커 노드에 넣는지 스케줄링 한다.&lt;/li&gt;
&lt;li&gt;kubelet은 컨테이너 런타임을 통해서 파드의 동작관리를 하고, 컨테이너를 생성한다.&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;2000&quot; data-origin-height=&quot;1013&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7IOxW/btsDJ0HyDDs/kBNOoK3fO0E3BqNzM2WauK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7IOxW/btsDJ0HyDDs/kBNOoK3fO0E3BqNzM2WauK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7IOxW/btsDJ0HyDDs/kBNOoK3fO0E3BqNzM2WauK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7IOxW%2FbtsDJ0HyDDs%2FkBNOoK3fO0E3BqNzM2WauK%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;2000&quot; height=&quot;1013&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1013&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언적인 시스템 : 추구하는 상태와 현재 상태를 맞추려고 한다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;api 서버와 etcd : etcd는 매번 api 서버에 업데이트 되었음을 알린다. API 서버는 매번 클러스터의 업데이트된 정보를 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 쿠버네티스의 파드 배포 흐름&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;관리자가 kubectl을 친다.&lt;/li&gt;
&lt;li&gt;API 서버는 etcd 서버와 거의 1대1 동기화 되어있다.&lt;/li&gt;
&lt;li&gt;컨트롤러 매니저&lt;/li&gt;
&lt;li&gt;스케줄러&lt;/li&gt;
&lt;li&gt;컨테이너 런타임이 파드를 생성하는 구조&lt;/li&gt;
&lt;li&gt;쿠버 프록시를 통해서 사용자가 통신한다. (calico를 사용)&lt;/li&gt;
&lt;/ol&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;&lt;b&gt;즉 모든 것은 api가 중심에 있다. 모든 것의 시작이자 끝이다&amp;hellip;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게 시작하는 쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/34</guid>
      <comments>https://worldi.tistory.com/34#entry34comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:04:02 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH2 배포를 통한 쿠버네티스 체험</title>
      <link>https://worldi.tistory.com/33</link>
      <description>&lt;h1&gt;애플리케이션 배포 (NGINX)&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스터 노드 실제로 마스터 노드에 kubectl 명령어 설치함. 워커 노드에 애플리케이션을 배포&lt;/li&gt;
&lt;li&gt;애플리케이션의 단위는 파드이다. 이는 컨테이너의 집합을 말한다.&lt;/li&gt;
&lt;li&gt;파드 배포를 실습한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;kubectl run nginx --image=nginx
kubectl get pod //  이미지 생성된 것을 확인
kubectl get pod -o wide // ip 확인
curl 172.16.132.1
&lt;/code&gt;&lt;/pre&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;외부에서 접속하려면 어떻게 해야하지?&lt;/li&gt;
&lt;li&gt;호스트 환경에서 172대의 ip로 접속해?&lt;/li&gt;
&lt;li&gt;ping과 curl 명령어로 확인해 보면, 접속이 불가능한 것을 알 수 있다. 즉, 쿠버네티스 클러스터가 다음과 같은 문에 둘러싸여 있다. 문 통과해야지 외부와 통과할 수 있다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&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;2000&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHd8Tq/btsDLL4cniz/v2GB5Rbt18FH01nNBAkb01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHd8Tq/btsDLL4cniz/v2GB5Rbt18FH01nNBAkb01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHd8Tq/btsDLL4cniz/v2GB5Rbt18FH01nNBAkb01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHd8Tq%2FbtsDLL4cniz%2Fv2GB5Rbt18FH01nNBAkb01%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;2000&quot; height=&quot;929&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;929&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프록시 구조 날려버린다. &amp;rarr; 문이 없어져서 보안 없어진다. (문제점임)&lt;/li&gt;
&lt;li&gt;문 밖에 안전한 구역을 만들어 놓는다. 이를 서비스라고 한다. (=svc) 서비스를 통해서 안전하게 찾아 갈 수 있다. 정확하게 말하자면, 노드에 접속하는 것이지, 파드에 직접 연결 되는 구조는 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;외부와 연결되는 pod 실습하기&lt;/h1&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;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl expose pod nginx --type=NodePort --port=80
&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;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl get service
&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;밖으로 노출되는 포트가 32630인 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl get nodes -o wide 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해, 노드에 대한 ip정보가 보여지게 된다. 외부에 노출되는 ip가 보여진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포트 32630을 통해서 해당 부분이 노출되어있다.&lt;/li&gt;
&lt;li&gt;192.168.1.101:32630 &amp;rarr; nginx가 보여지게 된다. (강의에서)&lt;/li&gt;
&lt;li&gt;다음과 같이, 각각 node 마다 외부 IP를 통해 들어갈 수 있다.&lt;/li&gt;
&lt;li&gt;내 실습&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;2000&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d31APP/btsDNqZNyX0/32Zv4VhutSKZCn99l6pQBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d31APP/btsDNqZNyX0/32Zv4VhutSKZCn99l6pQBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d31APP/btsDNqZNyX0/32Zv4VhutSKZCn99l6pQBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd31APP%2FbtsDNqZNyX0%2F32Zv4VhutSKZCn99l6pQBK%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;2000&quot; height=&quot;725&quot; data-origin-width=&quot;2000&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;&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;2000&quot; data-origin-height=&quot;827&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3IZLI/btsDNqS1OAX/bkIaWUvJX2ssxbwLBiko9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3IZLI/btsDNqS1OAX/bkIaWUvJX2ssxbwLBiko9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3IZLI/btsDNqS1OAX/bkIaWUvJX2ssxbwLBiko9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3IZLI%2FbtsDNqS1OAX%2FbkIaWUvJX2ssxbwLBiko9K%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;2000&quot; height=&quot;827&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이거 안된다면, GKE 의 방화벽을 추가해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP -&amp;gt; VPC 네트워크 -&amp;gt; 방화벽 -&amp;gt;방화벽 규칙 새로 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위를 default(1000)값 보다 작게 설정한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 범위를 0.0.0.0/0 (필터 안함) 으로 한다 .&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;h1&gt;파드의 한계를 극복하는 디플로이먼트&lt;/h1&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;파드는 한개만 존재, deployment는 파드가 여러 개가 모여있는 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;kubectl run // pod 배포 가능하고, deployment는 배포할 수가 없다. 
kubectl create // deployment 배포 가능 
kubectl apply // pod, deployment 배포할 수 있다. 파일을 통해서 배포한다. 
&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;deploy-nginx라는 이름으로 deployment가 배포된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;kubectl create deployment deploy-nginx --image=nginx
kubectl get pods // 파드 확인해 봄
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Deployment로 다수의 파드를 배포한다면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReplicaSet을 통해서, 파드가 늘어날 수 있다. 현재 default 값은 1이고, 이를 늘려주게 되면 그에 맞게 파드가 늘어난다.&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;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;kubectl scale deployment deploy-nginx --replicas=3
kubectl get pods // 여러 개 파드가 늘어난 것을 확인할 수 있다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Deployment를 서비스로 노출하기&lt;/h2&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;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl expose deployment deploy-nginx --type=NodePort --port=80
kubectl get services
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deployment를 노드 포트로 노출하게 되면 노드의 IP를 알아야 한다. IP를 알려주는 건 부담스럽다.&lt;/li&gt;
&lt;li&gt;따라서, 가장 좋은 방법은 로드밸런서로 선언하는 것이 가장 좋은 방법이다. 강의에서는 로드밸런서 MetalLB를 통해 선언해서 보여준다.&lt;/li&gt;
&lt;li&gt;chk-hn 라는 이미지를 Deploy하고, 이를 로드밸런스로 노출해 본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;노드포트보다 로드 밸런서가 좋은점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드 밸런서는 virtual IP이다. 따라서 노드의 실제 IP를 쓰지 않아도 된다.&lt;/li&gt;
&lt;li&gt;또한 로드 밸런서는 가야할 경로를 최적화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디플로이먼트를 로드밸런서로 배포&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;chk-hn을 배포하기 전에, MetalLB를 설치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;kubectl apply -f ~/_Lecture_k8s_starter.kit/ch2/2.4/metallb.yaml // 다운로드가 있는 폴더로 간다. 
kubectl create deployment chk-hn --image=sysnet4admin/chk-hn // 이미지 이름 동일하게 한다.
//  개인이 가지고 있는 이미지는 레지스트리 이름을 입력해야한다. 
kubectl scale deployment chk-hn --replicas=3 
// 로드 밸런서로 노출한다.
kubectl expose deployment chk-hn --type=LoadBalancer --port=80
kubectl get services // 서비스 확인, ip가 익명으로 되어 있다.
kubectl get pods -o wide // 실제 존재하는지 확인한다.
&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;컨테이너 애플리케이션을 모아 놓는 것은 : chk-hn와 같은 이미지&lt;/li&gt;
&lt;li&gt;deployment: 파드 모아 놓은 것&lt;/li&gt;
&lt;li&gt;쿠버네티스에서 서비스란 문같은 역할로, 이를 로드 밸런서로 설정한다. 서비스는 항상 다른 곳을 거치는 곳이라고 생각하면 된다. 즉, 어떤 곳을 넘어가기 위해 거쳐야 하는 곳&lt;/li&gt;
&lt;/ul&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;pod, service, deployment 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;kubectl delete service chk-hn //서비스 delete 하는 명령어
kubectl get service
kubectl delete service nginx  // 서비스를 삭제한다. 
kubectl delete deployment chk-hn // deployment delete
kubectl delete deployment deploy-nginx 
kubectl delete pod nginx
kubectl get pods // 파드 확인
kubectl delete -f ~/_Lecture_k8s_starter.kit/ch2/2.4/metallb.yaml // 파일로 지울 수 있다.
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게 시작하는 쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/33</guid>
      <comments>https://worldi.tistory.com/33#entry33comment</comments>
      <pubDate>Sun, 21 Jan 2024 21:02:11 +0900</pubDate>
    </item>
    <item>
      <title>쉽게 시작하는 쿠버네티스 CH1 쿠버네티스 환경 구성</title>
      <link>https://worldi.tistory.com/32</link>
      <description>&lt;h1&gt;도커&lt;/h1&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMv6fu/btsDKPshl3G/MqYEENVPtcSjuVedipd26K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMv6fu/btsDKPshl3G/MqYEENVPtcSjuVedipd26K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMv6fu/btsDKPshl3G/MqYEENVPtcSjuVedipd26K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMv6fu%2FbtsDKPshl3G%2FMqYEENVPtcSjuVedipd26K%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;2000&quot; height=&quot;1160&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;쿠버 네티스&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글의 보그 시스템을 CNCD 재단에다가 기부했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버 네티스가 CNCF 에서 관리 되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벤더의 종속성이 없다. &amp;rarr; 의존성이 생긴다. (Vender-Neutral)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 소스 프로젝트 &amp;rarr; 리눅스가 대표적인 시스템. 동일한 형태를 띠는 게 쿠버네티스이다.&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;관리형 쿠버네티스. : 많은 부분 관리할 필요가 없다.&lt;/li&gt;
&lt;li&gt;설치형 쿠버네티스: 랜처나 오픈 쉬프트 &amp;rarr; 이미 패키지화 되어있다. (배포용)&lt;/li&gt;
&lt;li&gt;구성형 쿠버네티스 : 베어메탈 등 여러 가지 요구사항에 맞지 않고, 자유롭게 요구사항 하고 싶거나 교육용 정도. kubeadm을 선택 (이번 강좌에서)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 플레이 쿠버네티스, 쿠버네티스 플레이 그라운드.&lt;/p&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;h1&gt;쿠버네티스 랩 환경 구성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 강의에서는 vagrant로 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워커노드 : 3기가, 마스터 노드 : 1.7 기가로 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vagrant가 디폴트로 선호하는 도구가 버츄얼 박스이다. 구현한 코드가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vagrant, virtual 박스를 설치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew tap hashicorp/tap
brew install hashicorp/tap/hashicorp-vagrant
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://virtualbox.org/wiki/Download_Old_Builds_7_0&quot;&gt;https://virtualbox.org/wiki/Download_Old_Builds_7_0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1705832704955&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;Oracle VM VirtualBox&quot; data-og-description=&quot;Welcome to VirtualBox.org! News Flash New January 16th, 2024VirtualBox 7.0.14 released! Oracle today released a 7.0 maintenance release which improves stability and fixes regressions. See the Changelog for details. New January 16th, 2024VirtualBox 6.1.50 r&quot; data-og-host=&quot;www.virtualbox.org&quot; data-og-source-url=&quot;https://virtualbox.org/wiki/Download_Old_Builds_7_0&quot; data-og-url=&quot;https://www.virtualbox.org/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://virtualbox.org/wiki/Download_Old_Builds_7_0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://virtualbox.org/wiki/Download_Old_Builds_7_0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Oracle VM VirtualBox&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome to VirtualBox.org! News Flash New January 16th, 2024VirtualBox 7.0.14 released! Oracle today released a 7.0 maintenance release which improves stability and fixes regressions. See the Changelog for details. New January 16th, 2024VirtualBox 6.1.50 r&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.virtualbox.org&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;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git clone -b v1.20 https://github.com/sysnet4admin/_Lecture_k8s_starter.kit.git&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;근데 나는 m3여서 저거 설치가 안되서, 그냥 GKE에서 동작과정만 확인하기로 했다.&lt;/p&gt;</description>
      <category>개발 관련 일지/k8s</category>
      <category>쉽게시작하는쿠버네티스</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/32</guid>
      <comments>https://worldi.tistory.com/32#entry32comment</comments>
      <pubDate>Sun, 21 Jan 2024 19:27:56 +0900</pubDate>
    </item>
    <item>
      <title>12장 아키텍처 스타일 결정하기</title>
      <link>https://worldi.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;언제 육각형 아키텍처를 사용해야 할까? 언제 계층형 아키텍처 스타일을 사용할까?&lt;/p&gt;
&lt;h1&gt;도메인이 왕이다.&lt;/h1&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;외부의 영향을 받지 않고 도메인 코드를 자유롭게 발전시킬 수 있다는 것은 육각형 아키텍처 스타일이 내세우는 가장 중요한 가치다.&lt;/p&gt;
&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;h1&gt;경험이 여왕이다.&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 아키텍처 스타일을 작은 모듈부터 시도해보자! 개념에 익숙해지고, 스타일에 익숙해져라.&lt;/p&gt;
&lt;h1&gt;그때그때 다르다.&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응.. 말그대로, 아키텍처 스타일은 그때그때 달라.. 소프트웨어의 종류, 도메인 코드의 역할, 팀의 경험에 따라서도 다르다.&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/31</guid>
      <comments>https://worldi.tistory.com/31#entry31comment</comments>
      <pubDate>Wed, 10 Jan 2024 00:55:04 +0900</pubDate>
    </item>
    <item>
      <title>11장 의식적으로 지름길 사용하기</title>
      <link>https://worldi.tistory.com/30</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어는 하드웨어에 비해 쉽게 변경할 수 있기 때문에, 어떤 떄는 지름길을 먼저 취하고 나중에 고치는 것이 더 경제적일 수 있다.&lt;/p&gt;
&lt;h1&gt;지름길은 깨진 창문과 같다.&lt;/h1&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;rarr; 레거시 코드 일 수록, 코드 품질이 낮아진다.&lt;/p&gt;
&lt;h1&gt;깨끗한 상태로 시작할 책임&lt;/h1&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;h1&gt;유스케이스 간 모델 공유하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유스케이스 간에 입출력 모델을 공유하게 되면 유스케이스들 사이에 결합이 생긴다. 즉 입출력 모델이 변경되면, 이에 의존하고 있는 유스케이스에 변경의 여지가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유스케이스 들이 기능적으로 묶여 있을 때는 입출력 모델을 공유하는 것은 괜찮다. 즉, 특정 세부 사항을 변경할 경우, 실제로 두 유스케이스 모두에 영향을 주고 싶을 때를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 유스케이스가 독립적으로 진화해야 하면, 입출력 모델을 분리하여야 한다.&lt;/p&gt;
&lt;h1&gt;도메인 엔티티를 입출력 모델로 사용하기&lt;/h1&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;h1&gt;인커밍 포트 건너 뛰기&lt;/h1&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;2000&quot; data-origin-height=&quot;957&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKGS8/btsDhMQi3lF/AhwdscVArr3kW6AevOimSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKGS8/btsDhMQi3lF/AhwdscVArr3kW6AevOimSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKGS8/btsDhMQi3lF/AhwdscVArr3kW6AevOimSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKGS8%2FbtsDhMQi3lF%2FAhwdscVArr3kW6AevOimSk%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;2000&quot; height=&quot;957&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;957&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;하지만, 인커밍 포트는 애플리케이션 중심에 접근하는 진입점을 정의한다. 전용 인커밍 포트를 통해 한눈에 애플리케이션의 진입점을 식별할 수 있다.&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;h1&gt;애플리케이션 서비스 건너 뛰기&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbChNT/btsDh8S7IsC/UApMK4qKKhyvdJ3g76Ec21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbChNT/btsDh8S7IsC/UApMK4qKKhyvdJ3g76Ec21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbChNT/btsDh8S7IsC/UApMK4qKKhyvdJ3g76Ec21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbChNT%2FbtsDh8S7IsC%2FUApMK4qKKhyvdJ3g76Ec21%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;2000&quot; height=&quot;694&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아웃고잉 어댑터에 있는 AccountPersistenceAdapter 클래스는 직접 인커밍 포트를 구현하여 일반적으로 인커밍 포트를 구현하는 애플리케이션 서비스를 대체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이는, 인커밍 어댑터와 아웃고잉 어댑터 사이에 모델을 공유해야 한다. 이 경우 공유해야 하는 모델이 도메인 모델이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 애플리케이션 코어에 유스케이스가 없어진다. 이는, 어댑터에 구현이 되어있으니, 유지보수하기가 어려워진다.&lt;/p&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 CRUD 유스케이스에 대해선 지름길의 유혹을 느낄 수 있는데, 이에 대한 가이드 라인을 정해야 한다.&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>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/30</guid>
      <comments>https://worldi.tistory.com/30#entry30comment</comments>
      <pubDate>Wed, 10 Jan 2024 00:45:10 +0900</pubDate>
    </item>
    <item>
      <title>10장 아키텍처 경계 강제하기</title>
      <link>https://worldi.tistory.com/29</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;규모가 커질 수록 아키텍처가 서서히 무너지게 된다. 계층 간의 경계가 약화되고, 코드는 테스트하기 어려워진다. 새로운 기능을 구현하는데 점점 더 많은 시간이 든다.&lt;/p&gt;
&lt;h1&gt;경계와 의존성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;경계를 강제한다는 것은 의존성이 올바른 방향을 향하도록 강제하는 것이다.&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;2000&quot; data-origin-height=&quot;2147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kGV9H/btsC8Vg0UTD/GdSO9Uma9suThdsdFiXp00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kGV9H/btsC8Vg0UTD/GdSO9Uma9suThdsdFiXp00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kGV9H/btsC8Vg0UTD/GdSO9Uma9suThdsdFiXp00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkGV9H%2FbtsC8Vg0UTD%2FGdSO9Uma9suThdsdFiXp00%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;2000&quot; height=&quot;2147&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2147&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;따라서 이러한 의존성 규칙을 강제하는 방법을 알아보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;방법 1 : 접근 제한자.&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public, protected, private, default(package-private) 제한자가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 default 제한자가 중요하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 자바 패키지를 통해, 클래스들을 응집적인 모듈로 만들어 주기 때문에, 모듈 내에 있는 클래스들은 서로 접근 가능 하지만, 패키지 바깥에서는 접근할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 패키지가 접근하려면, 모듈의 진입점으로 활용될 클래스를 public으로 만들어 주면된다.!!!!&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 class=&quot;gherkin&quot;&gt;&lt;code&gt;buckapl
|--- account
|    |----- adapter
|    |      |----- in
|    |      |      |---- web
|    |      |      |     |---- AccountController
|    |      |----- out
|    |      |      |---- persistence
|    |      |      |     |---- AccountPersistenceAdapter
|    |      |      |     |---- SpringDataAccountRepository
|    |---- domain
|    |     |----- Account
|    |     |----- Activity
|    |---- application
|    |     |----- SendMoneyService
|    |     |----- port
|    |     |     |---- in
|    |     |     |     |---- SendMoneyUseCase
|    |     |     |---- out
|    |     |     |     |---- LoadAccountPort
|    |     |     |     |---- UpdateAccountStatePort
&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;persistence 패키지는 외부에서 접근할 이유가 없기 때문에 default로 둬도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 SendMoneyService 도 마찬가지로 외부에서 접근할 이유가 없기 때문에 default로 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 주입 메커니즘은 리플랙션으로 주입되기 때문에, default 여도 상관없다.&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;하지만 이 방법은 클래스 패스 스캐닝을 이용할 때의 이야기 이고, 만약, 다른 방법(bean 주입) 이라면, public 제한자를 이용하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 클래스들 (domain, application 계층..) 은 public으로 정의되어 있어야 한다.&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;리팩터링을 하면서, 하위 패키지를 만들 수 있는데, 하위패키지는 다른 패키지로 취급되기 때문에 하위 패키지의 package-private 멤버에 접근할 수 없다. 이를 public으로 바꾸어주면 또 의존성 규칙이 깨질 수 있다.&lt;/p&gt;
&lt;h1&gt;방법 2: 컴파일 후 체크&lt;/h1&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;이에 대한 도구로 ArchUnit이 있다. 이는, 의존성 방향이 기대한 대로 잘 설정돼 있는지 체크할 수 있는 API이다. 의존성 방향을 위반할 시, 예외를 던지고, 테스트를 실패시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 잘 이용하면, 육각형 아키텍쳐 내에서 관련된 모든 패키지를 명시할 수 있는 일종의 도메인 특화 언어를 만들 수 있고, 패키지 사이의 의존성 방향이 올바른지 자동으로 체크할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
	void validateRegistrationContextArchitecture() {
		HexagonalArchitecture.boundedContext(&quot;io.reflectoring.buckpal.account&quot;)

				.withDomainLayer(&quot;domain&quot;)

				.withAdaptersLayer(&quot;adapter&quot;)
				.incoming(&quot;in.web&quot;)
				.outgoing(&quot;out.persistence&quot;)
				.and()

				.withApplicationLayer(&quot;application&quot;)
				.services(&quot;service&quot;)
				.incomingPorts(&quot;port.in&quot;)
				.outgoingPorts(&quot;port.out&quot;)
				.and()

				.withConfiguration(&quot;configuration&quot;)
				.check(new ClassFileImporter()
						.importPackages(&quot;io.reflectoring.buckpal..&quot;));
	}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바운디드 컨텍스트의 부모 패키지를 지정한 후, 하위패키지를 지정하고, check 함수를 통해 패키지 의존성이 의존성 규칙을 따라 유효하게 설정됐는지 검증할 수 있다.&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;하지만, 이에 대한 단점으로는 만약 타이핑 에러로 buckpal 이 아니라 bucpal 과 같이 작성되었다면, 테스트가 어떤 클래스도 찾지 못하여 테스트 자체가 무의미해진다. 따라서 리팩터링 할 시에 만약 패키지명이 바뀌면, 이에 대한 방안으로 클래스를 못찾았을 때 실패하는 테스트를 추가하여야 한다.&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;h1&gt;방법 3: 빌드 아티팩트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 아티팩트는 빌드 프로세스의 결과물이다. 대표적으로 자바에서는 메이븐과 그레이들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 도구의 주요 기능 중 하나는 의존성 해결이다. 이를 활용해서 모듈과 아키텍처의 계층 간의 의존성을 강제할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 의존성을 막기 위해 다음과 가이 아키텍처를 여러개의 빌드 아티팩트로 만들 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/birK1y/btsC9y69ef8/02AV5nqR63tCo0Gk5A8vJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/birK1y/btsC9y69ef8/02AV5nqR63tCo0Gk5A8vJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/birK1y/btsC9y69ef8/02AV5nqR63tCo0Gk5A8vJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbirK1y%2FbtsC9y69ef8%2F02AV5nqR63tCo0Gk5A8vJ1%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;2000&quot; height=&quot;1333&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1333&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;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;패키지로 구분하는 것에 비해 빌드 모듈로 구분하는 것의 장점&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점으로는 빌드 스크립트를 유지보수하여야 한다. &amp;rarr; 이는, 여러 개의 멀티 모듈로 나누기 전에 아키텍쳐가 안정되어야 한다.&lt;/p&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?&lt;/h1&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;p data-ke-size=&quot;size16&quot;&gt;새로운 코드를 추가하거나 리팩터링할 때 패키지 구조를 항상 생각하고, 가능하면, default 제한자를 통해 바깥에서 접근하면 안되는 클래스에 대해 의존성을 피한다.&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;하나의 빌드 모듈 안에서 아키텍처 경계를 강제해야 하고, default 제한자를 사용할 수 없다면 ArchUnit 같은 컴파일 후 체크 도구를 이용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아키텍처가 안정화 되면, 독립적인 빌드 모듈로 추출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/29</guid>
      <comments>https://worldi.tistory.com/29#entry29comment</comments>
      <pubDate>Wed, 10 Jan 2024 00:07:13 +0900</pubDate>
    </item>
    <item>
      <title>9장 애플리케이션 조립하기</title>
      <link>https://worldi.tistory.com/28</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;모든 의존성은 안쪽으로, 애플리케이션의 도메인 코드 방향으로 향해야한다. &amp;rarr; 도메인 코드가 바깥 계층의 변경으로부터 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유스케이스는 인터페이스만 알아야 하고, 런타임에 구현을 제공받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 생성자를 통해 객체를 전달 받기 때문에 실제 객체 대신 목으로 전달할 수 있고, 격리된 단위 테스트를 생성하기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 객체 인스턴스를 생성할 책임은 누구에게 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 클래스에 대한 의존성을 가지는 설정 컴포넌트가 있어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crsgfc/btsDdFqSDqW/w3frQ0mrnkDXZZ3jOIoGzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crsgfc/btsDdFqSDqW/w3frQ0mrnkDXZZ3jOIoGzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crsgfc/btsDdFqSDqW/w3frQ0mrnkDXZZ3jOIoGzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrsgfc%2FbtsDdFqSDqW%2Fw3frQ0mrnkDXZZ3jOIoGzk%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;2000&quot; height=&quot;1211&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1211&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;/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;웹 어댑터 인스턴스 생성, HTTP 요청 &amp;rarr; 웹 어댑터 전달 보장&lt;/li&gt;
&lt;li&gt;유스케이스 인스턴스 생성, 웹어댑터에 유스케이스 인스턴스 제공&lt;/li&gt;
&lt;li&gt;영속성 어댑터 인스턴스 생성, 유스케이스에 영속성 어댑터 인스턴스 제공&lt;/li&gt;
&lt;li&gt;영속성 어댑터가 실제로 데이터베이스에 접근할 수 있도록 보장&lt;/li&gt;
&lt;li&gt;설정 컴포넌트는 설정 파일이나 커맨드 라인 파라미터 등과 같은 설정 파라미터의 소스에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&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;엔터프라이즈 애플리케이션을 실행하기 위햇, 인스턴스 생성하는 로직을 다 작성해야 한다.&lt;/li&gt;
&lt;li&gt;각 클래스가 속한 패키지 외부에서 인스턴스를 생성하기 때문에 클래스가 모두 public이어야 한다. &amp;rarr; 이는 추후 의존성 주입 프레임 워크(스프링과 같은) 로 인해 package-private 의존성을 유지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;설정 컴포넌트 방법 1 : 스프링의 클래스 패스 스캐닝&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 클래스 패스에서 접근 가능한 모든 클래스를 확인해서, @Component 애너테이션이 붙은 클래스를 찾는다. 그리고 이에 대한 객체를 생성한다.&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 class=&quot;css&quot;&gt;&lt;code&gt;package com.book.cleanarchitecture.buckpal.shared;

import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface PersistenceAdapter {

    @AliasFor(annotation = Component.class)
    String value() default &quot;&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 아키텍처에 대해 쉽게 파악할 수 있다.&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;클래스에 프레임워크에 특화된 애너테이션을 붙여야 한다. 라이브러리나 프레임 워크를 만들 때는 스프링 프레임워크 의존성이 생겨버리기 때문에 사용하지 말아야 한다.&lt;/li&gt;
&lt;li&gt;애플리케이션 컨텍스트에 기대하지 않은 클래스가 올라갈 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;설정 컴포넌트 2 : 스프링의 자바 컨피그&lt;/h1&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;@Configuration 애너테이션을 통해 스프링의 클래스패스 스캐닝에서 발견해야 할 설정 클래스임을 표시한다.&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;@EnableJpaRepositories 애너테이션을 이용하면, 스프링이 우리가 정의한 스프링 데이터 레포지터리 인터페이스의 구현체를 직접 생성해서 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@EnableJpaRepositories는 메인 애플리케이션에도 붙일 수 있다. 이는, 애플리케이션이 시작할 때 마다 JPA를 활성화 시켜 영속성이 실질적으로 필요없는 테스트에서 애플리케이션을 실행할 때도 JPA 리포지토리들을 활성화한다. 따라서 이를 모듈에다가 옮기는 것이 한꺼번에 시작할 필요없이 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨피그파일을 통해 특정 모듈만 포함하고, 다른 모듈은 빈을 모킹해서 애플리케이션을 만들 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러면 테스트가 유연해진다. 리팩터링을 많이 하지 않고도, 각 모듈의 코드를 자체 코드베이스, 자체 패키지, 자체JAR 파일로 밀어넣을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;애플리케이션 계층을 스프링 프레임 워크에 대한 의존성 없이 깔끔하게 유지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정 클래스가 생성하는 빈이 설정 클래스와 같은 패키지에 존재하지 않는 다면 이 빈들을 public 으로 만들어야 한다.&lt;/li&gt;
&lt;li&gt;패키지를 모듈 경계로 사용하고, 각 패키지 안에 전용 설정 클래스를 만들 수 있지만 하위 패키지를 사용할 수 없다 (??) &amp;rarr; 10장에서 다룬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클래스패스 스캐닝 방식&lt;/b&gt;은 빠르게 개발할 수 있지만, 코드의 규모가 커지면, 투명성이 낮아진다. 적, 어떤 빈이 애플리케이션 컨텍스트에 올라오는지 알 수없고, 테스트 할 때에도, 애플리케이션 컨텍스트 일부분을 독립적으로 띄우기 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, &lt;b&gt;전용 설정 컴포넌트&lt;/b&gt; 같은 경우는 이에 대해 자유로워지고, 응집도가 매우 높다. 하지만 유지보수하는데 시간이 걸린다.&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/28</guid>
      <comments>https://worldi.tistory.com/28#entry28comment</comments>
      <pubDate>Tue, 9 Jan 2024 22:34:42 +0900</pubDate>
    </item>
    <item>
      <title>8장 경계 간 매핑하기</title>
      <link>https://worldi.tistory.com/27</link>
      <description>&lt;h1&gt;매핑하지 않기 전략 (No Mapping)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RrDnF/btsC88U4mVZ/SKqEpj8aQdDyAF81KlsY41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RrDnF/btsC88U4mVZ/SKqEpj8aQdDyAF81KlsY41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RrDnF/btsC88U4mVZ/SKqEpj8aQdDyAF81KlsY41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRrDnF%2FbtsC88U4mVZ%2FSKqEpj8aQdDyAF81KlsY41%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;2000&quot; height=&quot;391&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&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;절대로 쓰면 안되는 건 아니고, 간단한 CRUD 유스케이스 같은 경우, 모든 계층이 정확히 같은 구조의, 같은 정보를 필요로 한다면 괜찮다.&lt;/p&gt;
&lt;h1&gt;양방향 매핑 전략&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBa0GO/btsDayeuDYW/kGcxhk2A2y81UfGOaSOzS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBa0GO/btsDayeuDYW/kGcxhk2A2y81UfGOaSOzS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBa0GO/btsDayeuDYW/kGcxhk2A2y81UfGOaSOzS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBa0GO%2FbtsDayeuDYW%2FkGcxhk2A2y81UfGOaSOzS1%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;2000&quot; height=&quot;391&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&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;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&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;도메인 모델이 계층 경계를 넘어서 통신하는 데 사용된다. 즉, 도메인 객체가 인커밍 포트와 아웃고잉 포트의 입력 파라미터와 반환값으로 사용된다. &amp;rarr; 이는, 바깥쪽 계층의 요구에 따른 변경에 취약해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;완전 매핑 전략&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m4i8h/btsDd020G0d/BDZp13FEOb0BUbxkOCF6cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m4i8h/btsDd020G0d/BDZp13FEOb0BUbxkOCF6cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m4i8h/btsDd020G0d/BDZp13FEOb0BUbxkOCF6cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm4i8h%2FbtsDd020G0d%2FBDZp13FEOb0BUbxkOCF6cK%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;2000&quot; height=&quot;391&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;391&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;웹 계층은 입력을 애플리케이션 계층의 커맨드 객체로 매핑할 책임을 가진다.&lt;/li&gt;
&lt;li&gt;각 유스케이스는 전용 필드와 유효성 검증 로직을 가진 전용 커맨드를 가진다.&lt;/li&gt;
&lt;li&gt;애플리케이션 계층은 커맨드 객체를 유스케이스에 따라 도메인 모델을 변경하기 위해 필요한 모델로 매핑할 책임을 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&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;전역 패턴으로 추천하지는 않는다. 웹계층과 애플리케이션에서는 추천! (상태 변경 유스케이스가 명확할 때)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 애플리케이션 계층과 영속성 계층 사이에서는 매핑 오버헤드가 있다&amp;hellip;&lt;/p&gt;
&lt;h1&gt;단방향 매핑 전략&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KUAtS/btsDfVUe8Eg/kBlGZ5Kj1fPtddtBzpK8Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KUAtS/btsDfVUe8Eg/kBlGZ5Kj1fPtddtBzpK8Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KUAtS/btsDfVUe8Eg/kBlGZ5Kj1fPtddtBzpK8Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKUAtS%2FbtsDfVUe8Eg%2FkBlGZ5Kj1fPtddtBzpK8Wk%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;2000&quot; height=&quot;601&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;601&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;인터페이스는 특성에 대한 getter 메서드를 제공해서, 도메인 모델의 상태를 캡슐화 한다.&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;li&gt;바깥 계층 &amp;rarr; 애플리케이션 계층으로 전달 되는 객체들도 상태 인터 페이스를 구현한다. 이 매핑은 DDD 개념과 잘 어울린다. &amp;rarr; Factory&lt;/li&gt;
&lt;li&gt;각 계층은 한 방향으로만 매핑 (단방향) 한다. 한 계층이 다른 계층으로 부터 객체를 받으면, 해당 계층에서 이용할 수 있도록, 다른 무언가로 매핑한다.&lt;/li&gt;
&lt;li&gt;계층 간의 모델이 비슷할 때 효과적이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 읽기 전용 연산의 경우, 상태 인터페이스가 필요한 모든 정보를 제공한다. 따라서 웹 계층에서 전용 모델로 매핑할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;언제 어떤 매핑 전략을 사용할까?&lt;/h1&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;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;웹 계층과 애플리케이션 사이에서는 유스케이스의 결합을 제거하기 위해 완전 매핑 전략을 첫 번째 선택지로 둘 수 있다. &amp;rarr; 유스케이스 별 유효성 검증 로직이 명확해진다. 필요한 필드만 다룬다.&lt;/li&gt;
&lt;li&gt;애플리케이션과 영속성 계층 사이에서는 매핑 오버해드를 줄이기 위해 &amp;lsquo;매핑하지 않기&amp;rsquo; 전략을 첫번째 선택지로 둔다. &amp;rarr; but 애플리케이션 계층에서 영속성 문제를 다뤄야 한다면, 양방향 매핑전략으로 바꾼다.&lt;/li&gt;
&lt;/ul&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;매핑 오버헤드를 줄이고 빠르게 짜기 위해 매핑하지 않기 전략을 첫번째로 가져간다. (웹-애플리케이션, 애플리케이션-영속성 계층)&lt;/li&gt;
&lt;li&gt;하지만, 애플리케이션 계층에서 영속성, 또는 웹 문제를 다룬다면 양방향 매핑 전략으로 바꾼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?&lt;/h1&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;상황별로 매핑전략을 선택해야 한다. &amp;rarr; 유지보수하고 쉬운 코드를 이끈다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/27</guid>
      <comments>https://worldi.tistory.com/27#entry27comment</comments>
      <pubDate>Tue, 9 Jan 2024 18:58:13 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크세미나] Kafka 이벤트 기반 아키텍처 구축</title>
      <link>https://worldi.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2024.01.08 시청&lt;/p&gt;
&lt;h1&gt;발단 : 이벤트 기반 아키텍처 왜 사용?&lt;/h1&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;2000&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKYVjw/btsC7rTZkvf/nAMUf4ZdNARS47ORxKdVVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKYVjw/btsC7rTZkvf/nAMUf4ZdNARS47ORxKdVVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKYVjw/btsC7rTZkvf/nAMUf4ZdNARS47ORxKdVVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKYVjw%2FbtsC7rTZkvf%2FnAMUf4ZdNARS47ORxKdVVK%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;2000&quot; height=&quot;934&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;934&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;/li&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;배달만 잘 수행하길 바랬지만 요구 사항이 늘어날 수록 복잡도가 커진다.&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;&lt;b&gt;즉, 배달이 변경되었을 때, 관련 기능도 동시에 반영되어야 하는 가가 강한 일관성의 기준이 된다. 통계나, 쿠폰과 같은 개념은 관련 기능이 &amp;ldquo;언젠가&amp;rdquo; 반영되면 된다. (= 결과적 일관성)&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&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;관련 기능이 언젠가 반영되면 된다.&lt;/li&gt;
&lt;li&gt;이벤트는 시스템에서 일어난 행위이다.&lt;/li&gt;
&lt;li&gt;해결법 &amp;rarr; &lt;b&gt;이벤트 아키텍처&lt;/b&gt;를 이용하자!&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;이벤트 아키텍처 구현법&lt;/h1&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트의 정의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마틴 파울러에 따르면, 이벤트는 &amp;ldquo;도메인에 영향을 주는 관심 정보&amp;rdquo; 라고 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;대상, 행동, 정보, 시간으로 이루어져 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대상&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;행위&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;행위를 표현하는 방법&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;시간&lt;/h3&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;ex) 배달 (대상)이 A라이더에게 (행위 관련 값) 11시에(시간) 배차되었다.(행위) 로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 실제 이벤트를 데이터화 한 예시 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9PI4Q/btsC6wgYurW/Boza6IkA85ktPQdtLlPioK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9PI4Q/btsC6wgYurW/Boza6IkA85ktPQdtLlPioK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9PI4Q/btsC6wgYurW/Boza6IkA85ktPQdtLlPioK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9PI4Q%2FbtsC6wgYurW%2FBoza6IkA85ktPQdtLlPioK%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;2000&quot; height=&quot;838&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;838&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;이벤트 아키텍처 적용 후&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;/li&gt;
&lt;li&gt;데이터 분석 측면에서도 좋다.&lt;/li&gt;
&lt;li&gt;이벤트를 통해 소비처에게 정보 전달할 수 있다. (풍부한 정보의 이벤트 일 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;이벤트 아키텍처 사용 시 주의점&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;/li&gt;
&lt;li&gt;이벤트의 순서가 중요하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;이벤트 순서를 보장하고자 어떻게 했을까? &amp;rarr; 이벤트 파이프 라인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;이벤트 파이프 라인&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SNS, SQS, Kafka 등 다양하다.&lt;/li&gt;
&lt;/ul&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;h3 data-ke-size=&quot;size23&quot;&gt;순서 보장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토픽의 파티션을 통해 key별로 순서 보장한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;통합 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka Streams, Kafka Connect 등 다양한 통합 도구 제공&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;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;이벤트 발행이 실패하거나, 재시도 처리가 발생 시, 이벤트 순서가 변경이 되는 문제점이 있다. &amp;rarr; 이는 결국 도메인의 상태와 이벤트 발행 결과가 불일치하게 되었고, 시스템 장애로 이어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법 : &lt;b&gt;Transactional OutBox Pattern 을 사용한다.&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DH0RH/btsC88zWXIF/tgVKLoAv6EkF7UWHQdtJKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DH0RH/btsC88zWXIF/tgVKLoAv6EkF7UWHQdtJKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DH0RH/btsC88zWXIF/tgVKLoAv6EkF7UWHQdtJKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDH0RH%2FbtsC88zWXIF%2FtgVKLoAv6EkF7UWHQdtJKK%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;2000&quot; height=&quot;807&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&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;발행해야할 이벤트를 도메인 처리 트랜잭션과 묶어 아웃 박스 테이블에 저장한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이를 통해 도메인의 상태와 발행해야 될 이벤트 간의 일관성을 보장해준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;메세지 릴레이가 아웃박스 테이블의 저장된 이벤트을 순서대로 읽어 발행함으로써 이벤트의 순서와 발행을 보장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메세지 릴레이 구현&lt;/h3&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;오픈 소스 플랫폼에서 제공하는 Debezium 을 선택&lt;/li&gt;
&lt;li&gt;처리량을 높이기 위해 (한계가 있었음 테스크 수를 한개로만 지정해야하는) 아웃박스 테이블을 파티셔닝 처리한다. 프로듀서가 이벤트 키 기반으로 이벤트 파티셔닝한다. 적절한 아웃박스 테이블에 적재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;활용 사례&lt;/h1&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;이벤트 스트림으로 CQRS 를 적용할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조회 기능을 이벤트 기반으로 바꾼다. &amp;rarr; 쿼리쪽에 문제 발행해도 커맨드 쪽에 장애 가지 않도록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이벤트 스트림으로 데이터 분석 환경 구축&lt;/li&gt;
&lt;li&gt;이벤트 스트림으로 스트림즈 애플리케이션 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;출처&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=DY3sUeGu74M&quot;&gt;https://www.youtube.com/watch?v=DY3sUeGu74M&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지/세미나</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/26</guid>
      <comments>https://worldi.tistory.com/26#entry26comment</comments>
      <pubDate>Tue, 9 Jan 2024 00:19:20 +0900</pubDate>
    </item>
    <item>
      <title>6장 영속성 어댑터 구현하기</title>
      <link>https://worldi.tistory.com/25</link>
      <description>&lt;h1&gt;의존성 역전&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xVnLC/btsC9aRp9AQ/WHOjKtzrJbPNHUjKoajKk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xVnLC/btsC9aRp9AQ/WHOjKtzrJbPNHUjKoajKk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xVnLC/btsC9aRp9AQ/WHOjKtzrJbPNHUjKoajKk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxVnLC%2FbtsC9aRp9AQ%2FWHOjKtzrJbPNHUjKoajKk1%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;2000&quot; height=&quot;784&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;784&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;h1&gt;영속성 어댁터의 책임&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;입력을 받는다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;포트 인터 페이스를 통해 입력을 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;입력을 데이터 베이스 포맷으로 매핑한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;입력 모델을 데이터 베이스 테이블 구조를 반영한 JPA 엔티티 객체로 매핑한다.&lt;/li&gt;
&lt;li&gt;매핑하지 않는 전략은 8장에서 다룬다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;영속성 어댑터 입력 모델이 영속성 어댑터 내부에 있는 것이 아니라 애플리케이션 코어에 있다. &amp;rarr; 이를 통해 영속성 어댑터 내부를 변경하는 것이 바뀌지 않는 것이다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;입력을 데이터베이스로 보낸다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;쿼리를 날리고 결과를 받아 온다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;데이터베이스 응답을 포트에 정의된 출력 모델로 매핑해서 반환한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;출력 모델이 영속성 어댑터가 아니라 애플리케이션 코어에 위치한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;포트 인터페이스 나누기&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vnni4/btsC7sSlNKN/WXXZzk32DacvqiqtvdVen0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vnni4/btsC7sSlNKN/WXXZzk32DacvqiqtvdVen0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vnni4/btsC7sSlNKN/WXXZzk32DacvqiqtvdVen0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVnni4%2FbtsC7sSlNKN%2FWXXZzk32DacvqiqtvdVen0%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;2000&quot; height=&quot;788&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;788&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;이를 인터페이스 분리 원칙 (Interface Segregation Principle, ISP) 를 통해 해결 할 수 있다.&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;2000&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFq0rp/btsC83Lui2X/cV5G9uEVJ0eNjIKo6XiOa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFq0rp/btsC83Lui2X/cV5G9uEVJ0eNjIKo6XiOa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFq0rp/btsC83Lui2X/cV5G9uEVJ0eNjIKo6XiOa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFq0rp%2FbtsC83Lui2X%2FcV5G9uEVJ0eNjIKo6XiOa1%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;2000&quot; height=&quot;788&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 불필요한 의존성을 제거하고 필요한 메서드에만 의존한다. 포트의 이름이 명확하다는 장점이 있다.&lt;/p&gt;
&lt;h1&gt;영속성 어댑터 나누기&lt;/h1&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;2000&quot; data-origin-height=&quot;787&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsECx0/btsDawsTxHB/xugcu4t2FTJaSmeHSNJjk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsECx0/btsDawsTxHB/xugcu4t2FTJaSmeHSNJjk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsECx0/btsDawsTxHB/xugcu4t2FTJaSmeHSNJjk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsECx0%2FbtsDawsTxHB%2Fxugcu4t2FTJaSmeHSNJjk1%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;2000&quot; height=&quot;787&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;787&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;JPA, ORM 의 영속성 포트를 구현하며 성능을 개선하기 위해 평범한 SQL 을 이용하는 다른 종류의 포트를 구현하는 경우.&lt;/li&gt;
&lt;li&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;/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;2000&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bihCCK/btsC84cyUXa/4gtqoF8zYCM6XCoKyz1rW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bihCCK/btsC84cyUXa/4gtqoF8zYCM6XCoKyz1rW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bihCCK/btsC84cyUXa/4gtqoF8zYCM6XCoKyz1rW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbihCCK%2FbtsC84cyUXa%2F4gtqoF8zYCM6XCoKyz1rW0%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;2000&quot; height=&quot;694&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;JPA 예제&lt;/h1&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;Account&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1704620694834&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.reflectoring.buckpal.account.domain;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Account {

	@Getter private final AccountId id;
	@Getter private final Money baselineBalance
	@Getter private final ActivityWindow activityWindow;

	public static Account withoutId(
					Money baselineBalance,
					ActivityWindow activityWindow) {
		return new Account(null, baselineBalance, activityWindow);
	}

	public static Account withId(
					AccountId accountId,
					Money baselineBalance,
					ActivityWindow activityWindow) {
		return new Account(accountId, baselineBalance, activityWindow);
	}

	public Optional&amp;lt;AccountId&amp;gt; getId(){
		return Optional.ofNullable(this.id);
	}

	public Money calculateBalance() {
		/** 생략 **/
	}

	public boolean withdraw(Money money, AccountId targetAccountId) {
		/** 생략 **/
	}

	private boolean mayWithdraw(Money money) {
		return Money.add(
				this.calculateBalance(),
				money.negate())
				.isPositiveOrZero();
	}

	public boolean deposit(Money money, AccountId sourceAccountId) {
		/** 생략 **/
	}

	@Value
	public static class AccountId {
		private Long value;
	}
}&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;- AccountJpaEntity&lt;/p&gt;
&lt;pre id=&quot;code_1704620717182&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.reflectoring.buckpal.account.adapter.out.persistence;

@Entity
@Table(name = &quot;account&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
class AccountJpaEntity {

	@Id
	@GeneratedValue
	private Long id;

}&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;- ActivityJpaEntity&lt;/p&gt;
&lt;pre id=&quot;code_1704620738845&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.reflectoring.buckpal.account.adapter.out.persistence;

@Entity
@Table(name = &quot;activity&quot;)
@Data
@AllArgsConstructor
@NoArgsConstructor
class ActivityJpaEntity {

	@Id
	@GeneratedValue
	private Long id;

	@Column
	private LocalDateTime timestamp;

	@Column
	private Long ownerAccountId;

	@Column
	private Long sourceAccountId;

	@Column
	private Long targetAccountId;

	@Column
	private Long amount;

}&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;- ActivityRepository&lt;/p&gt;
&lt;pre id=&quot;code_1704620764628&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface ActivityRepository extends JpaRepository&amp;lt;ActivityJpaEntity, Long&amp;gt; {

	@Query(&quot;select a from ActivityJpaEntity a &quot; +
			&quot;where a.ownerAccountId = :ownerAccountId &quot; +
			&quot;and a.timestamp &amp;gt;= :since&quot;)
	List&amp;lt;ActivityJpaEntity&amp;gt; findByOwnerSince(
			@Param(&quot;ownerAccountId&quot;) Long ownerAccountId,
			@Param(&quot;since&quot;) LocalDateTime since);

	@Query(&quot;select sum(a.amount) from ActivityJpaEntity a &quot; +
			&quot;where a.targetAccountId = :accountId &quot; +
			&quot;and a.ownerAccountId = :accountId &quot; +
			&quot;and a.timestamp &amp;lt; :until&quot;)
	Long getDepositBalanceUntil(
			@Param(&quot;accountId&quot;) Long accountId,
			@Param(&quot;until&quot;) LocalDateTime until);

	@Query(&quot;select sum(a.amount) from ActivityJpaEntity a &quot; +
			&quot;where a.sourceAccountId = :accountId &quot; +
			&quot;and a.ownerAccountId = :accountId &quot; +
			&quot;and a.timestamp &amp;lt; :until&quot;)
	Long getWithdrawalBalanceUntil(
			@Param(&quot;accountId&quot;) Long accountId,
			@Param(&quot;until&quot;) LocalDateTime until);

}&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;- AccountPersistenceAdapter&lt;/p&gt;
&lt;pre id=&quot;code_1704620781558&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Component
class AccountPersistenceAdapter implements
		LoadAccountPort,
		UpdateAccountStatePort {

	private final SpringDataAccountRepository accountRepository;
	private final ActivityRepository activityRepository;
	private final AccountMapper accountMapper;

	@Override
	public Account loadAccount(
					AccountId accountId,
					LocalDateTime baselineDate) {

		AccountJpaEntity account =
				accountRepository.findById(accountId.getValue())
						.orElseThrow(EntityNotFoundException::new);

		List&amp;lt;ActivityJpaEntity&amp;gt; activities =
				activityRepository.findByOwnerSince(
						accountId.getValue(),
						baselineDate);

		Long withdrawalBalance = orZero(activityRepository
				.getWithdrawalBalanceUntil(
						accountId.getValue(),
						baselineDate));

		Long depositBalance = orZero(activityRepository
				.getDepositBalanceUntil(
						accountId.getValue(),
						baselineDate));

		return accountMapper.mapToDomainEntity(
				account,
				activities,
				withdrawalBalance,
				depositBalance);

	}

	private Long orZero(Long value){
		return value == null ? 0L : value;
	}


	@Override
	public void updateActivities(Account account) {
		for (Activity activity : account.getActivityWindow().getActivities()) {
			if (activity.getId() == null) {
				activityRepository.save(accountMapper.mapToJpaEntity(activity));
			}
		}
	}

}&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;다음과 같이 Account와 AccountJpaEntity, Activity와 ActivityJpaEntity 에는 양방향 매핑이 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매핑하지 않는 전략은 8장에서 다룬다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 JPA 에 종속적일 수 밖에 없다. JPA는 기본 생성자를 필요로 한다.&lt;/li&gt;
&lt;li&gt;영속성 계층에서는 성능 측면에서 @ManyToOne 관계가 적절할 수 있지만, 데이터의 일부만 가져오기를 바라면 도메인 모델에서는 이 관계가 반대가 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;영속성 타협 없이 풍부한 도메인 모델을 원한다면, 도메인 모델과 영속성 모델을 매핑하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;데이터베이스 트랜잭션&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영속성 어댑터는 어떤 데이터베이스 연산이 같은 유스케이스에 포함되는지 알지 못하기 떄무넹 언제 트랜잭션을 열고 닫을지 결정할 수 없다. 이 책임은 영속성 어댑터 호출을 관장하는 서비스에 위임해야 한다.&lt;/p&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?&lt;/h1&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;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/25</guid>
      <comments>https://worldi.tistory.com/25#entry25comment</comments>
      <pubDate>Sun, 7 Jan 2024 18:41:05 +0900</pubDate>
    </item>
    <item>
      <title>5장 웹 어댑터 구현하기</title>
      <link>https://worldi.tistory.com/24</link>
      <description>&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;의존성 역전&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인커밍 어댑터는 애플리케이션 서비스에 의해 구현된 인터페이스인 전용 포트를 통해 애플리케이션 계층과 통신한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 왜 어댑터와 유스케이스 중간에 간접 계층을 넣어야 할까? 애플리케이션 코어가 외부 세계와 통신할 수 있는 곳에 대한 명세가 포트이기 때문이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 여기에 대한 이야기는 11장에서 더 하기로 한다.&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a id=&quot;user-content-웹-어댑터의-책임&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/ZI-won-ZONE-ha/CS_JONGJIBU/blob/main/%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C%20%EB%B0%B0%EC%9A%B0%EB%8A%94%20%ED%81%B4%EB%A6%B0%20%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98/Chapter5%20%EC%9B%B9%20%EC%96%B4%EB%8C%91%ED%84%B0%20%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0/JONGHA.md#%EC%9B%B9-%EC%96%B4%EB%8C%91%ED%84%B0%EC%9D%98-%EC%B1%85%EC%9E%84&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;웹 어댑터의 책임&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Http 요청을 자바 객체로 매핑.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파라미터와 콘텐츠를 객체로 역직렬화 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;권한 검사&lt;/li&gt;
&lt;li&gt;입력 유효성 검증
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;유스 케이스 입력 모델은 유스케이스의 맥락에서 유효한 입력만 허용해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;웹어댑터의 입력모델과 유스케이스의 입력 모델과는 또 다른 유효성 검증을 해야한다.&lt;/li&gt;
&lt;li&gt;웹 어댑터의 입력 모델을 유스케이스의 입력 모델로 변환할 수 있다는 것을 검증해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;입력을 유스케이스의 입력 모델로 매핑&lt;/li&gt;
&lt;li&gt;유스케이스 호출&lt;/li&gt;
&lt;li&gt;유스케이스의 출력을 HTTP로 매핑&lt;/li&gt;
&lt;li&gt;HTTP 응답을 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a id=&quot;user-content-컨트롤러-나누기&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/ZI-won-ZONE-ha/CS_JONGJIBU/blob/main/%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C%20%EB%B0%B0%EC%9A%B0%EB%8A%94%20%ED%81%B4%EB%A6%B0%20%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98/Chapter5%20%EC%9B%B9%20%EC%96%B4%EB%8C%91%ED%84%B0%20%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0/JONGHA.md#%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EB%82%98%EB%88%84%EA%B8%B0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;컨트롤러 나누기&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 어댑터는 한 개 이상의 클래스로 구성해도 된다. 각 컨트롤러가 가능한 한 좁고 다른 컨트롤ㄹ러와 가능한 한 적게 공유하는 웹 어댑터 조각을 구현해야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;계좌 리소스와 관련된 모든 것이 하나의 클래스에 모여 있는 게 괜찮을까?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단점은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스마다 코드는 적을 수록 좋다. 보기 힘들다.&lt;/li&gt;
&lt;li&gt;테스트 코드 파악이 어렵다. 특정 프로덕션 코드에 해당 하는 테스트 코드를 찾기 쉽게 만들어야 한다.&lt;/li&gt;
&lt;li&gt;데이터 구조의 재활용을 촉진한다. 즉, 계좌 리소스에서는 모든 연산에서 필요한 데이터를 담는 통 역할을 하게 되어, 불필요한 정보 포함이 생길 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컨트롤러를 나누면 장점은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컨트롤러에 맞는 모델을 새로 만들게 될 확률이 높다.&lt;/li&gt;
&lt;li&gt;컨트롤러 명과 서비스명에 대해서도 의미를 드러낼 수 있는 단어 채택을 할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-유지-보수-가능한-소프트웨어를-만드는데-어떻게-도움이-될까&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/ZI-won-ZONE-ha/CS_JONGJIBU/blob/main/%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C%20%EB%B0%B0%EC%9A%B0%EB%8A%94%20%ED%81%B4%EB%A6%B0%20%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98/Chapter5%20%EC%9B%B9%20%EC%96%B4%EB%8C%91%ED%84%B0%20%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0/JONGHA.md#%EC%9C%A0%EC%A7%80-%EB%B3%B4%EC%88%98-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EB%A5%BC-%EB%A7%8C%EB%93%9C%EB%8A%94%EB%8D%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%84%EC%9B%80%EC%9D%B4-%EB%90%A0%EA%B9%8C&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;유지 보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 어댑터에서는 애플리케이션, 도메인 로직을 수행하지 않도록, HTTP 요청을 변환하는 역할 을 해야한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션 계층은 Http 와 관련된 작업을 하면 안된다.&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/24</guid>
      <comments>https://worldi.tistory.com/24#entry24comment</comments>
      <pubDate>Sun, 7 Jan 2024 15:57:55 +0900</pubDate>
    </item>
    <item>
      <title>4장 유스케이스 구현하기</title>
      <link>https://worldi.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;육각형 아키텍쳐의 장점은 애플리케이션, 웹, 영속성 계층이 아주 느슨하게 결합돼 있다는 점이다. 이를 통해, 도메인 코드를 자유롭게 모델링 하고, DDD, 풍부하거나 빈약한 도메인 모델을 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqDIm5/btsC8REh987/h22MY7lT3kdec8wH4hFcQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqDIm5/btsC8REh987/h22MY7lT3kdec8wH4hFcQ1/img.png&quot; data-alt=&quot;예제에 나와 있는 Account 도메인 값객체인 ActivityWindow에 대한 설명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqDIm5/btsC8REh987/h22MY7lT3kdec8wH4hFcQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqDIm5%2FbtsC8REh987%2Fh22MY7lT3kdec8wH4hFcQ1%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;2000&quot; height=&quot;1024&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제에 나와 있는 Account 도메인 값객체인 ActivityWindow에 대한 설명&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;유스케이스&lt;/h1&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;b&gt;비즈니스 규칙&lt;/b&gt;을 검증할 책임이 있다. 도메인 엔티티와 이 책임을 공유한다. 유스케이스는 입력을 기반으로 어떤 방법으로든 모델의 상태를 변경한다.&lt;/li&gt;
&lt;li&gt;아웃고잉 어댑터를 호출한다. 아웃고잉 어댑터에서 온 출력값을, 유스케이스를 호출한 어댑터로 반환할 출력 객체로 변환한다.&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;2000&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT2Cg4/btsC53lcpDk/kZ2ozcLkznJCMbvkPsMDi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT2Cg4/btsC53lcpDk/kZ2ozcLkznJCMbvkPsMDi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT2Cg4/btsC53lcpDk/kZ2ozcLkznJCMbvkPsMDi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT2Cg4%2FbtsC53lcpDk%2FkZ2ozcLkznJCMbvkPsMDi0%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;2000&quot; height=&quot;804&quot; data-origin-width=&quot;2000&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;다음과 같이,, 하나의 서비스가 하나의 유스케이스를 구현한다. 도메인 모델을 변경하고, 변경된 상태를 저장하기 위해 아웃고잉 포트를 호출.&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;유효성 검증에 Bean Validation API를 사용할 수 있다&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;@Getter
public class SendMoneyCommand extends SelfValidating&amp;lt;SendMoneyCommand&amp;gt;{

	public SendMoneyCommand(
		Account.AccountId sourceAccoundId, 
		Account.AccountId targetAccoundId){
		
		this.validateSelf();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SelfValidating 추상 클래스는 validateSelf 메서드를 통해 생성자 코드의 마지막 문장에서 이메서드를 호출한다. 이는 Bean Validataion 애너테이션을 검증하고, 유효성 검증 규칙을 위반하면 예외를 던진다.&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;만약 파라미터가 많다면, 생성자를 Private 메서드로 만들고, 빌더 패턴을 이용할 수 있다.&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 에 의존적이게 되지 않을까..?&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;이는, 당연이 null 허용과 더불어 다른 유스케이스의 결합이 생길 수 있는 부작용을 막는다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출금 계좌는 초과 출금 되어 서는 안된다. &amp;rarr; 모델의 상태에 접근한다는 측면해서, 비즈니스 규칙이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;송금되는 금액은 0보다 커야 한다. &amp;rarr; 모델에 접근하지 않고도 검증될 수 있다. 입력 유효성 검증이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비즈니스 규칙 검증은 어디에서 해야할까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 엔티티 &amp;rarr; 비즈니스 로직 바로 옆에 규칙이 위치하여, 추론이 쉽다.&lt;/li&gt;
&lt;li&gt;유스케이스 코드 &amp;rarr; 도메인 엔티티 사용하기 전에&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풍부한 도메인 모델 vs 빈약한 도메인 모델&lt;/h2&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;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;/li&gt;
&lt;/ul&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;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 class=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
class GetAccountBalanceService implements GetAcccountBalanceQuery {
	private final LoadAccountPort loadAccountPort;
	
	@Override
	public Money getAccountBalance (AccountId accountId) {
	return loadAccountPort.loadAccount(accountId, LocalDateTime.now().calcaulateBalance(););	
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용 쿼리는 쓰기가 가능한 유스케이스(커맨드)와 구분된다. 이는 CQS와 CQRS 같은 개념과 잘 맞는다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/23</guid>
      <comments>https://worldi.tistory.com/23#entry23comment</comments>
      <pubDate>Sun, 7 Jan 2024 14:06:48 +0900</pubDate>
    </item>
    <item>
      <title>3장 코드 구성하기</title>
      <link>https://worldi.tistory.com/22</link>
      <description>&lt;h1&gt;계층으로 구성하기&lt;/h1&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;buckapl
|--- domain
|    |----- Account
|    |----- Activity
|    |----- AccountRepository
|    |----- AccountService
|--- persistence
|    |----- AccountRepositoryImpl
|--- web
|    |----- AccountController
&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;/p&gt;
&lt;h1&gt;기능으로 구성하기&lt;/h1&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;buckapl
|--- account
|    |----- Account
|    |----- Activity
|    |----- AccountRepository
|    |----- SendMoneyService
|    |----- AccountRepositoryImpl
|    |----- AccountController
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pakage-private 접근 수준과 결합하여 각 기능 사이의 불필요한 의존성을 방지할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  소리치는 아키텍처 : 애플리케이션의 기능을 코드를 통해 볼 수 있게 만드는 것&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제점&lt;/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;h1&gt;아키텍쳐 적으로 표현력 있는 구조&lt;/h1&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;buckapl
|--- account
|    |----- adapter
|    |      |----- in
|    |      |      |---- web
|    |      |      |     |---- AccountController
|    |      |----- out
|    |      |      |---- persistence
|    |      |      |     |---- AccountPersistenceAdapter
|    |      |      |     |---- SpringDataAccountRepository
|    |---- domain
|    |     |----- Account
|    |     |----- Activity
|    |---- application
|    |     |----- SendMoneyService
|    |     |----- port
|    |     |     |---- in
|    |     |     |     |---- SendMoneyUseCase
|    |     |     |---- out
|    |     |     |     |---- LoadAccountPort
|    |     |     |     |---- UpdateAccountStatePort
&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;domain : 도메인 모델이 속함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application : 도메인 모델을 둘러싼 서비스 계층을 포함&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LoadAccountPort, UpdateAccountStatePort : 아웃고잉 포트 인터 페이스.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adapter : 인커밍 포트를 호출하는 인커밍 어댑터와 애플리케이션 계층의 아웃고잉 포트에 대한 구현을 제공하는 아웃고잉 어댑터 포함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;adapter : &lt;b&gt;package-private 접근 수준으로 놔도 됨&lt;/b&gt;. 애플리케이션에서 어댑터 클래스로 향하는 의존성이 없다.&lt;/li&gt;
&lt;li&gt;application, domain : 도메인 클래스는 public. 서비스는 인커밍 포트 인터페이스 뒤에 숨겨질 수 있기 떄문에 public 일 필요가 없다.&lt;/li&gt;
&lt;li&gt;어댑터를 다른 구현으로 바꿀때 교체가 빠르다.&lt;/li&gt;
&lt;li&gt;DDD 개념을 직접적으로 대응시킬 수 있다. 상위레벨 패키지(account)는 다른 바운디드 컨텍스트와 통신할 전용진입점이자, 출구를 포함하는 바운디드 컨텍스트&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;의존성 주입&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKilMj/btsC8Xdqs4n/2RvjxQsO3SpuAAN5yKkKkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKilMj/btsC8Xdqs4n/2RvjxQsO3SpuAAN5yKkKkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKilMj/btsC8Xdqs4n/2RvjxQsO3SpuAAN5yKkKkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKilMj%2FbtsC8Xdqs4n%2F2RvjxQsO3SpuAAN5yKkKkK%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;2000&quot; height=&quot;1263&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1263&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;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;h1&gt;유지보수 가능한 소프트웨어 만드는데 어떻게 도움?&lt;/h1&gt;
육각형 아키텍처 패키지 구조를 통해, 의사소통, 개발, 유지보수가 편해진다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/22</guid>
      <comments>https://worldi.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 7 Jan 2024 12:22:24 +0900</pubDate>
    </item>
    <item>
      <title>2장 의존성 역전하기</title>
      <link>https://worldi.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;목차에&amp;nbsp;앞서서,&amp;nbsp;SOLID&amp;nbsp;의&amp;nbsp;핵심&amp;nbsp;기능&amp;nbsp;중&amp;nbsp;하나인&amp;nbsp;S와&amp;nbsp;D를&amp;nbsp;먼저&amp;nbsp;이야기&amp;nbsp;나누어&amp;nbsp;보겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단일 책임 원칙 (SRP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Single Responsibility Principle&lt;/b&gt; 로 직역하자면 &lt;b&gt;하나의 컴포넌트는 오로지 한 가지 일만 해야하고, 그것을 올바르게 수행해야 한다는 의미&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 실제 정의는 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 책임은 오로지 한가지 일을 하는 것 보다 &lt;b&gt;&amp;ldquo;변경할 이유&amp;rdquo;&lt;/b&gt; 로 해석되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQaTRg/btsCTu35YHE/MKM2IJlhRW0PvciHsA82rK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQaTRg/btsCTu35YHE/MKM2IJlhRW0PvciHsA82rK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQaTRg/btsCTu35YHE/MKM2IJlhRW0PvciHsA82rK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQaTRg%2FbtsCTu35YHE%2FMKM2IJlhRW0PvciHsA82rK%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;2000&quot; height=&quot;1692&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1692&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;다음과 같이, E는 다른 컴포넌트에 의존하지 않으므로, E를 변경할 이유는 새로운 요구사항에 의해 E의 기능을 바꾸어야 할 때이다.&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;하지만, A와 같은 경우, 여러 컴포넌트에 의존하고 있기 때문에, 직접적인 B, C 의존성은 물론, 간접적인 D, E의 변경 또한 신경써줘야 한다.&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 도메인 계층이 영속성 계층을 의존하기 때문에 &lt;b&gt;영속성 계층을 변경하게 되면, 잠재적으로 도메인 계층도 변화한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 어떻게 해결할 수 있을까?&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;바로 의존성 역전 원칙이다! (Dependency Inversion Principle, DIP)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드상의 어떤 의존성이든 그 방향을 바꿀 수 있다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 조건으로 의존성의 양쪽 코드를 모두 제어할 수 있을 때 의존성을 역전시킬 수 있다. 즉 다른 라이브러리에 의존성이 있다면, 해당 라이브러리를 제어할 수 없기에 의존성 역전이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 코드와 영속성 코드간의 의존성을 역전시켜서 영속성 코드가 도메인 코드에 의존하도록 변경해보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lu4Cn/btsCUz41Ub6/LDLk2nnPvlKIspTKLyhj9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lu4Cn/btsCUz41Ub6/LDLk2nnPvlKIspTKLyhj9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lu4Cn/btsCUz41Ub6/LDLk2nnPvlKIspTKLyhj9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flu4Cn%2FbtsCUz41Ub6%2FLDLk2nnPvlKIspTKLyhj9k%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;2000&quot; height=&quot;1220&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1220&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;h1&gt;클린 아키텍처&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로버스 C. 마틴이 클린아키텍처를 다음과 같이 말한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 규칙의 테스트를 용이하게 하고, 비즈니스 규칙은 프레임워크, 데이터베이스, UI, 외부 애플리케이션, 인터페이스로 부터 독립적이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이는, 도메인 코드가 어떤 의존성을 갖고 있지 않고, 모든 의존성이 도메인 코드로 향하게 두어야 함을 의미한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/olQEL/btsCUzRsfFB/INWPJZLUEQRv8vVWlxzV7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/olQEL/btsCUzRsfFB/INWPJZLUEQRv8vVWlxzV7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/olQEL/btsCUzRsfFB/INWPJZLUEQRv8vVWlxzV7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FolQEL%2FbtsCUzRsfFB%2FINWPJZLUEQRv8vVWlxzV7K%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;2000&quot; height=&quot;1469&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1469&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처의 코어에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다.그리고 &lt;b&gt;유스케이스는 서비스로 단일 책임을 갖기 위해, 조금 더 세분화되어 있다.&lt;/b&gt; 이를 통해 &lt;b&gt;넓은 서비스 문제&lt;/b&gt;를 피할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하기 위해선, 도메인 계층과 외부 계층이 철저히 분리 되어야 하므로 애플리케이션의 각 엔티티에 대한 모델을 각 계층에서 유지보수하여야 한다.&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;즉, 영속성 계층에서 ORM을 쓴다고 가정한다면, 데이터베이스 구조 및 객체 필드와 데이터베이스 칼럼의 매핑을 서술한 메타데이터를 담고 있는 엔티티 클래스를 필요로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 계층은 영속성 계층을 모르기 때문에, 이는 도메인 계층과 영속성 계층 각각 엔티티를 만들어야 한다. &lt;b&gt;즉, 도메인 계층과 영속성 계층이 데이터를 주고 받을 때, 두 엔티티를 서로 변환해야한다는 뜻이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;육각형 아키텍처 (헥사고날 아키텍처)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;육각형 아키텍처는 알리스테어 콕번이 만든 용어로, 클린 아키텍처에서의 같은 양상을 띤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;997&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J86WY/btsCQpoNVWn/3VqcQll1KcHTH2GJyTviKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J86WY/btsCQpoNVWn/3VqcQll1KcHTH2GJyTviKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J86WY/btsCQpoNVWn/3VqcQll1KcHTH2GJyTviKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ86WY%2FbtsCQpoNVWn%2F3VqcQll1KcHTH2GJyTviKK%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;2000&quot; height=&quot;997&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;997&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;육각형 아키텍처는 다음과 같이, 애플리케이션이 다른 시스템이나 어댑터와 연결되는 4개 이상의 면을 가질 수 있음을 보여주기 위해 일반적인 사각형 대신, 육각형을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;육각형 안에는 도메인 엔티티와 상호작용하는 유스케이스가 있고, 모든 의존성은 도메인 쪽으로 향하는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 육각형 바깥쪽에는 애플리케이션과 상호작용하는 여러 어댑터가 존재한다. 이는 데이터베이스 뿐만 아니라, 웹 어댑터, 외부 어댑터를 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;b&gt;포트와 어댑터 아키텍처&lt;/b&gt; 로도 알려져있다. 가장 바깥쪽의 계층은 애플리케이션과 다른 시스템 간의 번역을 담당하는 어댑터로 구성돼 있다. 그리고 포트+ 유스케이스 구현체를 결합해 &lt;b&gt;애플리케이션 계층&lt;/b&gt;을 구성한다. 그리고 안에 계층은 도메인 엔티티가 위치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;유지 보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?&lt;/h1&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;도메인 코드를 변경할 이유를 줄이고, 유지보수성을 좋게한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/21</guid>
      <comments>https://worldi.tistory.com/21#entry21comment</comments>
      <pubDate>Mon, 1 Jan 2024 23:30:10 +0900</pubDate>
    </item>
    <item>
      <title>1장 계층형 아키텍처의 문제는 무엇일까?</title>
      <link>https://worldi.tistory.com/20</link>
      <description>&lt;h1&gt;계층형 아키텍쳐란?&lt;/h1&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;h2 data-ke-size=&quot;size26&quot;&gt;1. 데이터 베이스 주도 설계를 유도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 계층은 &amp;rarr; 도메인 계층, 도메인 계층은 &amp;rarr; 영속성 계층에 의존한다.&lt;/li&gt;
&lt;li&gt;데이터 베이스 중심 아키텍쳐가 만들어지는 큰 원인은 ORM을 사용하기 때문이다. ORM에 의해 관리되는 엔티티들은 일반적으로 영속성 계층에 둔다. 이를 통해 영속성 계층과 도메인 계층에 강한 결합이 생긴다.&lt;/li&gt;
&lt;li&gt;서비스는 영속성 모델을 비즈니스 모델처럼 사용하게 되고, 도메인 로직 뿐만 아니라, 즉시 로딩, 지연 로딩, 데이터 베이스 트랜잭션, 캐시 플러시 등등 영속성 계층과 관련된 작업들을 해야만 한다.&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;2000&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPcrf9/btsCTYw46yN/xocqSIU2gX20l15zazTfr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPcrf9/btsCTYw46yN/xocqSIU2gX20l15zazTfr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPcrf9/btsCTYw46yN/xocqSIU2gX20l15zazTfr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPcrf9%2FbtsCTYw46yN%2FxocqSIU2gX20l15zazTfr1%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;2000&quot; height=&quot;1076&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 지름길을 택하기 쉬워진다.&lt;/h2&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;이를 위해, 아키텍쳐 규칙을 강제할 수 있는데, &amp;lsquo;강제한다&amp;rsquo;는 의미는 해당 규칙이 깨졌을 때 빌드가 실패하도록 만드는 규칙을 의미한다.&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;2000&quot; data-origin-height=&quot;894&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LsiCD/btsCUZWOHjJ/AMGbhpzvB0CzfiO8vZw6Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LsiCD/btsCUZWOHjJ/AMGbhpzvB0CzfiO8vZw6Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LsiCD/btsCUZWOHjJ/AMGbhpzvB0CzfiO8vZw6Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLsiCD%2FbtsCUZWOHjJ%2FAMGbhpzvB0CzfiO8vZw6Yk%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;2000&quot; height=&quot;894&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;894&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 테스트하기 어려워진다.&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chQwJ8/btsCP2NZVHs/SP8YipriutaDNiEePC3SE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chQwJ8/btsCP2NZVHs/SP8YipriutaDNiEePC3SE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chQwJ8/btsCP2NZVHs/SP8YipriutaDNiEePC3SE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchQwJ8%2FbtsCP2NZVHs%2FSP8YipriutaDNiEePC3SE1%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;2000&quot; height=&quot;1426&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1426&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;도메인 로직을 웹 계층에 구현하게 된다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;유스 케이스가 확장된다면, 웹 계층에 구현하는 일이 많아질 테고 책임이 섞일 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;웹 계층 테스트에서 영속성 계층을 모킹하여야 한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;단위 테스트의 복잡도가 높아진다. 이는 테스트 코드를 작성하는 것 보다, 종속성을 이해하고 mock을 만드는 데 더 많은 시간이 걸린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 유스 케이스를 숨긴다.&lt;/h2&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;계층형 아키텍쳐는 여러 개의 유스 케이스를 담당하는 넓은 서비스를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VyJN9/btsCOz6eyax/bAbhTVrZ01L0xGf9LHktUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VyJN9/btsCOz6eyax/bAbhTVrZ01L0xGf9LHktUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VyJN9/btsCOz6eyax/bAbhTVrZ01L0xGf9LHktUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVyJN9%2FbtsCOz6eyax%2FbAbhTVrZ01L0xGf9LHktUk%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;2000&quot; height=&quot;1304&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1304&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;h2 data-ke-size=&quot;size26&quot;&gt;5. 동시 작업이 어려워진다.&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연되는 소프트웨어 프로젝트에 인력을 더하는 것은 개발을 늦출 뿐이다.&lt;/p&gt;
&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 data-ke-size=&quot;size16&quot;&gt;물론 인터페이스를 먼저 같이 정의하고, 이 인터페이스 들로 작업하면 된다고 이야기 할 수 있다. 하지만, 이는 데이터베이스 주도 설계를 하지 않는 경우에만 가능하다. 왜냐하면 데이터 베이스 주도 설계는 영속성 계층과 도메인 계층이 섞여있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 넓은 서비스가 존재한다면, 동시 편집과 더불어 병합 충돌로 이어진다.&lt;/p&gt;
&lt;h1&gt;유지보수 가능한 소프트웨어를 만드는 방법?&lt;/h1&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>책 일지/만들면서 배우는 클린 아키텍쳐</category>
      <category>만들면서배우는클린아키텍처</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/20</guid>
      <comments>https://worldi.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 1 Jan 2024 22:08:48 +0900</pubDate>
    </item>
    <item>
      <title>[카카오 기출] 호텔 방 배정 풀이</title>
      <link>https://worldi.tistory.com/18</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Union-find&lt;/p&gt;
&lt;h1&gt;풀이 과정&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;le; k &amp;le; 10^12 이하이므로 long 으로 자료형을 설정한다.&lt;/li&gt;
&lt;li&gt;주의사항 : 배열 같은 경우, index를 long으로 설정하지 못한다. &amp;rarr; Map 자료구조 관리하여야 한다.&lt;/li&gt;
&lt;li&gt;어려웠던 점 : parent를 찾을 때 일반적으로 배열을 사용했는데 map 자료구조를 떠올려야 했다.&lt;/li&gt;
&lt;li&gt;시간초과 난 부분
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;맨 처음에 Map을 parent[i] = i 와 같은 식으로 k범위 까지 초기화 시켜줬는데, 시간초과 난다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;parent를 계속 갱신 시켜주며, 가장 가까운 호텔을 parent로 다시 갱신한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;코드&lt;/h1&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;import java.util.*;
class Solution {
    public Map&amp;lt;Long, Long&amp;gt; parent = new HashMap&amp;lt;&amp;gt;();
    public long findParent(long ori, long a) {
        if (parent.get(a) == null) {
            parent.put(a, a+1);
            return a;
        }
        parent.put(a, findParent(ori, parent.get(a)));
        return parent.get(a);
    }
   
    public long[] solution(long k, long[] room_number) {
        long [] answer = new long[room_number.length];
     
        for (int i = 0 ; i&amp;lt; room_number.length ; i++) {
            long num = room_number[i];
            long par = findParent(num, num);
            answer[i] = par;
        }
        
        return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/18</guid>
      <comments>https://worldi.tistory.com/18#entry18comment</comments>
      <pubDate>Sun, 24 Dec 2023 23:18:56 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 5577 1학년 풀이</title>
      <link>https://worldi.tistory.com/17</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP&lt;/p&gt;
&lt;h1&gt;풀이 과정&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 생각나는 대로 짜봤다. 예상대로 시간초과가 난다. n이 최대 100이므로, 시간 복잡도가 2^100 이기 때문이다. 이를 dp로 메모이제이션 하는 작업이 필요할 것 같다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;   public static void dfs(int start, int end, int sum, int answer) {
        if (start == end) {
            if (sum == answer) {
                total++;
            }
            return;
        }
        int tempSum = sum + arr[start];
        if (0&amp;lt;= tempSum &amp;amp;&amp;amp; tempSum &amp;lt;= 20) {
            dfs(start+1, end, tempSum, answer);
        }
        tempSum = sum - arr[start];
        if (0&amp;lt;= tempSum &amp;amp;&amp;amp; tempSum &amp;lt;= 20) {
            dfs(start+1, end, tempSum, answer);
        }
    }
&lt;/code&gt;&lt;/pre&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[i][j] = dp[i-1][j+arr[i-1]] + dp[i-1][j-arr[i-1]];&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 i번째 숫자까지 봤을 때, j의 합을 가졌을 때의 경우의 수이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    public static StringTokenizer st = null;
    public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
   
		public static int n;
    public static int[] arr;
    public static long[][] dp;

    public static void main(String[] args) throws IOException {
        n = Integer.parseInt(br.readLine());
        arr = new int[n + 1];

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &amp;lt; n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        // dp[i][j] = dp[i-1][j+arr[i-1]] + dp[i-1][j-arr[i-1]];
        dp = new long[n + 1][21];
        dp[1][arr[0]] = 1;
        for (int i = 1; i &amp;lt;= n - 1; i++) {
            for (int j = 0; j &amp;lt;= 20; j++) {
                int tempSum = j + arr[i - 1];
                if (0 &amp;lt;= tempSum &amp;amp;&amp;amp; tempSum &amp;lt;= 20) {
                    dp[i][tempSum] += dp[i - 1][j];
                }
                tempSum = j - arr[i - 1];
                if (0 &amp;lt;= tempSum &amp;amp;&amp;amp; tempSum &amp;lt;= 20) {
                    dp[i][tempSum] += dp[i - 1][j];
                }
            }
        }
        System.out.println(dp[n - 1][arr[n - 1]]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 DP의 basecode를 dp[1][arr[0]] = 1 가 아닌, dp[0][0] = 1 이라고 세웠었다. 그래서 한 82%센트 쯤에 틀렸었습니다 떴었는데 이유는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&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;0 0 0&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[0][0] = 1이라면, dp[1][0] = 2, dp[2][0] = 4 가 되어 답은 4가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리가 예상한 답은, 0+0 = 0, 0-0= 0 으로 2이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최소한 +,- 가 하나쯤은 존재하려면, basecode가 0이 아닌 1에서 부터 시작하는 것으로 답을 매겨야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;h1&gt;결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP를 풀면서 항상 느낀 것은 DP의 점화식을 올바르게 세웠는데 틀린 다면,for문의 범위를 제대로 잘 돌고 있는 지 그리고 basecode 초기화를 잘 시켜주고 있는지 따져야 한다.&lt;/p&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/17</guid>
      <comments>https://worldi.tistory.com/17#entry17comment</comments>
      <pubDate>Sun, 24 Dec 2023 21:26:19 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1823 수확 풀이</title>
      <link>https://worldi.tistory.com/16</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP&lt;/p&gt;
&lt;h1&gt;풀이 과정&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n이 최대 2000이므로, 브루트 포스는 안될 것 같다. &amp;rarr; 자연스레 DP를 하는 방향으로 갔다.&lt;/li&gt;
&lt;li&gt;DP는 작은 문제를 &amp;rarr; 큰문제로 확장시켜 나가는 방식이다. 즉, 크기를 확장시키는 순서가 있어야 한다.&lt;/li&gt;
&lt;li&gt;ex) 13152 같은 경우, 양 끝쪽만 빠져나가는 것을 확인 할 수 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m34EG/btsCycDikMp/Om9ZnilbXThZqrWaMbPm61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m34EG/btsCycDikMp/Om9ZnilbXThZqrWaMbPm61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m34EG/btsCycDikMp/Om9ZnilbXThZqrWaMbPm61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm34EG%2FbtsCycDikMp%2FOm9ZnilbXThZqrWaMbPm61%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;2000&quot; height=&quot;1500&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;따라서&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;점화식을&amp;nbsp;도출해냈다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;BaseCode가&amp;nbsp;중요한데,&amp;nbsp;start==end&amp;nbsp;일&amp;nbsp;경우,&amp;nbsp;맨&amp;nbsp;마지막&amp;nbsp;숫자이므로,&amp;nbsp;dp[start][start]&amp;nbsp;=&amp;nbsp;arr[start]&amp;nbsp;*&amp;nbsp;n이&amp;nbsp;된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이를&amp;nbsp;코드로&amp;nbsp;옮기면&amp;nbsp;다음과&amp;nbsp;같다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class P1823 {
    public static BufferedReader br=  new BufferedReader(new InputStreamReader(System.in));

    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(br.readLine());
        int [] arr = new int [n];
        int [][] dp = new int [n][n];
        for (int i = 0 ; i&amp;lt; n ;  i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }
        // dp[start][end] = Math.max(dp[start+1][end] + cnt * a[start] ,  dp[start][end-1] + cnt * a[end]) ;
        // 크기 순으로 0 - n-1 까지
        for (int i = 0 ; i&amp;lt; n ; i++) {
            dp[i][i] = arr[i] * n;
        }
        for (int size = 1 ; size &amp;lt; n; size++){
            for (int start = 0 ; start &amp;lt; n ; start++) {
                int end = start + size;
                if (end &amp;gt;= n) continue;
                int cnt = n- (end-start);
                dp[start][end] = Math.max(dp[start+1][end] + cnt * arr[start], dp[start][end-1] + cnt * arr[end]);
            }
        }

        System.out.println(dp[0][n-1]);
    }

}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/16</guid>
      <comments>https://worldi.tistory.com/16#entry16comment</comments>
      <pubDate>Sun, 24 Dec 2023 17:52:32 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2151 거울설치 풀이</title>
      <link>https://worldi.tistory.com/15</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS&lt;/p&gt;
&lt;h1&gt;풀이 과정&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;조합 개수로 반복 DFS를 돌리면서 해볼까 생각했는데, 이는 N &amp;le; 10 정도 일때만 통과한다. 지금은 1&amp;le; N &amp;le; 50 이므로 시간 초과 또는 메모리 초과가 난다.&lt;/li&gt;
&lt;li&gt;다른 풀이 방법을 생각했어야 했는데, 한쪽 문을 start 지점으로 정하고, 다른 문을 만날때까지 BFS를 진행한다. PriorityQueue는 거울의 최소 개수로 정렬되게 한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;! 를 만나면, 방향을 바꿀 수 있다. 즉 90도 방향을 바꾸고 PriorityQueue에다가 넣어준다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;*&amp;rsquo; 를 만나면 벽이므로 이동할 수 없다.&lt;/li&gt;
&lt;li&gt;그 외 다른 것들은 방향을 직진해서 갈 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 코드에 옮겼고, 다음과 같이 같다.&lt;/p&gt;
&lt;h1&gt;트러블 슈팅&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 코드를 다음과 같이 짰었다. 이 조건을 통해 90도 체크가 되는 지 확인했었다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  if ((i + poll[2]) % 2 == 0) {
			continue;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잘못된 풀이&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  private static int BFS(int n, int startX, int startY, int endX, int endY) {
        boolean[][][] visit = new boolean[n][n][4];
        PriorityQueue&amp;lt;int[]&amp;gt; q = new PriorityQueue&amp;lt;&amp;gt;((a, b) -&amp;gt; (a[3] - b[3]));

        for (int i = 0; i &amp;lt; 4; i++) {
            q.add(new int[]{startX, startY, i, 0});
            visit[startX][startY][i] = true;
        }

        while (!q.isEmpty()) {
            int[] poll = q.poll();

            if (poll[0] == endX &amp;amp;&amp;amp; poll[1] == endY) {
                return poll[3];
            }

            int nextX = poll[0] + dx[poll[2]];
            int nextY = poll[1] + dy[poll[2]];
            if (nextX &amp;lt; 0 || nextX &amp;gt;= map.length || nextY &amp;lt; 0 || nextY &amp;gt;= map.length) {
                continue;
            }

            if (map[nextX][nextY] == '*') {
                continue;
            }

            if (visit[nextX][nextY][poll[2]]) {
                continue;
            }

            visit[nextX][nextY][poll[2]] = true;
            q.add(new int[]{nextX, nextY, poll[2], poll[3]});

            if (map[nextX][nextY] == '!') {
                for (int i = 0; i &amp;lt; 4; i++) {	
                    if ((i + poll[2]) % 2 == 0) {
                        continue;
                    }
                    if (visit[nextX][nextY][i]) {
                        continue;
                    }
                    visit[nextX][nextY][i] = true;
                    q.add(new int[]{nextX, nextY, i, poll[3] + 1});
                }
            }

        }
        return Integer.MAX_VALUE;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 한 82 퍼센트 쯤 틀렸습니다가 떴다.&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;if (map[nextX][nextY] == '!') {
         for (int i = 0; i &amp;lt; 4; i++) {
                    if (i % 2 == 0) {
                        continue;
                    }
                    int dir = (poll[2] + i) % 4;

                    if (visit[nextX][nextY][dir]) {
                        continue;
                    }
                    visit[nextX][nextY][dir] = true;
                    q.add(new int[]{nextX, nextY, dir, poll[3] + 1});
                }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도사님이 보시자마자, 맞는 코드도 방향을 반대로 돌리면 틀렸습니다가 뜬다고 알려주시고, 방향을 체크하는 부분에서 문제가 있음을 알려주셨다. 따라서 방문체크를 boolean이 아닌, int 로 하여 더 작을 때 갱신하게끔 바꾸니 맞는 코드가 되었다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;private static int BFS(int n, int startX, int startY, int endX, int endY) {
        boolean[][][] visit = new boolean[n][n][4];
        PriorityQueue&amp;lt;int[]&amp;gt; q = new PriorityQueue&amp;lt;&amp;gt;((a, b) -&amp;gt; (a[3] - b[3]));

        for (int i = 0; i &amp;lt; 4; i++) {
            q.add(new int[]{startX, startY, i, 0});
            visit[startX][startY][i] = true;
        }

        while (!q.isEmpty()) {
            int[] poll = q.poll();

            if (poll[0] == endX &amp;amp;&amp;amp; poll[1] == endY) {
                return poll[3];
            }

            int nextX = poll[0] + dx[poll[2]];
            int nextY = poll[1] + dy[poll[2]];
            if (nextX &amp;lt; 0 || nextX &amp;gt;= map.length || nextY &amp;lt; 0 || nextY &amp;gt;= map.length) {
                continue;
            }

            if (map[nextX][nextY] == '*') {
                continue;
            }

            if (visit[nextX][nextY][poll[2]]) {
                continue;
            }
            visit[nextX][nextY][poll[2]] = true;
            q.add(new int[]{nextX, nextY, poll[2], poll[3]});

            if (map[nextX][nextY] == '!') {
                for (int i = 0; i &amp;lt; 4; i++) {
                    if (i % 2 == 0) {
                        continue;
                    }
                    int dir = (poll[2] + i) % 4;

                    if (visit[nextX][nextY][dir]) {
                        continue;
                    }
                    visit[nextX][nextY][dir] = true;
                    q.add(new int[]{nextX, nextY, dir, poll[3] + 1});
                }
            }

        }
        return Integer.MAX_VALUE;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 다음과 같이, !를 만날 때, 꺾는 방향을 PQ를 넣고 방문 처리를 해버리면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1775&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KgoQ7/btsCyowA8IS/ABbOJKWedoNnabyfkP2nVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KgoQ7/btsCyowA8IS/ABbOJKWedoNnabyfkP2nVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KgoQ7/btsCyowA8IS/ABbOJKWedoNnabyfkP2nVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKgoQ7%2FbtsCyowA8IS%2FABbOJKWedoNnabyfkP2nVK%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;2000&quot; height=&quot;1775&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1775&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;2000&quot; data-origin-height=&quot;1471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLMRUt/btsCCzXVyGX/BXY0OYgC12oRdELUir2xU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLMRUt/btsCCzXVyGX/BXY0OYgC12oRdELUir2xU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLMRUt/btsCCzXVyGX/BXY0OYgC12oRdELUir2xU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLMRUt%2FbtsCCzXVyGX%2FBXY0OYgC12oRdELUir2xU0%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;2000&quot; height=&quot;1471&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같은 코드를 위해 !를 만날때는 따로 방문처리를 하지 않고, pq에다가 넣어주는 방식으로 바꾸었다.&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;2000&quot; data-origin-height=&quot;2742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buyOrO/btsCzTvOrdq/KORlW7NOkYYruidEk53Mok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buyOrO/btsCzTvOrdq/KORlW7NOkYYruidEk53Mok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buyOrO/btsCzTvOrdq/KORlW7NOkYYruidEk53Mok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuyOrO%2FbtsCzTvOrdq%2FKORlW7NOkYYruidEk53Mok%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;2000&quot; height=&quot;2742&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;if (map[nextX][nextY] == '!') {
         for (int i = 0; i &amp;lt; 4; i++) {
                    if (i== poll[2]) continue;
                    if ((i + poll[2]) % 2 == 0) {
                        continue;
                    }
                    if (visit[nextX][nextY][i]) {
                        continue;
                    }
                    q.add(new int[]{nextX, nextY, i, poll[3] + 1});
                }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;느낀 점&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BFS 시, 억까를 당하면, &lt;b&gt;방문체크&lt;/b&gt;를 잘 하고 있는 지 확인하자! &lt;b&gt;무조건 방문하고 체크하는게 맞는게 아니다&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;(결론은 우리반 알고도사 곽민규 짱..)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/15</guid>
      <comments>https://worldi.tistory.com/15#entry15comment</comments>
      <pubDate>Sat, 23 Dec 2023 22:00:10 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2255 합분해 풀이</title>
      <link>https://worldi.tistory.com/14</link>
      <description>&lt;h1&gt;알고리즘&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이나믹 프로그래밍&lt;/p&gt;
&lt;h1&gt;풀이 방법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[i][j] = 현재 합은 i이고, k개의 수가 남았을 때 경우의 수.&lt;/li&gt;
&lt;li&gt;ex) dp[20][2]일때, dp[20][2] = dp[20][1] + dp[19][1] + dp[18][1] + &amp;hellip; 로 표현할 수 있다.&lt;/li&gt;
&lt;li&gt;해당 식을 일반화 한다면, dp[i][j] = dp[i-j][k-1] ( 0&amp;le; j &amp;le; i ) 이다.&lt;/li&gt;
&lt;li&gt;dp의 base 식은 dp[0][0] = 1이다. 0을 만들기 위해서 남은 수가 없을 때 경우의 수는 1가지이기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;코드&lt;/h1&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class P2225 {

    public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static StringTokenizer st = null;
    public static int n, k;
    /*
        * 0부터 N까지의 정수 K개를 더해서 그 합이 N이 되는 경우의 수를 구하는 프로그램을 작성하시오.
        * 덧셈의 순서가 바뀐 경우는 다른 경우로 센다(1+2와 2+1은 서로 다른 경우). 또한 한 개의 수를 여러 번 쓸 수도 있다.
        입력
        첫째 줄에 두 정수 N(1 &amp;le; N &amp;le; 200), K(1 &amp;le; K &amp;le; 200)가 주어진다.

        출력
        첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.
    * */
    public static int REMAINDER = 1_000_000_000;
    public static int[][]dp;
    public static void main(String[] args) throws IOException {
        st = new StringTokenizer(br.readLine());
       
        n = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        dp = new int[n+1][k+1];

        dp[0][0] = 1;

        for (int i = 0 ; i&amp;lt;= n ; i++) {
            for (int j = 1 ; j&amp;lt;=k ; j++) {
                dp[i][j] = 0;
                for (int t = 0 ;t &amp;lt;=i ; t++) {
                    dp[i][j] += dp[i-t][j-1];
                    dp[i][j] %= REMAINDER;
                }
            }
        }
        System.out.println(dp[n][k]%REMAINDER);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 관련 일지/알고리즘</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/14</guid>
      <comments>https://worldi.tistory.com/14#entry14comment</comments>
      <pubDate>Fri, 22 Dec 2023 17:02:05 +0900</pubDate>
    </item>
    <item>
      <title>[대만4박5일] 2일차 예스폭진지 투어</title>
      <link>https://worldi.tistory.com/11</link>
      <description>&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;날씨 상황보고 그바로 전날 결정할라고 했는데,&amp;nbsp; 정말 선택안하면 못갈거같아서 2일전에 예약을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴때 가끔 p의 단점이 보인다. 하지만 포기 못한다. ㅎㅎ 얼렁뚱땅 어떻게든 잘 굴러가는 내인생&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 이거때문에 7시 반에 일어났다. 재빨리 화장을 하고, 곱창국수와 흑당 버블티를 아침으로 조졌다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uv17S/btsB1xgeJdu/4Ggs1qWgzVBPvzOvbmNEVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uv17S/btsB1xgeJdu/4Ggs1qWgzVBPvzOvbmNEVK/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-24 001jpeg.jpeg&quot; style=&quot;width: 45.9644%; margin-right: 10px;&quot; data-widthpercent=&quot;47.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uv17S/btsB1xgeJdu/4Ggs1qWgzVBPvzOvbmNEVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuv17S%2FbtsB1xgeJdu%2F4Ggs1qWgzVBPvzOvbmNEVK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kf9bj/btsB0fmsvox/AKayxCUTp5ulzT1YWbJSV0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kf9bj/btsB0fmsvox/AKayxCUTp5ulzT1YWbJSV0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-25 002jpeg.jpeg&quot; data-widthpercent=&quot;26.47&quot; style=&quot;width: 25.855%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kf9bj/btsB0fmsvox/AKayxCUTp5ulzT1YWbJSV0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKf9bj%2FbtsB0fmsvox%2FAKayxCUTp5ulzT1YWbJSV0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ufgj9/btsB3c3iVzd/Mrz5gAhHyWQzk6YZ8msjD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ufgj9/btsB3c3iVzd/Mrz5gAhHyWQzk6YZ8msjD1/img.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.855%;&quot; data-widthpercent=&quot;26.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ufgj9/btsB3c3iVzd/Mrz5gAhHyWQzk6YZ8msjD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUfgj9%2FbtsB3c3iVzd%2FMrz5gAhHyWQzk6YZ8msjD1%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;눈 부은 채로 곱창 국수를 먹었다. 흑당 버블티는 존맛탱이다.&amp;nbsp;&lt;/figcaption&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnB3eq/btsBYaMJ2By/7uFLppQSK7rhGm0KQu10n1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnB3eq/btsBYaMJ2By/7uFLppQSK7rhGm0KQu10n1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-25 003jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnB3eq/btsBYaMJ2By/7uFLppQSK7rhGm0KQu10n1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnB3eq%2FbtsBYaMJ2By%2F7uFLppQSK7rhGm0KQu10n1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beq5wp/btsB2Q7covB/MMZKtZkV6bK1UhdYkSvJB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beq5wp/btsB2Q7covB/MMZKtZkV6bK1UhdYkSvJB1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-27 005jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beq5wp/btsB2Q7covB/MMZKtZkV6bK1UhdYkSvJB1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbeq5wp%2FbtsB2Q7covB%2FMMZKtZkV6bK1UhdYkSvJB1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGgu9k/btsB3vPgc3p/gNUc3SHMpPC3deSHPiNbK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGgu9k/btsB3vPgc3p/gNUc3SHMpPC3deSHPiNbK1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-28 006jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGgu9k/btsB3vPgc3p/gNUc3SHMpPC3deSHPiNbK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGgu9k%2FbtsB3vPgc3p%2FgNUc3SHMpPC3deSHPiNbK1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCkM6w/btsBZHXKsNq/dbYe9RzuJy1mGmrsRzb9kk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCkM6w/btsBZHXKsNq/dbYe9RzuJy1mGmrsRzb9kk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-28 007jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCkM6w/btsBZHXKsNq/dbYe9RzuJy1mGmrsRzb9kk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCkM6w%2FbtsBZHXKsNq%2FdbYe9RzuJy1mGmrsRzb9kk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;날씨가 개 미친듯이 더웠다 갑자기 30도 까지 올라갔는데 한겨울 옷을 입고갔으니 계속 땀났다..&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예류지질공원에 도착했다. 좋기는 한데 개 더웠다. 진짜. 난 겨울옷 입고가서 더 그랬다. 여왕벌 바위가 있는데 거기 앞에서 찍을라면 줄 겁나 서야 한다. 나는 진작에 포기했다. 망고 스무디 맛있다. 걷는 길에 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QlBAm/btsB2ScR4mi/Kvqb4Mb2bbmxptNsyjZugK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QlBAm/btsB2ScR4mi/Kvqb4Mb2bbmxptNsyjZugK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-29 009jpeg.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QlBAm/btsB2ScR4mi/Kvqb4Mb2bbmxptNsyjZugK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQlBAm%2FbtsB2ScR4mi%2FKvqb4Mb2bbmxptNsyjZugK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s1E75/btsBVAyoOPx/chqTgRlualFFTOPSQrTBT1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s1E75/btsBVAyoOPx/chqTgRlualFFTOPSQrTBT1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-30 010jpeg.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s1E75/btsBVAyoOPx/chqTgRlualFFTOPSQrTBT1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs1E75%2FbtsBVAyoOPx%2FchqTgRlualFFTOPSQrTBT1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTQjV4/btsB1FyvgNt/cnFTqATGwyWLVskF5D7rt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTQjV4/btsB1FyvgNt/cnFTqATGwyWLVskF5D7rt1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-31 011jpeg.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTQjV4/btsB1FyvgNt/cnFTqATGwyWLVskF5D7rt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTQjV4%2FbtsB1FyvgNt%2FcnFTqATGwyWLVskF5D7rt1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;닭다리 볶음밥인가 저게 진짜 ㄹㅇ 개 존맛탱 입에서 사르르 녹는다. 중간 사진은 풍등 글씨가 너무 웃겨서 찍었다.ㅋㅋㅋㅋㅋㅋ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFXMTq/btsB1ucJH1u/JMKujWYBKwx2blmz9dwTN0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFXMTq/btsB1ucJH1u/JMKujWYBKwx2blmz9dwTN0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-32 014jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFXMTq/btsB1ucJH1u/JMKujWYBKwx2blmz9dwTN0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFXMTq%2FbtsB1ucJH1u%2FJMKujWYBKwx2blmz9dwTN0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4BOxz/btsB18mJjKR/Lbxk14CO1POp27m37sgz1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4BOxz/btsB18mJjKR/Lbxk14CO1POp27m37sgz1k/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-31 013jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4BOxz/btsB18mJjKR/Lbxk14CO1POp27m37sgz1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4BOxz%2FbtsB18mJjKR%2FLbxk14CO1POp27m37sgz1k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;더워서 또 스무디를 사먹었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 그 다음엔 스펀을 갔다. 저 풍등이 머라고 사람이 저리 많은지 이해가 안간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대만에 볼거리가 정말 없구나를 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWfRBJ/btsBXPWtDN3/QavaYfQ6J2jbB8ZJGrkFS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWfRBJ/btsBXPWtDN3/QavaYfQ6J2jbB8ZJGrkFS1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-33 017jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWfRBJ/btsBXPWtDN3/QavaYfQ6J2jbB8ZJGrkFS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWfRBJ%2FbtsBXPWtDN3%2FQavaYfQ6J2jbB8ZJGrkFS1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xOaR8/btsBY82dtmi/feyoM5F6fOeJm1p2fKQLC1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xOaR8/btsBY82dtmi/feyoM5F6fOeJm1p2fKQLC1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-31 012jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xOaR8/btsBY82dtmi/feyoM5F6fOeJm1p2fKQLC1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxOaR8%2FbtsBY82dtmi%2FfeyoM5F6fOeJm1p2fKQLC1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;스펀 폭포 앞에서.. 엄마가 인생샷을 찍어줬다 감사합니다.&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-33 016jpeg.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3xomJ/btsBZIbf13H/yLNLQZ70KY6PpHb9s6T1F0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3xomJ/btsBZIbf13H/yLNLQZ70KY6PpHb9s6T1F0/img.jpg&quot; data-alt=&quot;생마늘하고 소세지를 같이 먹으면 레알 맛있다... 60원이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3xomJ/btsBZIbf13H/yLNLQZ70KY6PpHb9s6T1F0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3xomJ%2FbtsBZIbf13H%2FyLNLQZ70KY6PpHb9s6T1F0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;435&quot; height=&quot;580&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-33 016jpeg.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생마늘하고 소세지를 같이 먹으면 레알 맛있다... 60원이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 스펀 폭포이다. 전망대가 1,2,3이 있느넫, 시간상 나는 1만 갔다. 폭포가 거기서 거기라고 생각한다... 미국 폭포 멋있는 거 보다가 이거 보니 시시하긴 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QWy1i/btsBXOb6Hn0/NTSrgad4uNx5C5bOtcbVGK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QWy1i/btsBXOb6Hn0/NTSrgad4uNx5C5bOtcbVGK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-34 018jpeg.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QWy1i/btsBXOb6Hn0/NTSrgad4uNx5C5bOtcbVGK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQWy1i%2FbtsBXOb6Hn0%2FNTSrgad4uNx5C5bOtcbVGK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQWrjF/btsBVCXieWn/OumvTLknarijWJEe8mA5Qk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQWrjF/btsBVCXieWn/OumvTLknarijWJEe8mA5Qk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-35 019jpeg.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQWrjF/btsBVCXieWn/OumvTLknarijWJEe8mA5Qk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQWrjF%2FbtsBVCXieWn%2FOumvTLknarijWJEe8mA5Qk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lx9ul/btsBY64pokv/8gXvRKpTb7BYZjVhr3Jkwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lx9ul/btsBY64pokv/8gXvRKpTb7BYZjVhr3Jkwk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-35 020jpeg.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lx9ul/btsBY64pokv/8gXvRKpTb7BYZjVhr3Jkwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLx9ul%2FbtsBY64pokv%2F8gXvRKpTb7BYZjVhr3Jkwk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;지우펀&lt;/figcaption&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;다시 가라고 해도 안갈거같다. 아니면 9시넘으면 사람 쫙빠진다는데, 숙박하고 사람 없을때 보는거도 나쁘지 않을듯..&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;&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3Uj3H/btsB1GRy4iv/heeATyCLdHLyMCHsgqYZ80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3Uj3H/btsB1GRy4iv/heeATyCLdHLyMCHsgqYZ80/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-36 021jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3Uj3H/btsB1GRy4iv/heeATyCLdHLyMCHsgqYZ80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3Uj3H%2FbtsB1GRy4iv%2FheeATyCLdHLyMCHsgqYZ80%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FZaYz/btsB3bpONY9/jrAs2Gpl1jZbXmqoyXeZA0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FZaYz/btsB3bpONY9/jrAs2Gpl1jZbXmqoyXeZA0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-23-09-37 022jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FZaYz/btsB3bpONY9/jrAs2Gpl1jZbXmqoyXeZA0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFZaYz%2FbtsB3bpONY9%2FjrAs2Gpl1jZbXmqoyXeZA0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;개꿀맛이에요 맛있어요 짱잉에요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>여행/국외</category>
      <category>대만자유여행</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/11</guid>
      <comments>https://worldi.tistory.com/11#entry11comment</comments>
      <pubDate>Thu, 14 Dec 2023 23:14:32 +0900</pubDate>
    </item>
    <item>
      <title>[대만4박5일] 1일차 타오위안 공항 - 메인역 - 단수이</title>
      <link>https://worldi.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;엄마랑 두번째로 이번년도 해외여행을 갔다왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대만으로 여행지를 잡은 이유는 1. 너무 추워서 2. 청주공항에 가는 곳이 한정적인 이유였다.&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;사전에는 어느 때와 같이 공항, 숙박, 여행 보험, esim, 출입국 심사 정도만 준비하고 갔다.&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;549&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/42DbW/btsB1DUIFug/2KGCZZxtorFZ6rEyUbMobK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/42DbW/btsB1DUIFug/2KGCZZxtorFZ6rEyUbMobK/img.png&quot; data-alt=&quot;엄마 미안.. 내가 p라서...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/42DbW/btsB1DUIFug/2KGCZZxtorFZ6rEyUbMobK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F42DbW%2FbtsB1DUIFug%2F2KGCZZxtorFZ6rEyUbMobK%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;570&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엄마 미안.. 내가 p라서...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아침 8시 50분 비행기였는데, 안개가 너무 많이 껴있어서 출국할 수 있을 지 의문이었다. 역시나 지연되었고, 30분정도 후에 날씨가 개자 탑승할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmCYy8/btsB1FyjNOK/JHOFV8tlIQpNC0TjqJXEaK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmCYy8/btsB1FyjNOK/JHOFV8tlIQpNC0TjqJXEaK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-18-59-09 001jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmCYy8/btsB1FyjNOK/JHOFV8tlIQpNC0TjqJXEaK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmCYy8%2FbtsB1FyjNOK%2FJHOFV8tlIQpNC0TjqJXEaK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nojxd/btsB1K0m39A/WfXu8kL92JVkjblknzWQL1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nojxd/btsB1K0m39A/WfXu8kL92JVkjblknzWQL1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-18-59-10 002jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nojxd/btsB1K0m39A/WfXu8kL92JVkjblknzWQL1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNojxd%2FbtsB1K0m39A%2FWfXu8kL92JVkjblknzWQL1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대만에 내리니, 완전히 덥지도 습하지도 않은 쨍한 날씨가 펼쳐져서 행복했다. 한국의 겨울은 추위를 많이 타는 나에게 너무 지옥이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 대만 여행지원금을 뽑으러 갔다. 환전을 하기 전에, 얼마정도 예산을 지출할지 예상해보기 위함이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데, 엄마도 나도 똥손이기 때문에 역시나 되지 않았다. 내 기계는 심지어 눌리지 않음ㅋㅋ (좀 빡침)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러고서 엄마 트래블로그 카드로 환전했다. 엄마가 환전을 미국거로 하는 바람에 외않돼 억까를 당해서, 좀 시간 오래걸렸다....&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;대략 6000원(25만원정도)를 뽑고, 타이베이 메인역으로 향했다.&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-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-20 012jpeg.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOu8lJ/btsB2RER6vl/Ql3qWKSSXIcLACmtKhacI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOu8lJ/btsB2RER6vl/Ql3qWKSSXIcLACmtKhacI1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOu8lJ/btsB2RER6vl/Ql3qWKSSXIcLACmtKhacI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOu8lJ%2FbtsB2RER6vl%2FQl3qWKSSXIcLACmtKhacI1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-20 012jpeg.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;p data-ke-size=&quot;size16&quot;&gt;짐을 일단 보관소에 맡기고 단수이로 가기로 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5번출구 옆쪽에 메인역은 짐보관소가 있는데, 처음에 어떤 할배가 짐을 이제 놓을 공간이 없다고 더이상 못받는 다고 하셨다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 어떻게 계획을 바꿀지 당황스러운 기색이 역력하던 찰나에, 할배가 다시 해줬다.(?) 어떤 대만분이 영어를 잘하셔서 소통역할을 대신 해주었다. 대만 분들이 착하시다는데 정말 맞는거 같다는 생각을 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;돈은 엄마와 내 캐리어 합쳐서 80원이 들었다. 그런데 인터넷에서는 8시까지로 영업시간을 봤는데 7시까지랬다. (이게 추후 화근이 될 줄 몰랐다...)&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CNiRy/btsBXJWpglk/nDZukkmuroDRptIYiGdJIK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CNiRy/btsBXJWpglk/nDZukkmuroDRptIYiGdJIK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-20 013jpeg.jpeg&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CNiRy/btsBXJWpglk/nDZukkmuroDRptIYiGdJIK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCNiRy%2FbtsBXJWpglk%2FnDZukkmuroDRptIYiGdJIK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brSPzh/btsB3es88Fl/PNkVTTe1yVNVfYM4KxKhK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brSPzh/btsB3es88Fl/PNkVTTe1yVNVfYM4KxKhK1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-19 011jpeg.jpeg&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brSPzh/btsB3es88Fl/PNkVTTe1yVNVfYM4KxKhK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrSPzh%2FbtsB3es88Fl%2FPNkVTTe1yVNVfYM4KxKhK1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;흠...개맛없.. 그떄당시에는 배고파서 맛있게 먹긴함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 끼니를 떼우기 위해서, 만두집을 찾아봤는데 메인역&amp;nbsp; 만두 (이름은 기억은 안나지만) 체인점을 들리는 길에 가려고 했다. 그런데 그때가 한 2-3시였는데 줄을 기다릴 자신이 없어 그냥 지나가다가 어떤 로컬집을 갔다. 만두 한 30원하는데 지금 보니 드럽게 맛이 없었다.. ㅋㅋ&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;죽도 처음엔 맛있었는데 나중에는 비렸다. 어쨌든 다먹었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVODMq/btsB0dWlZUo/FwCCsT6w4FGPD8zasnXYR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVODMq/btsB0dWlZUo/FwCCsT6w4FGPD8zasnXYR0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-19 009jpeg.jpeg&quot; data-widthpercent=&quot;33.33&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVODMq/btsB0dWlZUo/FwCCsT6w4FGPD8zasnXYR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVODMq%2FbtsB0dWlZUo%2FFwCCsT6w4FGPD8zasnXYR0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pGk1C/btsB2QsqHFK/V9ckvHs9ta1D2RgflOHj2k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pGk1C/btsB2QsqHFK/V9ckvHs9ta1D2RgflOHj2k/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-19 008jpeg.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pGk1C/btsB2QsqHFK/V9ckvHs9ta1D2RgflOHj2k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpGk1C%2FbtsB2QsqHFK%2FV9ckvHs9ta1D2RgflOHj2k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buwNUL/btsBXHYBRuZ/W8mVC0vmWPSVE6dKax92R0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buwNUL/btsBXHYBRuZ/W8mVC0vmWPSVE6dKax92R0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-19 010jpeg.jpeg&quot; data-widthpercent=&quot;33.34&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buwNUL/btsBXHYBRuZ/W8mVC0vmWPSVE6dKax92R0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuwNUL%2FbtsBXHYBRuZ%2FW8mVC0vmWPSVE6dKax92R0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러고 단수이로 향했다. 지하철로 한시간 정도 걸렸던 것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단수이는 노을 맛집으로 유명한 곳이다. 그리고 내가 좋아했던 '말할 수 없는 비밀' 의 촬영지로도 유명한 곳이다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단수이 터미널에 도착해서 26번 버스를 타서 홍마오청으로 향했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;홍마오청이 아마(?) 말할 수 없는 비밀의 촬영지일 것이다. 너무 더워서 반팔로 갈아입었는데, 엄마랑 예전에 샀던 꼼데티를 같이 입었다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;크기가 생각보다 작아서 대충 훑어보고 소백궁도 훑어 보고, 시간이 4시 반정도가 되어서 노을을 보러갔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buv5Zl/btsB1uqakHB/h9yvZTbWdkXK5gIy1VPmz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buv5Zl/btsB1uqakHB/h9yvZTbWdkXK5gIy1VPmz1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-18 006jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buv5Zl/btsB1uqakHB/h9yvZTbWdkXK5gIy1VPmz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbuv5Zl%2FbtsB1uqakHB%2Fh9yvZTbWdkXK5gIy1VPmz1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLoug5/btsB2P1lB6H/7WQZl4S5lSPBdqBAoJqK3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLoug5/btsB2P1lB6H/7WQZl4S5lSPBdqBAoJqK3K/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-18 005jpeg.jpeg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLoug5/btsB2P1lB6H/7WQZl4S5lSPBdqBAoJqK3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLoug5%2FbtsB2P1lB6H%2F7WQZl4S5lSPBdqBAoJqK3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMltnm/btsB2axUMrv/a70QGvKmAtUrFvyiC8EbP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMltnm/btsB2axUMrv/a70QGvKmAtUrFvyiC8EbP1/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-17 004jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMltnm/btsB2axUMrv/a70QGvKmAtUrFvyiC8EbP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMltnm%2FbtsB2axUMrv%2Fa70QGvKmAtUrFvyiC8EbP1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDVgqi/btsBYaZ6T1K/1D1q95u5fVRmOYGqOWzTO0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDVgqi/btsBYaZ6T1K/1D1q95u5fVRmOYGqOWzTO0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-18 007jpeg.jpeg&quot; style=&quot;width: 49.4186%; margin-top: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDVgqi/btsBYaZ6T1K/1D1q95u5fVRmOYGqOWzTO0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDVgqi%2FbtsBYaZ6T1K%2F1D1q95u5fVRmOYGqOWzTO0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일몰 시점이라 노을이 이쁘게 졌다. 너무 이뻤는데 흐려서 조금 아쉬웠다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러고서 밀크티를 마시러, 바다가 보이는 카페를 들렸다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대만에서 먹어본 첫번째 밀크티는 한국에서 먹는 밀크티 보다 맛이없었다...너무 많은 기대를 한것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사진을 열심히 찍고, 약속 시간인 7시를 맞추기 위해 5시 15분쯤 버스를 타서 돌아오는 길이었는데 생각보다 차가 너무 많이 막혔다..&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원래는 단수이 역까지 6시20분에는 도착해야하는데 도착하지 않았고, 7시까지 짐을 빼기 위해 버스를 도중에 내려서 1.2km를 졸라 뛰어갔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아 진짜 역대급으로 힘들었다..&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;엄마는 또 긍정적으로 지나가는 길에 이쁜걸 봐서 좋다라고 말씀했지만 엄마를 너무 고생시킨것 같아 마음이 그렇게 좋지 않았다..&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대략 20000만보를 걸었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7시에 이제 짐을 찾고, 에어비앤비 숙소인 시먼역을 갔다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아니 이놈의 에어비앤비는 뭔 집 키하고, 실제 숙소를 다른 곳에다 놓냐 개빡쳤다.. 짐들고 가는데 헛걸음질해서&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;근데 이것도 내가 결국엔 잘 안본거라서 엄마한테 좀 미안함..&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어쨌든 1일차 치고 무탈하게 집을 가서 너무 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lxjCZ/btsBXL7krkN/46dQbUKyNZtFlAc6AQyUTk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lxjCZ/btsBXL7krkN/46dQbUKyNZtFlAc6AQyUTk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-17 003jpeg.jpeg&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lxjCZ/btsBXL7krkN/46dQbUKyNZtFlAc6AQyUTk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlxjCZ%2FbtsBXL7krkN%2F46dQbUKyNZtFlAc6AQyUTk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qFjnU/btsBY5jVQuW/E58im9qpDkutIZGCyDDiq0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qFjnU/btsBY5jVQuW/E58im9qpDkutIZGCyDDiq0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_Photo_2023-12-14-19-03-16 002jpeg.jpeg&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qFjnU/btsBY5jVQuW/E58im9qpDkutIZGCyDDiq0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqFjnU%2FbtsBY5jVQuW%2FE58im9qpDkutIZGCyDDiq0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숙소에 짐을 풀고, 집 근처 식당을 찾는데 진천미라는 식당이 별점이 좋길래 가서 밥을 먹었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;알고보니 그 다음날 투어에서 가이드님이 진천미 식당을 추천했다. ㅋㅋ 어쩐지 맛있더라&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시먼역 근처 구경하다가 인생네컷이 있길래 엄마랑 인생네컷도 찍었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그전날에 잠을 3시간 자고 일찍일어난 터라 거의 기절하듯이 잠을 잤다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 다음날에는 무려 예스폭진지 투어가 있는 날이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>여행/국외</category>
      <category>대만자유여행</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/10</guid>
      <comments>https://worldi.tistory.com/10#entry10comment</comments>
      <pubDate>Thu, 14 Dec 2023 18:58:01 +0900</pubDate>
    </item>
    <item>
      <title>[TrobleShooting] JWT signature does not match locally computed signature.</title>
      <link>https://worldi.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;msa를 구축하다가, token 유효 검증 부분에서 signature가 맞지 않는 다는 에러가 떴다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;[apigateway-service] [ctor-http-nio-2] c.e.a.config.Authorization Header Filter :
 JWT signature does not match locally computed signature. 
JWT validity cannot be asserted and should not be trusted.
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;해결법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 secretKey 를 secretKey.getBytes()로 바꾸어주니 정상 작동하였다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public String generateToken(final String userId) {
        return Jwts.builder()
            .setExpiration(new Date(System.currentTimeMillis() + expireTime))
            .signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
            .setSubject(userId)
            .compact();
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;final Claims body = Jwts.parser()
                .setSigningKey(secretKey.getBytes())
                .parseClaimsJws(token)
                .getBody();
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WkMDJ/btsBHRyl1rh/tQ9kXPED3KEIXT1Tgf3831/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WkMDJ/btsBHRyl1rh/tQ9kXPED3KEIXT1Tgf3831/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WkMDJ/btsBHRyl1rh/tQ9kXPED3KEIXT1Tgf3831/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWkMDJ%2FbtsBHRyl1rh%2FtQ9kXPED3KEIXT1Tgf3831%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;638&quot; height=&quot;412&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;참고 자료&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/56639392/jwt-signature-does-not-match-locally-computed-signature-jwt-validity-cannot-be&quot;&gt;https://stackoverflow.com/questions/56639392/jwt-signature-does-not-match-locally-computed-signature-jwt-validity-cannot-be&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발 관련 일지/Springs</category>
      <author>worldi</author>
      <guid isPermaLink="true">https://worldi.tistory.com/9</guid>
      <comments>https://worldi.tistory.com/9#entry9comment</comments>
      <pubDate>Fri, 8 Dec 2023 23:48:21 +0900</pubDate>
    </item>
  </channel>
</rss>